};
 
 struct scmi_powercap_state {
+       bool enabled;
+       u32 last_pcap;
        bool meas_notif_enabled;
        u64 thresholds;
 #define THRESH_LOW(p, id)                              \
                                                 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;
 }
 
 {
        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);
 }
        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,
        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
                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);
 }
 
 
  * @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
                       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,