brintos

brintos / linux-shallow public Read only

0
0
Text · 26.9 KiB · 1fa79bb Raw
1035 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Control and Management Interface (SCMI) Powercap Protocol4 *5 * Copyright (C) 2022 ARM Ltd.6 */7 8#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt9 10#include <linux/bitfield.h>11#include <linux/io.h>12#include <linux/module.h>13#include <linux/scmi_protocol.h>14 15#include <trace/events/scmi.h>16 17#include "protocols.h"18#include "notify.h"19 20/* Updated only after ALL the mandatory features for that version are merged */21#define SCMI_PROTOCOL_SUPPORTED_VERSION		0x2000022 23enum scmi_powercap_protocol_cmd {24	POWERCAP_DOMAIN_ATTRIBUTES = 0x3,25	POWERCAP_CAP_GET = 0x4,26	POWERCAP_CAP_SET = 0x5,27	POWERCAP_PAI_GET = 0x6,28	POWERCAP_PAI_SET = 0x7,29	POWERCAP_DOMAIN_NAME_GET = 0x8,30	POWERCAP_MEASUREMENTS_GET = 0x9,31	POWERCAP_CAP_NOTIFY = 0xa,32	POWERCAP_MEASUREMENTS_NOTIFY = 0xb,33	POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,34};35 36enum {37	POWERCAP_FC_CAP,38	POWERCAP_FC_PAI,39	POWERCAP_FC_MAX,40};41 42struct scmi_msg_resp_powercap_domain_attributes {43	__le32 attributes;44#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x)		((x) & BIT(31))45#define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x)	((x) & BIT(30))46#define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x)		((x) & BIT(29))47#define SUPPORTS_EXTENDED_NAMES(x)			((x) & BIT(28))48#define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x)		((x) & BIT(27))49#define SUPPORTS_POWERCAP_MONITORING(x)			((x) & BIT(26))50#define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x)		((x) & BIT(25))51#define SUPPORTS_POWERCAP_FASTCHANNELS(x)		((x) & BIT(22))52#define POWERCAP_POWER_UNIT(x)				\53		(FIELD_GET(GENMASK(24, 23), (x)))54#define	SUPPORTS_POWER_UNITS_MW(x)			\55		(POWERCAP_POWER_UNIT(x) == 0x2)56#define	SUPPORTS_POWER_UNITS_UW(x)			\57		(POWERCAP_POWER_UNIT(x) == 0x1)58	u8 name[SCMI_SHORT_NAME_MAX_SIZE];59	__le32 min_pai;60	__le32 max_pai;61	__le32 pai_step;62	__le32 min_power_cap;63	__le32 max_power_cap;64	__le32 power_cap_step;65	__le32 sustainable_power;66	__le32 accuracy;67	__le32 parent_id;68};69 70struct scmi_msg_powercap_set_cap_or_pai {71	__le32 domain;72	__le32 flags;73#define CAP_SET_ASYNC		BIT(1)74#define CAP_SET_IGNORE_DRESP	BIT(0)75	__le32 value;76};77 78struct scmi_msg_resp_powercap_cap_set_complete {79	__le32 domain;80	__le32 power_cap;81};82 83struct scmi_msg_resp_powercap_meas_get {84	__le32 power;85	__le32 pai;86};87 88struct scmi_msg_powercap_notify_cap {89	__le32 domain;90	__le32 notify_enable;91};92 93struct scmi_msg_powercap_notify_thresh {94	__le32 domain;95	__le32 notify_enable;96	__le32 power_thresh_low;97	__le32 power_thresh_high;98};99 100struct scmi_powercap_cap_changed_notify_payld {101	__le32 agent_id;102	__le32 domain_id;103	__le32 power_cap;104	__le32 pai;105};106 107struct scmi_powercap_meas_changed_notify_payld {108	__le32 agent_id;109	__le32 domain_id;110	__le32 power;111};112 113struct scmi_powercap_state {114	bool enabled;115	u32 last_pcap;116	bool meas_notif_enabled;117	u64 thresholds;118#define THRESH_LOW(p, id)				\119	(lower_32_bits((p)->states[(id)].thresholds))120#define THRESH_HIGH(p, id)				\121	(upper_32_bits((p)->states[(id)].thresholds))122};123 124struct powercap_info {125	u32 version;126	int num_domains;127	bool notify_cap_cmd;128	bool notify_measurements_cmd;129	struct scmi_powercap_state *states;130	struct scmi_powercap_info *powercaps;131};132 133static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {134	POWERCAP_CAP_NOTIFY,135	POWERCAP_MEASUREMENTS_NOTIFY,136};137 138static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,139				u32 domain, int message_id, bool enable);140 141static int142scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,143			     struct powercap_info *pi)144{145	int ret;146	struct scmi_xfer *t;147 148	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,149				      sizeof(u32), &t);150	if (ret)151		return ret;152 153	ret = ph->xops->do_xfer(ph, t);154	if (!ret) {155		u32 attributes;156 157		attributes = get_unaligned_le32(t->rx.buf);158		pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);159	}160 161	ph->xops->xfer_put(ph, t);162 163	if (!ret) {164		if (!ph->hops->protocol_msg_check(ph,165						  POWERCAP_CAP_NOTIFY, NULL))166			pi->notify_cap_cmd = true;167 168		if (!ph->hops->protocol_msg_check(ph,169						  POWERCAP_MEASUREMENTS_NOTIFY,170						  NULL))171			pi->notify_measurements_cmd = true;172	}173 174	return ret;175}176 177static inline int178scmi_powercap_validate(unsigned int min_val, unsigned int max_val,179		       unsigned int step_val, bool configurable)180{181	if (!min_val || !max_val)182		return -EPROTO;183 184	if ((configurable && min_val == max_val) ||185	    (!configurable && min_val != max_val))186		return -EPROTO;187 188	if (min_val != max_val && !step_val)189		return -EPROTO;190 191	return 0;192}193 194static int195scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,196				    struct powercap_info *pinfo, u32 domain)197{198	int ret;199	u32 flags;200	struct scmi_xfer *t;201	struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;202	struct scmi_msg_resp_powercap_domain_attributes *resp;203 204	ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,205				      sizeof(domain), sizeof(*resp), &t);206	if (ret)207		return ret;208 209	put_unaligned_le32(domain, t->tx.buf);210	resp = t->rx.buf;211 212	ret = ph->xops->do_xfer(ph, t);213	if (!ret) {214		flags = le32_to_cpu(resp->attributes);215 216		dom_info->id = domain;217		if (pinfo->notify_cap_cmd)218			dom_info->notify_powercap_cap_change =219				SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);220		if (pinfo->notify_measurements_cmd)221			dom_info->notify_powercap_measurement_change =222				SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);223		dom_info->async_powercap_cap_set =224			SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);225		dom_info->powercap_cap_config =226			SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);227		dom_info->powercap_monitoring =228			SUPPORTS_POWERCAP_MONITORING(flags);229		dom_info->powercap_pai_config =230			SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);231		dom_info->powercap_scale_mw =232			SUPPORTS_POWER_UNITS_MW(flags);233		dom_info->powercap_scale_uw =234			SUPPORTS_POWER_UNITS_UW(flags);235		dom_info->fastchannels =236			SUPPORTS_POWERCAP_FASTCHANNELS(flags);237 238		strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);239 240		dom_info->min_pai = le32_to_cpu(resp->min_pai);241		dom_info->max_pai = le32_to_cpu(resp->max_pai);242		dom_info->pai_step = le32_to_cpu(resp->pai_step);243		ret = scmi_powercap_validate(dom_info->min_pai,244					     dom_info->max_pai,245					     dom_info->pai_step,246					     dom_info->powercap_pai_config);247		if (ret) {248			dev_err(ph->dev,249				"Platform reported inconsistent PAI config for domain %d - %s\n",250				dom_info->id, dom_info->name);251			goto clean;252		}253 254		dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);255		dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);256		dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);257		ret = scmi_powercap_validate(dom_info->min_power_cap,258					     dom_info->max_power_cap,259					     dom_info->power_cap_step,260					     dom_info->powercap_cap_config);261		if (ret) {262			dev_err(ph->dev,263				"Platform reported inconsistent CAP config for domain %d - %s\n",264				dom_info->id, dom_info->name);265			goto clean;266		}267 268		dom_info->sustainable_power =269			le32_to_cpu(resp->sustainable_power);270		dom_info->accuracy = le32_to_cpu(resp->accuracy);271 272		dom_info->parent_id = le32_to_cpu(resp->parent_id);273		if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&274		    (dom_info->parent_id >= pinfo->num_domains ||275		     dom_info->parent_id == dom_info->id)) {276			dev_err(ph->dev,277				"Platform reported inconsistent parent ID for domain %d - %s\n",278				dom_info->id, dom_info->name);279			ret = -ENODEV;280		}281	}282 283clean:284	ph->xops->xfer_put(ph, t);285 286	/*287	 * If supported overwrite short name with the extended one;288	 * on error just carry on and use already provided short name.289	 */290	if (!ret && SUPPORTS_EXTENDED_NAMES(flags))291		ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,292					    domain, NULL, dom_info->name,293					    SCMI_MAX_STR_SIZE);294 295	return ret;296}297 298static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)299{300	struct powercap_info *pi = ph->get_priv(ph);301 302	return pi->num_domains;303}304 305static const struct scmi_powercap_info *306scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)307{308	struct powercap_info *pi = ph->get_priv(ph);309 310	if (domain_id >= pi->num_domains)311		return NULL;312 313	return pi->powercaps + domain_id;314}315 316static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,317				      u32 domain_id, u32 *power_cap)318{319	int ret;320	struct scmi_xfer *t;321 322	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),323				      sizeof(u32), &t);324	if (ret)325		return ret;326 327	put_unaligned_le32(domain_id, t->tx.buf);328	ret = ph->xops->do_xfer(ph, t);329	if (!ret)330		*power_cap = get_unaligned_le32(t->rx.buf);331 332	ph->xops->xfer_put(ph, t);333 334	return ret;335}336 337static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,338				   const struct scmi_powercap_info *dom,339				   u32 *power_cap)340{341	if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {342		*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);343		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,344				   dom->id, *power_cap, 0);345		return 0;346	}347 348	return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);349}350 351static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,352				 u32 domain_id, u32 *power_cap)353{354	const struct scmi_powercap_info *dom;355 356	if (!power_cap)357		return -EINVAL;358 359	dom = scmi_powercap_dom_info_get(ph, domain_id);360	if (!dom)361		return -EINVAL;362 363	return __scmi_powercap_cap_get(ph, dom, power_cap);364}365 366static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,367				      const struct scmi_powercap_info *pc,368				      u32 power_cap, bool ignore_dresp)369{370	int ret;371	struct scmi_xfer *t;372	struct scmi_msg_powercap_set_cap_or_pai *msg;373 374	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,375				      sizeof(*msg), 0, &t);376	if (ret)377		return ret;378 379	msg = t->tx.buf;380	msg->domain = cpu_to_le32(pc->id);381	msg->flags =382		cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, pc->async_powercap_cap_set) |383			    FIELD_PREP(CAP_SET_IGNORE_DRESP, ignore_dresp));384	msg->value = cpu_to_le32(power_cap);385 386	if (!pc->async_powercap_cap_set || ignore_dresp) {387		ret = ph->xops->do_xfer(ph, t);388	} else {389		ret = ph->xops->do_xfer_with_response(ph, t);390		if (!ret) {391			struct scmi_msg_resp_powercap_cap_set_complete *resp;392 393			resp = t->rx.buf;394			if (le32_to_cpu(resp->domain) == pc->id)395				dev_dbg(ph->dev,396					"Powercap ID %d CAP set async to %u\n",397					pc->id,398					get_unaligned_le32(&resp->power_cap));399			else400				ret = -EPROTO;401		}402	}403 404	ph->xops->xfer_put(ph, t);405	return ret;406}407 408static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,409				   struct powercap_info *pi, u32 domain_id,410				   u32 power_cap, bool ignore_dresp)411{412	int ret = -EINVAL;413	const struct scmi_powercap_info *pc;414 415	pc = scmi_powercap_dom_info_get(ph, domain_id);416	if (!pc || !pc->powercap_cap_config)417		return ret;418 419	if (power_cap &&420	    (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))421		return ret;422 423	if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {424		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];425 426		iowrite32(power_cap, fci->set_addr);427		ph->hops->fastchannel_db_ring(fci->set_db);428		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,429				   domain_id, power_cap, 0);430		ret = 0;431	} else {432		ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,433						 ignore_dresp);434	}435 436	/* Save the last explicitly set non-zero powercap value */437	if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)438		pi->states[domain_id].last_pcap = power_cap;439 440	return ret;441}442 443static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,444				 u32 domain_id, u32 power_cap,445				 bool ignore_dresp)446{447	struct powercap_info *pi = ph->get_priv(ph);448 449	/*450	 * Disallow zero as a possible explicitly requested powercap:451	 * there are enable/disable operations for this.452	 */453	if (!power_cap)454		return -EINVAL;455 456	/* Just log the last set request if acting on a disabled domain */457	if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&458	    !pi->states[domain_id].enabled) {459		pi->states[domain_id].last_pcap = power_cap;460		return 0;461	}462 463	return __scmi_powercap_cap_set(ph, pi, domain_id,464				       power_cap, ignore_dresp);465}466 467static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,468				      u32 domain_id, u32 *pai)469{470	int ret;471	struct scmi_xfer *t;472 473	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),474				      sizeof(u32), &t);475	if (ret)476		return ret;477 478	put_unaligned_le32(domain_id, t->tx.buf);479	ret = ph->xops->do_xfer(ph, t);480	if (!ret)481		*pai = get_unaligned_le32(t->rx.buf);482 483	ph->xops->xfer_put(ph, t);484 485	return ret;486}487 488static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,489				 u32 domain_id, u32 *pai)490{491	struct scmi_powercap_info *dom;492	struct powercap_info *pi = ph->get_priv(ph);493 494	if (!pai || domain_id >= pi->num_domains)495		return -EINVAL;496 497	dom = pi->powercaps + domain_id;498	if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {499		*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);500		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,501				   domain_id, *pai, 0);502		return 0;503	}504 505	return scmi_powercap_xfer_pai_get(ph, domain_id, pai);506}507 508static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,509				      u32 domain_id, u32 pai)510{511	int ret;512	struct scmi_xfer *t;513	struct scmi_msg_powercap_set_cap_or_pai *msg;514 515	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,516				      sizeof(*msg), 0, &t);517	if (ret)518		return ret;519 520	msg = t->tx.buf;521	msg->domain = cpu_to_le32(domain_id);522	msg->flags = cpu_to_le32(0);523	msg->value = cpu_to_le32(pai);524 525	ret = ph->xops->do_xfer(ph, t);526 527	ph->xops->xfer_put(ph, t);528	return ret;529}530 531static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,532				 u32 domain_id, u32 pai)533{534	const struct scmi_powercap_info *pc;535 536	pc = scmi_powercap_dom_info_get(ph, domain_id);537	if (!pc || !pc->powercap_pai_config || !pai ||538	    pai < pc->min_pai || pai > pc->max_pai)539		return -EINVAL;540 541	if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {542		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];543 544		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,545				   domain_id, pai, 0);546		iowrite32(pai, fci->set_addr);547		ph->hops->fastchannel_db_ring(fci->set_db);548		return 0;549	}550 551	return scmi_powercap_xfer_pai_set(ph, domain_id, pai);552}553 554static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,555					  u32 domain_id, u32 *average_power,556					  u32 *pai)557{558	int ret;559	struct scmi_xfer *t;560	struct scmi_msg_resp_powercap_meas_get *resp;561	const struct scmi_powercap_info *pc;562 563	pc = scmi_powercap_dom_info_get(ph, domain_id);564	if (!pc || !pc->powercap_monitoring || !pai || !average_power)565		return -EINVAL;566 567	ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,568				      sizeof(u32), sizeof(*resp), &t);569	if (ret)570		return ret;571 572	resp = t->rx.buf;573	put_unaligned_le32(domain_id, t->tx.buf);574	ret = ph->xops->do_xfer(ph, t);575	if (!ret) {576		*average_power = le32_to_cpu(resp->power);577		*pai = le32_to_cpu(resp->pai);578	}579 580	ph->xops->xfer_put(ph, t);581	return ret;582}583 584static int585scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,586					 u32 domain_id, u32 *power_thresh_low,587					 u32 *power_thresh_high)588{589	struct powercap_info *pi = ph->get_priv(ph);590 591	if (!power_thresh_low || !power_thresh_high ||592	    domain_id >= pi->num_domains)593		return -EINVAL;594 595	*power_thresh_low =  THRESH_LOW(pi, domain_id);596	*power_thresh_high = THRESH_HIGH(pi, domain_id);597 598	return 0;599}600 601static int602scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,603					 u32 domain_id, u32 power_thresh_low,604					 u32 power_thresh_high)605{606	int ret = 0;607	struct powercap_info *pi = ph->get_priv(ph);608 609	if (domain_id >= pi->num_domains ||610	    power_thresh_low > power_thresh_high)611		return -EINVAL;612 613	/* Anything to do ? */614	if (THRESH_LOW(pi, domain_id) == power_thresh_low &&615	    THRESH_HIGH(pi, domain_id) == power_thresh_high)616		return ret;617 618	pi->states[domain_id].thresholds =619		(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |620		 FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));621 622	/* Update thresholds if notification already enabled */623	if (pi->states[domain_id].meas_notif_enabled)624		ret = scmi_powercap_notify(ph, domain_id,625					   POWERCAP_MEASUREMENTS_NOTIFY,626					   true);627 628	return ret;629}630 631static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,632					u32 domain_id, bool enable)633{634	int ret;635	u32 power_cap;636	struct powercap_info *pi = ph->get_priv(ph);637 638	if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)639		return -EINVAL;640 641	if (enable == pi->states[domain_id].enabled)642		return 0;643 644	if (enable) {645		/* Cannot enable with a zero powercap. */646		if (!pi->states[domain_id].last_pcap)647			return -EINVAL;648 649		ret = __scmi_powercap_cap_set(ph, pi, domain_id,650					      pi->states[domain_id].last_pcap,651					      true);652	} else {653		ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);654	}655 656	if (ret)657		return ret;658 659	/*660	 * Update our internal state to reflect final platform state: the SCMI661	 * server could have ignored a disable request and kept enforcing some662	 * powercap limit requested by other agents.663	 */664	ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);665	if (!ret)666		pi->states[domain_id].enabled = !!power_cap;667 668	return ret;669}670 671static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,672					u32 domain_id, bool *enable)673{674	int ret;675	u32 power_cap;676	struct powercap_info *pi = ph->get_priv(ph);677 678	*enable = true;679	if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)680		return 0;681 682	/*683	 * Report always real platform state; platform could have ignored684	 * a previous disable request. Default true on any error.685	 */686	ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);687	if (!ret)688		*enable = !!power_cap;689 690	/* Update internal state with current real platform state */691	pi->states[domain_id].enabled = *enable;692 693	return 0;694}695 696static const struct scmi_powercap_proto_ops powercap_proto_ops = {697	.num_domains_get = scmi_powercap_num_domains_get,698	.info_get = scmi_powercap_dom_info_get,699	.cap_get = scmi_powercap_cap_get,700	.cap_set = scmi_powercap_cap_set,701	.cap_enable_set = scmi_powercap_cap_enable_set,702	.cap_enable_get = scmi_powercap_cap_enable_get,703	.pai_get = scmi_powercap_pai_get,704	.pai_set = scmi_powercap_pai_set,705	.measurements_get = scmi_powercap_measurements_get,706	.measurements_threshold_set = scmi_powercap_measurements_threshold_set,707	.measurements_threshold_get = scmi_powercap_measurements_threshold_get,708};709 710static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,711					 u32 domain, struct scmi_fc_info **p_fc)712{713	struct scmi_fc_info *fc;714 715	fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);716	if (!fc)717		return;718 719	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,720				   POWERCAP_CAP_SET, 4, domain,721				   &fc[POWERCAP_FC_CAP].set_addr,722				   &fc[POWERCAP_FC_CAP].set_db,723				   &fc[POWERCAP_FC_CAP].rate_limit);724 725	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,726				   POWERCAP_CAP_GET, 4, domain,727				   &fc[POWERCAP_FC_CAP].get_addr, NULL,728				   &fc[POWERCAP_FC_CAP].rate_limit);729 730	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,731				   POWERCAP_PAI_SET, 4, domain,732				   &fc[POWERCAP_FC_PAI].set_addr,733				   &fc[POWERCAP_FC_PAI].set_db,734				   &fc[POWERCAP_FC_PAI].rate_limit);735 736	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,737				   POWERCAP_PAI_GET, 4, domain,738				   &fc[POWERCAP_FC_PAI].get_addr, NULL,739				   &fc[POWERCAP_FC_PAI].rate_limit);740 741	*p_fc = fc;742}743 744static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,745				u32 domain, int message_id, bool enable)746{747	int ret;748	struct scmi_xfer *t;749 750	switch (message_id) {751	case POWERCAP_CAP_NOTIFY:752	{753		struct scmi_msg_powercap_notify_cap *notify;754 755		ret = ph->xops->xfer_get_init(ph, message_id,756					      sizeof(*notify), 0, &t);757		if (ret)758			return ret;759 760		notify = t->tx.buf;761		notify->domain = cpu_to_le32(domain);762		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);763		break;764	}765	case POWERCAP_MEASUREMENTS_NOTIFY:766	{767		u32 low, high;768		struct scmi_msg_powercap_notify_thresh *notify;769 770		/*771		 * Note that we have to pick the most recently configured772		 * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY773		 * enable request and we fail, complaining, if no thresholds774		 * were ever set, since this is an indication the API has been775		 * used wrongly.776		 */777		ret = scmi_powercap_measurements_threshold_get(ph, domain,778							       &low, &high);779		if (ret)780			return ret;781 782		if (enable && !low && !high) {783			dev_err(ph->dev,784				"Invalid Measurements Notify thresholds: %u/%u\n",785				low, high);786			return -EINVAL;787		}788 789		ret = ph->xops->xfer_get_init(ph, message_id,790					      sizeof(*notify), 0, &t);791		if (ret)792			return ret;793 794		notify = t->tx.buf;795		notify->domain = cpu_to_le32(domain);796		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);797		notify->power_thresh_low = cpu_to_le32(low);798		notify->power_thresh_high = cpu_to_le32(high);799		break;800	}801	default:802		return -EINVAL;803	}804 805	ret = ph->xops->do_xfer(ph, t);806 807	ph->xops->xfer_put(ph, t);808	return ret;809}810 811static bool812scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,813			       u8 evt_id, u32 src_id)814{815	bool supported = false;816	const struct scmi_powercap_info *dom_info;817	struct powercap_info *pi = ph->get_priv(ph);818 819	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)820		return false;821 822	dom_info = pi->powercaps + src_id;823	if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)824		supported = dom_info->notify_powercap_cap_change;825	else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)826		supported = dom_info->notify_powercap_measurement_change;827 828	return supported;829}830 831static int832scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,833				 u8 evt_id, u32 src_id, bool enable)834{835	int ret, cmd_id;836	struct powercap_info *pi = ph->get_priv(ph);837 838	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)839		return -EINVAL;840 841	cmd_id = evt_2_cmd[evt_id];842	ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);843	if (ret)844		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",845			 evt_id, src_id, ret);846	else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)847		/*848		 * On success save the current notification enabled state, so849		 * as to be able to properly update the notification thresholds850		 * when they are modified on a domain for which measurement851		 * notifications were currently enabled.852		 *853		 * This is needed because the SCMI Notification core machinery854		 * and API does not support passing per-notification custom855		 * arguments at callback registration time.856		 *857		 * Note that this can be done here with a simple flag since the858		 * SCMI core Notifications code takes care of keeping proper859		 * per-domain enables refcounting, so that this helper function860		 * will be called only once (for enables) when the first user861		 * registers a callback on this domain and once more (disable)862		 * when the last user de-registers its callback.863		 */864		pi->states[src_id].meas_notif_enabled = enable;865 866	return ret;867}868 869static void *870scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,871				 u8 evt_id, ktime_t timestamp,872				 const void *payld, size_t payld_sz,873				 void *report, u32 *src_id)874{875	void *rep = NULL;876 877	switch (evt_id) {878	case SCMI_EVENT_POWERCAP_CAP_CHANGED:879	{880		const struct scmi_powercap_cap_changed_notify_payld *p = payld;881		struct scmi_powercap_cap_changed_report *r = report;882 883		if (sizeof(*p) != payld_sz)884			break;885 886		r->timestamp = timestamp;887		r->agent_id = le32_to_cpu(p->agent_id);888		r->domain_id = le32_to_cpu(p->domain_id);889		r->power_cap = le32_to_cpu(p->power_cap);890		r->pai = le32_to_cpu(p->pai);891		*src_id = r->domain_id;892		rep = r;893		break;894	}895	case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:896	{897		const struct scmi_powercap_meas_changed_notify_payld *p = payld;898		struct scmi_powercap_meas_changed_report *r = report;899 900		if (sizeof(*p) != payld_sz)901			break;902 903		r->timestamp = timestamp;904		r->agent_id = le32_to_cpu(p->agent_id);905		r->domain_id = le32_to_cpu(p->domain_id);906		r->power = le32_to_cpu(p->power);907		*src_id = r->domain_id;908		rep = r;909		break;910	}911	default:912		break;913	}914 915	return rep;916}917 918static int919scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)920{921	struct powercap_info *pi = ph->get_priv(ph);922 923	if (!pi)924		return -EINVAL;925 926	return pi->num_domains;927}928 929static const struct scmi_event powercap_events[] = {930	{931		.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,932		.max_payld_sz =933			sizeof(struct scmi_powercap_cap_changed_notify_payld),934		.max_report_sz =935			sizeof(struct scmi_powercap_cap_changed_report),936	},937	{938		.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,939		.max_payld_sz =940			sizeof(struct scmi_powercap_meas_changed_notify_payld),941		.max_report_sz =942			sizeof(struct scmi_powercap_meas_changed_report),943	},944};945 946static const struct scmi_event_ops powercap_event_ops = {947	.is_notify_supported = scmi_powercap_notify_supported,948	.get_num_sources = scmi_powercap_get_num_sources,949	.set_notify_enabled = scmi_powercap_set_notify_enabled,950	.fill_custom_report = scmi_powercap_fill_custom_report,951};952 953static const struct scmi_protocol_events powercap_protocol_events = {954	.queue_sz = SCMI_PROTO_QUEUE_SZ,955	.ops = &powercap_event_ops,956	.evts = powercap_events,957	.num_events = ARRAY_SIZE(powercap_events),958};959 960static int961scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)962{963	int domain, ret;964	u32 version;965	struct powercap_info *pinfo;966 967	ret = ph->xops->version_get(ph, &version);968	if (ret)969		return ret;970 971	dev_dbg(ph->dev, "Powercap Version %d.%d\n",972		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));973 974	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);975	if (!pinfo)976		return -ENOMEM;977 978	ret = scmi_powercap_attributes_get(ph, pinfo);979	if (ret)980		return ret;981 982	pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,983					sizeof(*pinfo->powercaps),984					GFP_KERNEL);985	if (!pinfo->powercaps)986		return -ENOMEM;987 988	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,989				     sizeof(*pinfo->states), GFP_KERNEL);990	if (!pinfo->states)991		return -ENOMEM;992 993	/*994	 * Note that any failure in retrieving any domain attribute leads to995	 * the whole Powercap protocol initialization failure: this way the996	 * reported Powercap domains are all assured, when accessed, to be well997	 * formed and correlated by sane parent-child relationship (if any).998	 */999	for (domain = 0; domain < pinfo->num_domains; domain++) {1000		ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);1001		if (ret)1002			return ret;1003 1004		if (pinfo->powercaps[domain].fastchannels)1005			scmi_powercap_domain_init_fc(ph, domain,1006						     &pinfo->powercaps[domain].fc_info);1007 1008		/* Grab initial state when disable is supported. */1009		if (PROTOCOL_REV_MAJOR(version) >= 0x2) {1010			ret = __scmi_powercap_cap_get(ph,1011						      &pinfo->powercaps[domain],1012						      &pinfo->states[domain].last_pcap);1013			if (ret)1014				return ret;1015 1016			pinfo->states[domain].enabled =1017				!!pinfo->states[domain].last_pcap;1018		}1019	}1020 1021	pinfo->version = version;1022	return ph->set_priv(ph, pinfo, version);1023}1024 1025static const struct scmi_protocol scmi_powercap = {1026	.id = SCMI_PROTOCOL_POWERCAP,1027	.owner = THIS_MODULE,1028	.instance_init = &scmi_powercap_protocol_init,1029	.ops = &powercap_proto_ops,1030	.events = &powercap_protocol_events,1031	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,1032};1033 1034DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)1035