Skip to content

Commit 758cd5f

Browse files
cris-masudeep-holla
authored andcommitted
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: Cristian Marussi <cristian.marussi@arm.com> Link: https://lore.kernel.org/r/20230531152039.2363181-3-cristian.marussi@arm.com Signed-off-by: Sudeep Holla <sudeep.holla@arm.com>
1 parent 4e1a53b commit 758cd5f

2 files changed

Lines changed: 122 additions & 6 deletions

File tree

drivers/firmware/arm_scmi/powercap.c

Lines changed: 104 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ struct scmi_powercap_meas_changed_notify_payld {
108108
};
109109

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

417+
/* Save the last explicitly set non-zero powercap value */
418+
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
419+
pi->states[domain_id].last_pcap = power_cap;
420+
415421
return ret;
416422
}
417423

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

430+
/*
431+
* Disallow zero as a possible explicitly requested powercap:
432+
* there are enable/disable operations for this.
433+
*/
434+
if (!power_cap)
435+
return -EINVAL;
436+
437+
/* Just log the last set request if acting on a disabled domain */
438+
if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
439+
!pi->states[domain_id].enabled) {
440+
pi->states[domain_id].last_pcap = power_cap;
441+
return 0;
442+
}
443+
424444
return __scmi_powercap_cap_set(ph, pi, domain_id,
425445
power_cap, ignore_dresp);
426446
}
@@ -589,11 +609,78 @@ scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
589609
return ret;
590610
}
591611

612+
static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
613+
u32 domain_id, bool enable)
614+
{
615+
int ret;
616+
u32 power_cap;
617+
struct powercap_info *pi = ph->get_priv(ph);
618+
619+
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
620+
return -EINVAL;
621+
622+
if (enable == pi->states[domain_id].enabled)
623+
return 0;
624+
625+
if (enable) {
626+
/* Cannot enable with a zero powercap. */
627+
if (!pi->states[domain_id].last_pcap)
628+
return -EINVAL;
629+
630+
ret = __scmi_powercap_cap_set(ph, pi, domain_id,
631+
pi->states[domain_id].last_pcap,
632+
true);
633+
} else {
634+
ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
635+
}
636+
637+
if (ret)
638+
return ret;
639+
640+
/*
641+
* Update our internal state to reflect final platform state: the SCMI
642+
* server could have ignored a disable request and kept enforcing some
643+
* powercap limit requested by other agents.
644+
*/
645+
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
646+
if (!ret)
647+
pi->states[domain_id].enabled = !!power_cap;
648+
649+
return ret;
650+
}
651+
652+
static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
653+
u32 domain_id, bool *enable)
654+
{
655+
int ret;
656+
u32 power_cap;
657+
struct powercap_info *pi = ph->get_priv(ph);
658+
659+
*enable = true;
660+
if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
661+
return 0;
662+
663+
/*
664+
* Report always real platform state; platform could have ignored
665+
* a previous disable request. Default true on any error.
666+
*/
667+
ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
668+
if (!ret)
669+
*enable = !!power_cap;
670+
671+
/* Update internal state with current real platform state */
672+
pi->states[domain_id].enabled = *enable;
673+
674+
return 0;
675+
}
676+
592677
static const struct scmi_powercap_proto_ops powercap_proto_ops = {
593678
.num_domains_get = scmi_powercap_num_domains_get,
594679
.info_get = scmi_powercap_dom_info_get,
595680
.cap_get = scmi_powercap_cap_get,
596681
.cap_set = scmi_powercap_cap_set,
682+
.cap_enable_set = scmi_powercap_cap_enable_set,
683+
.cap_enable_get = scmi_powercap_cap_enable_get,
597684
.pai_get = scmi_powercap_pai_get,
598685
.pai_set = scmi_powercap_pai_set,
599686
.measurements_get = scmi_powercap_measurements_get,
@@ -854,6 +941,11 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
854941
if (!pinfo->powercaps)
855942
return -ENOMEM;
856943

944+
pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
945+
sizeof(*pinfo->states), GFP_KERNEL);
946+
if (!pinfo->states)
947+
return -ENOMEM;
948+
857949
/*
858950
* Note that any failure in retrieving any domain attribute leads to
859951
* the whole Powercap protocol initialization failure: this way the
@@ -868,15 +960,21 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
868960
if (pinfo->powercaps[domain].fastchannels)
869961
scmi_powercap_domain_init_fc(ph, domain,
870962
&pinfo->powercaps[domain].fc_info);
871-
}
872963

873-
pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
874-
sizeof(*pinfo->states), GFP_KERNEL);
875-
if (!pinfo->states)
876-
return -ENOMEM;
964+
/* Grab initial state when disable is supported. */
965+
if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
966+
ret = __scmi_powercap_cap_get(ph,
967+
&pinfo->powercaps[domain],
968+
&pinfo->states[domain].last_pcap);
969+
if (ret)
970+
return ret;
877971

878-
pinfo->version = version;
972+
pinfo->states[domain].enabled =
973+
!!pinfo->states[domain].last_pcap;
974+
}
975+
}
879976

977+
pinfo->version = version;
880978
return ph->set_priv(ph, pinfo);
881979
}
882980

include/linux/scmi_protocol.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -629,11 +629,25 @@ struct scmi_powercap_info {
629629
* @num_domains_get: get the count of powercap domains provided by SCMI.
630630
* @info_get: get the information for the specified domain.
631631
* @cap_get: get the current CAP value for the specified domain.
632+
* On SCMI platforms supporting powercap zone disabling, this could
633+
* report a zero value for a zone where powercapping is disabled.
632634
* @cap_set: set the CAP value for the specified domain to the provided value;
633635
* if the domain supports setting the CAP with an asynchronous command
634636
* this request will finally trigger an asynchronous transfer, but, if
635637
* @ignore_dresp here is set to true, this call will anyway return
636638
* immediately without waiting for the related delayed response.
639+
* Note that the powercap requested value must NOT be zero, even if
640+
* the platform supports disabling a powercap by setting its cap to
641+
* zero (since SCMI v3.2): there are dedicated operations that should
642+
* be used for that. (@cap_enable_set/get)
643+
* @cap_enable_set: enable or disable the powercapping on the specified domain,
644+
* if supported by the SCMI platform implementation.
645+
* Note that, by the SCMI specification, the platform can
646+
* silently ignore our disable request and decide to enforce
647+
* anyway some other powercap value requested by another agent
648+
* on the system: for this reason @cap_get and @cap_enable_get
649+
* will always report the final platform view of the powercaps.
650+
* @cap_enable_get: get the current CAP enable status for the specified domain.
637651
* @pai_get: get the current PAI value for the specified domain.
638652
* @pai_set: set the PAI value for the specified domain to the provided value.
639653
* @measurements_get: retrieve the current average power measurements for the
@@ -662,6 +676,10 @@ struct scmi_powercap_proto_ops {
662676
u32 *power_cap);
663677
int (*cap_set)(const struct scmi_protocol_handle *ph, u32 domain_id,
664678
u32 power_cap, bool ignore_dresp);
679+
int (*cap_enable_set)(const struct scmi_protocol_handle *ph,
680+
u32 domain_id, bool enable);
681+
int (*cap_enable_get)(const struct scmi_protocol_handle *ph,
682+
u32 domain_id, bool *enable);
665683
int (*pai_get)(const struct scmi_protocol_handle *ph, u32 domain_id,
666684
u32 *pai);
667685
int (*pai_set)(const struct scmi_protocol_handle *ph, u32 domain_id,

0 commit comments

Comments
 (0)