1212#include <linux/module.h>
1313#include <linux/powercap.h>
1414#include <linux/scmi_protocol.h>
15+ #include <linux/slab.h>
1516
1617#define to_scmi_powercap_zone (z ) \
1718 container_of(z, struct scmi_powercap_zone, zone)
1819
1920static const struct scmi_powercap_proto_ops * powercap_ops ;
2021
2122struct scmi_powercap_zone {
23+ bool registered ;
24+ bool invalid ;
2225 unsigned int height ;
2326 struct device * dev ;
2427 struct scmi_protocol_handle * ph ;
@@ -32,6 +35,7 @@ struct scmi_powercap_root {
3235 unsigned int num_zones ;
3336 struct scmi_powercap_zone * spzones ;
3437 struct list_head * registered_zones ;
38+ struct list_head scmi_zones ;
3539};
3640
3741static struct powercap_control_type * scmi_top_pcntrl ;
@@ -271,12 +275,6 @@ static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr)
271275 }
272276}
273277
274- static inline bool
275- scmi_powercap_is_zone_registered (struct scmi_powercap_zone * spz )
276- {
277- return !list_empty (& spz -> node );
278- }
279-
280278static inline unsigned int
281279scmi_powercap_get_zone_height (struct scmi_powercap_zone * spz )
282280{
@@ -295,11 +293,46 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
295293 return & spz -> spzones [spz -> info -> parent_id ];
296294}
297295
296+ static int scmi_powercap_register_zone (struct scmi_powercap_root * pr ,
297+ struct scmi_powercap_zone * spz ,
298+ struct scmi_powercap_zone * parent )
299+ {
300+ int ret = 0 ;
301+ struct powercap_zone * z ;
302+
303+ if (spz -> invalid ) {
304+ list_del (& spz -> node );
305+ return - EINVAL ;
306+ }
307+
308+ z = powercap_register_zone (& spz -> zone , scmi_top_pcntrl , spz -> info -> name ,
309+ parent ? & parent -> zone : NULL ,
310+ & zone_ops , 1 , & constraint_ops );
311+ if (!IS_ERR (z )) {
312+ spz -> height = scmi_powercap_get_zone_height (spz );
313+ spz -> registered = true;
314+ list_move (& spz -> node , & pr -> registered_zones [spz -> height ]);
315+ dev_dbg (spz -> dev , "Registered node %s - parent %s - height:%d\n" ,
316+ spz -> info -> name , parent ? parent -> info -> name : "ROOT" ,
317+ spz -> height );
318+ } else {
319+ list_del (& spz -> node );
320+ ret = PTR_ERR (z );
321+ dev_err (spz -> dev ,
322+ "Error registering node:%s - parent:%s - h:%d - ret:%d\n" ,
323+ spz -> info -> name ,
324+ parent ? parent -> info -> name : "ROOT" ,
325+ spz -> height , ret );
326+ }
327+
328+ return ret ;
329+ }
330+
298331/**
299- * scmi_powercap_register_zone - Register an SCMI powercap zone recursively
332+ * scmi_zones_register - Register SCMI powercap zones starting from parent zones
300333 *
334+ * @dev: A reference to the SCMI device
301335 * @pr: A reference to the root powercap zones descriptors
302- * @spz: A reference to the SCMI powercap zone to register
303336 *
304337 * When registering SCMI powercap zones with the powercap framework we should
305338 * take care to always register zones starting from the root ones and to
@@ -309,10 +342,10 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
309342 * zones provided by the SCMI platform firmware is built to comply with such
310343 * requirement.
311344 *
312- * This function, given an SCMI powercap zone to register, takes care to walk
313- * the SCMI powercap zones tree up to the root looking recursively for
314- * unregistered parent zones before registering the provided zone ; at the same
315- * time each registered zone height in such a tree is accounted for and each
345+ * This function, given the set of SCMI powercap zones to register, takes care
346+ * to walk the SCMI powercap zones trees up to the root registering any
347+ * unregistered parent zone before registering the child zones ; at the same
348+ * time each registered- zone height in such a tree is accounted for and each
316349 * zone, once registered, is stored in the @registered_zones array that is
317350 * indexed by zone height: this way will be trivial, at unregister time, to walk
318351 * the @registered_zones array backward and unregister all the zones starting
@@ -330,57 +363,55 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
330363 *
331364 * Return: 0 on Success
332365 */
333- static int scmi_powercap_register_zone (struct scmi_powercap_root * pr ,
334- struct scmi_powercap_zone * spz )
366+ static int scmi_zones_register (struct device * dev ,
367+ struct scmi_powercap_root * pr )
335368{
336369 int ret = 0 ;
337- struct scmi_powercap_zone * parent ;
338-
339- if (!spz -> info )
340- return ret ;
370+ unsigned int sp = 0 , reg_zones = 0 ;
371+ struct scmi_powercap_zone * spz , * * zones_stack ;
341372
342- parent = scmi_powercap_get_parent_zone (spz );
343- if (parent && !scmi_powercap_is_zone_registered (parent )) {
344- /*
345- * Bail out if a parent domain was marked as unsupported:
346- * only domains participating as leaves can be skipped.
347- */
348- if (!parent -> info )
349- return - ENODEV ;
373+ zones_stack = kcalloc (pr -> num_zones , sizeof (spz ), GFP_KERNEL );
374+ if (!zones_stack )
375+ return - ENOMEM ;
350376
351- ret = scmi_powercap_register_zone ( pr , parent );
352- if ( ret )
353- return ret ;
354- }
377+ spz = list_first_entry_or_null ( & pr -> scmi_zones ,
378+ struct scmi_powercap_zone , node );
379+ while ( spz ) {
380+ struct scmi_powercap_zone * parent ;
355381
356- if (!scmi_powercap_is_zone_registered (spz )) {
357- struct powercap_zone * z ;
358-
359- z = powercap_register_zone (& spz -> zone ,
360- scmi_top_pcntrl ,
361- spz -> info -> name ,
362- parent ? & parent -> zone : NULL ,
363- & zone_ops , 1 , & constraint_ops );
364- if (!IS_ERR (z )) {
365- spz -> height = scmi_powercap_get_zone_height (spz );
366- list_add (& spz -> node ,
367- & pr -> registered_zones [spz -> height ]);
368- dev_dbg (spz -> dev ,
369- "Registered node %s - parent %s - height:%d\n" ,
370- spz -> info -> name ,
371- parent ? parent -> info -> name : "ROOT" ,
372- spz -> height );
373- ret = 0 ;
382+ parent = scmi_powercap_get_parent_zone (spz );
383+ if (parent && !parent -> registered ) {
384+ zones_stack [sp ++ ] = spz ;
385+ spz = parent ;
374386 } else {
375- ret = PTR_ERR (z );
376- dev_err (spz -> dev ,
377- "Error registering node:%s - parent:%s - h:%d - ret:%d\n" ,
378- spz -> info -> name ,
379- parent ? parent -> info -> name : "ROOT" ,
380- spz -> height , ret );
387+ ret = scmi_powercap_register_zone (pr , spz , parent );
388+ if (!ret ) {
389+ reg_zones ++ ;
390+ } else if (sp ) {
391+ /* Failed to register a non-leaf zone.
392+ * Bail-out.
393+ */
394+ dev_err (dev ,
395+ "Failed to register non-leaf zone - ret:%d\n" ,
396+ ret );
397+ scmi_powercap_unregister_all_zones (pr );
398+ reg_zones = 0 ;
399+ goto out ;
400+ }
401+ /* Pick next zone to process */
402+ if (sp )
403+ spz = zones_stack [-- sp ];
404+ else
405+ spz = list_first_entry_or_null (& pr -> scmi_zones ,
406+ struct scmi_powercap_zone ,
407+ node );
381408 }
382409 }
383410
411+ out :
412+ kfree (zones_stack );
413+ dev_info (dev , "Registered %d SCMI Powercap domains !\n" , reg_zones );
414+
384415 return ret ;
385416}
386417
@@ -424,6 +455,8 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
424455 if (!pr -> registered_zones )
425456 return - ENOMEM ;
426457
458+ INIT_LIST_HEAD (& pr -> scmi_zones );
459+
427460 for (i = 0 , spz = pr -> spzones ; i < pr -> num_zones ; i ++ , spz ++ ) {
428461 /*
429462 * Powercap domains are validate by the protocol layer, i.e.
@@ -438,6 +471,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
438471 INIT_LIST_HEAD (& spz -> node );
439472 INIT_LIST_HEAD (& pr -> registered_zones [i ]);
440473
474+ list_add_tail (& spz -> node , & pr -> scmi_zones );
441475 /*
442476 * Forcibly skip powercap domains using an abstract scale.
443477 * Note that only leaves domains can be skipped, so this could
@@ -448,7 +482,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
448482 dev_warn (dev ,
449483 "Abstract power scale not supported. Skip %s.\n" ,
450484 spz -> info -> name );
451- spz -> info = NULL ;
485+ spz -> invalid = true ;
452486 continue ;
453487 }
454488 }
@@ -457,21 +491,12 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
457491 * Scan array of retrieved SCMI powercap domains and register them
458492 * recursively starting from the root domains.
459493 */
460- for (i = 0 , spz = pr -> spzones ; i < pr -> num_zones ; i ++ , spz ++ ) {
461- ret = scmi_powercap_register_zone (pr , spz );
462- if (ret ) {
463- dev_err (dev ,
464- "Failed to register powercap zone %s - ret:%d\n" ,
465- spz -> info -> name , ret );
466- scmi_powercap_unregister_all_zones (pr );
467- return ret ;
468- }
469- }
494+ ret = scmi_zones_register (dev , pr );
495+ if (ret )
496+ return ret ;
470497
471498 dev_set_drvdata (dev , pr );
472499
473- dev_info (dev , "Registered %d SCMI Powercap domains !\n" , pr -> num_zones );
474-
475500 return ret ;
476501}
477502
0 commit comments