Commit 758cd5fc authored by Cristian Marussi's avatar Cristian Marussi Committed by Sudeep Holla
Browse files

firmware: arm_scmi: Add Powercap protocol enable support



SCMI powercap protocol v3.2 supports disabling the powercap on a zone
by zone basis by providing a zero valued powercap.

Expose new operations to enable/disable powercapping on a per-zone base.

Signed-off-by: default avatarCristian Marussi <cristian.marussi@arm.com>
Link: https://lore.kernel.org/r/20230531152039.2363181-3-cristian.marussi@arm.com


Signed-off-by: default avatarSudeep Holla <sudeep.holla@arm.com>
parent 4e1a53b4
Loading
Loading
Loading
Loading
+104 −6
Original line number Diff line number Diff line
@@ -108,6 +108,8 @@ struct scmi_powercap_meas_changed_notify_payld {
};

struct scmi_powercap_state {
	bool enabled;
	u32 last_pcap;
	bool meas_notif_enabled;
	u64 thresholds;
#define THRESH_LOW(p, id)				\
@@ -412,6 +414,10 @@ static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
						 ignore_dresp);
	}

	/* Save the last explicitly set non-zero powercap value */
	if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
		pi->states[domain_id].last_pcap = power_cap;

	return ret;
}

@@ -421,6 +427,20 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
{
	struct powercap_info *pi = ph->get_priv(ph);

	/*
	 * Disallow zero as a possible explicitly requested powercap:
	 * there are enable/disable operations for this.
	 */
	if (!power_cap)
		return -EINVAL;

	/* Just log the last set request if acting on a disabled domain */
	if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
	    !pi->states[domain_id].enabled) {
		pi->states[domain_id].last_pcap = power_cap;
		return 0;
	}

	return __scmi_powercap_cap_set(ph, pi, domain_id,
				       power_cap, ignore_dresp);
}
@@ -589,11 +609,78 @@ scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
	return ret;
}

static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
					u32 domain_id, bool enable)
{
	int ret;
	u32 power_cap;
	struct powercap_info *pi = ph->get_priv(ph);

	if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
		return -EINVAL;

	if (enable == pi->states[domain_id].enabled)
		return 0;

	if (enable) {
		/* Cannot enable with a zero powercap. */
		if (!pi->states[domain_id].last_pcap)
			return -EINVAL;

		ret = __scmi_powercap_cap_set(ph, pi, domain_id,
					      pi->states[domain_id].last_pcap,
					      true);
	} else {
		ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
	}

	if (ret)
		return ret;

	/*
	 * Update our internal state to reflect final platform state: the SCMI
	 * server could have ignored a disable request and kept enforcing some
	 * powercap limit requested by other agents.
	 */
	ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
	if (!ret)
		pi->states[domain_id].enabled = !!power_cap;

	return ret;
}

static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
					u32 domain_id, bool *enable)
{
	int ret;
	u32 power_cap;
	struct powercap_info *pi = ph->get_priv(ph);

	*enable = true;
	if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
		return 0;

	/*
	 * Report always real platform state; platform could have ignored
	 * a previous disable request. Default true on any error.
	 */
	ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
	if (!ret)
		*enable = !!power_cap;

	/* Update internal state with current real platform state */
	pi->states[domain_id].enabled = *enable;

	return 0;
}

static const struct scmi_powercap_proto_ops powercap_proto_ops = {
	.num_domains_get = scmi_powercap_num_domains_get,
	.info_get = scmi_powercap_dom_info_get,
	.cap_get = scmi_powercap_cap_get,
	.cap_set = scmi_powercap_cap_set,
	.cap_enable_set = scmi_powercap_cap_enable_set,
	.cap_enable_get = scmi_powercap_cap_enable_get,
	.pai_get = scmi_powercap_pai_get,
	.pai_set = scmi_powercap_pai_set,
	.measurements_get = scmi_powercap_measurements_get,
@@ -854,6 +941,11 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
	if (!pinfo->powercaps)
		return -ENOMEM;

	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
				     sizeof(*pinfo->states), GFP_KERNEL);
	if (!pinfo->states)
		return -ENOMEM;

	/*
	 * Note that any failure in retrieving any domain attribute leads to
	 * the whole Powercap protocol initialization failure: this way the
@@ -868,15 +960,21 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
		if (pinfo->powercaps[domain].fastchannels)
			scmi_powercap_domain_init_fc(ph, domain,
						     &pinfo->powercaps[domain].fc_info);
	}

	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
				     sizeof(*pinfo->states), GFP_KERNEL);
	if (!pinfo->states)
		return -ENOMEM;
		/* Grab initial state when disable is supported. */
		if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
			ret = __scmi_powercap_cap_get(ph,
						      &pinfo->powercaps[domain],
						      &pinfo->states[domain].last_pcap);
			if (ret)
				return ret;

	pinfo->version = version;
			pinfo->states[domain].enabled =
				!!pinfo->states[domain].last_pcap;
		}
	}

	pinfo->version = version;
	return ph->set_priv(ph, pinfo);
}

+18 −0
Original line number Diff line number Diff line
@@ -629,11 +629,25 @@ struct scmi_powercap_info {
 * @num_domains_get: get the count of powercap domains provided by SCMI.
 * @info_get: get the information for the specified domain.
 * @cap_get: get the current CAP value for the specified domain.
 *	     On SCMI platforms supporting powercap zone disabling, this could
 *	     report a zero value for a zone where powercapping is disabled.
 * @cap_set: set the CAP value for the specified domain to the provided value;
 *	     if the domain supports setting the CAP with an asynchronous command
 *	     this request will finally trigger an asynchronous transfer, but, if
 *	     @ignore_dresp here is set to true, this call will anyway return
 *	     immediately without waiting for the related delayed response.
 *	     Note that the powercap requested value must NOT be zero, even if
 *	     the platform supports disabling a powercap by setting its cap to
 *	     zero (since SCMI v3.2): there are dedicated operations that should
 *	     be used for that. (@cap_enable_set/get)
 * @cap_enable_set: enable or disable the powercapping on the specified domain,
 *		    if supported by the SCMI platform implementation.
 *		    Note that, by the SCMI specification, the platform can
 *		    silently ignore our disable request and decide to enforce
 *		    anyway some other powercap value requested by another agent
 *		    on the system: for this reason @cap_get and @cap_enable_get
 *		    will always report the final platform view of the powercaps.
 * @cap_enable_get: get the current CAP enable status for the specified domain.
 * @pai_get: get the current PAI value for the specified domain.
 * @pai_set: set the PAI value for the specified domain to the provided value.
 * @measurements_get: retrieve the current average power measurements for the
@@ -662,6 +676,10 @@ struct scmi_powercap_proto_ops {
		       u32 *power_cap);
	int (*cap_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
		       u32 power_cap, bool ignore_dresp);
	int (*cap_enable_set)(const struct scmi_protocol_handle *ph,
			      u32 domain_id, bool enable);
	int (*cap_enable_get)(const struct scmi_protocol_handle *ph,
			      u32 domain_id, bool *enable);
	int (*pai_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
		       u32 *pai);
	int (*pai_set)(const struct scmi_protocol_handle *ph, u32 domain_id,