@@ -108,6 +108,8 @@ struct scmi_powercap_meas_changed_notify_payld {
108108};
109109
110110struct scmi_powercap_state {
111+ bool enabled ;
112+ u32 last_pcap ;
111113 bool meas_notif_enabled ;
112114 u64 thresholds ;
113115#define THRESH_LOW (p , id ) \
@@ -313,24 +315,33 @@ static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
313315 return ret ;
314316}
315317
316- static int scmi_powercap_cap_get (const struct scmi_protocol_handle * ph ,
317- u32 domain_id , u32 * power_cap )
318+ static int __scmi_powercap_cap_get (const struct scmi_protocol_handle * ph ,
319+ const struct scmi_powercap_info * dom ,
320+ u32 * power_cap )
318321{
319- struct scmi_powercap_info * dom ;
320- struct powercap_info * pi = ph -> get_priv (ph );
321-
322- if (!power_cap || domain_id >= pi -> num_domains )
323- return - EINVAL ;
324-
325- dom = pi -> powercaps + domain_id ;
326322 if (dom -> fc_info && dom -> fc_info [POWERCAP_FC_CAP ].get_addr ) {
327323 * power_cap = ioread32 (dom -> fc_info [POWERCAP_FC_CAP ].get_addr );
328324 trace_scmi_fc_call (SCMI_PROTOCOL_POWERCAP , POWERCAP_CAP_GET ,
329- domain_id , * power_cap , 0 );
325+ dom -> id , * power_cap , 0 );
330326 return 0 ;
331327 }
332328
333- return scmi_powercap_xfer_cap_get (ph , domain_id , power_cap );
329+ return scmi_powercap_xfer_cap_get (ph , dom -> id , power_cap );
330+ }
331+
332+ static int scmi_powercap_cap_get (const struct scmi_protocol_handle * ph ,
333+ u32 domain_id , u32 * power_cap )
334+ {
335+ const struct scmi_powercap_info * dom ;
336+
337+ if (!power_cap )
338+ return - EINVAL ;
339+
340+ dom = scmi_powercap_dom_info_get (ph , domain_id );
341+ if (!dom )
342+ return - EINVAL ;
343+
344+ return __scmi_powercap_cap_get (ph , dom , power_cap );
334345}
335346
336347static int scmi_powercap_xfer_cap_set (const struct scmi_protocol_handle * ph ,
@@ -375,17 +386,20 @@ static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
375386 return ret ;
376387}
377388
378- static int scmi_powercap_cap_set (const struct scmi_protocol_handle * ph ,
379- u32 domain_id , u32 power_cap ,
380- bool ignore_dresp )
389+ static int __scmi_powercap_cap_set (const struct scmi_protocol_handle * ph ,
390+ struct powercap_info * pi , u32 domain_id ,
391+ u32 power_cap , bool ignore_dresp )
381392{
393+ int ret = - EINVAL ;
382394 const struct scmi_powercap_info * pc ;
383395
384396 pc = scmi_powercap_dom_info_get (ph , domain_id );
385- if (!pc || !pc -> powercap_cap_config || !power_cap ||
386- power_cap < pc -> min_power_cap ||
387- power_cap > pc -> max_power_cap )
388- return - EINVAL ;
397+ if (!pc || !pc -> powercap_cap_config )
398+ return ret ;
399+
400+ if (power_cap &&
401+ (power_cap < pc -> min_power_cap || power_cap > pc -> max_power_cap ))
402+ return ret ;
389403
390404 if (pc -> fc_info && pc -> fc_info [POWERCAP_FC_CAP ].set_addr ) {
391405 struct scmi_fc_info * fci = & pc -> fc_info [POWERCAP_FC_CAP ];
@@ -394,10 +408,41 @@ static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
394408 ph -> hops -> fastchannel_db_ring (fci -> set_db );
395409 trace_scmi_fc_call (SCMI_PROTOCOL_POWERCAP , POWERCAP_CAP_SET ,
396410 domain_id , power_cap , 0 );
411+ ret = 0 ;
412+ } else {
413+ ret = scmi_powercap_xfer_cap_set (ph , pc , power_cap ,
414+ ignore_dresp );
415+ }
416+
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+
421+ return ret ;
422+ }
423+
424+ static int scmi_powercap_cap_set (const struct scmi_protocol_handle * ph ,
425+ u32 domain_id , u32 power_cap ,
426+ bool ignore_dresp )
427+ {
428+ struct powercap_info * pi = ph -> get_priv (ph );
429+
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 ;
397441 return 0 ;
398442 }
399443
400- return scmi_powercap_xfer_cap_set (ph , pc , power_cap , ignore_dresp );
444+ return __scmi_powercap_cap_set (ph , pi , domain_id ,
445+ power_cap , ignore_dresp );
401446}
402447
403448static int scmi_powercap_xfer_pai_get (const struct scmi_protocol_handle * ph ,
@@ -564,11 +609,78 @@ scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
564609 return ret ;
565610}
566611
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+
567677static const struct scmi_powercap_proto_ops powercap_proto_ops = {
568678 .num_domains_get = scmi_powercap_num_domains_get ,
569679 .info_get = scmi_powercap_dom_info_get ,
570680 .cap_get = scmi_powercap_cap_get ,
571681 .cap_set = scmi_powercap_cap_set ,
682+ .cap_enable_set = scmi_powercap_cap_enable_set ,
683+ .cap_enable_get = scmi_powercap_cap_enable_get ,
572684 .pai_get = scmi_powercap_pai_get ,
573685 .pai_set = scmi_powercap_pai_set ,
574686 .measurements_get = scmi_powercap_measurements_get ,
@@ -829,6 +941,11 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
829941 if (!pinfo -> powercaps )
830942 return - ENOMEM ;
831943
944+ pinfo -> states = devm_kcalloc (ph -> dev , pinfo -> num_domains ,
945+ sizeof (* pinfo -> states ), GFP_KERNEL );
946+ if (!pinfo -> states )
947+ return - ENOMEM ;
948+
832949 /*
833950 * Note that any failure in retrieving any domain attribute leads to
834951 * the whole Powercap protocol initialization failure: this way the
@@ -843,15 +960,21 @@ scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
843960 if (pinfo -> powercaps [domain ].fastchannels )
844961 scmi_powercap_domain_init_fc (ph , domain ,
845962 & pinfo -> powercaps [domain ].fc_info );
846- }
847963
848- pinfo -> states = devm_kcalloc (ph -> dev , pinfo -> num_domains ,
849- sizeof (* pinfo -> states ), GFP_KERNEL );
850- if (!pinfo -> states )
851- 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 ;
852971
853- pinfo -> version = version ;
972+ pinfo -> states [domain ].enabled =
973+ !!pinfo -> states [domain ].last_pcap ;
974+ }
975+ }
854976
977+ pinfo -> version = version ;
855978 return ph -> set_priv (ph , pinfo );
856979}
857980
0 commit comments