12
12
#include <linux/module.h>
13
13
#include <linux/powercap.h>
14
14
#include <linux/scmi_protocol.h>
15
+ #include <linux/slab.h>
15
16
16
17
#define to_scmi_powercap_zone (z ) \
17
18
container_of(z, struct scmi_powercap_zone, zone)
18
19
19
20
static const struct scmi_powercap_proto_ops * powercap_ops ;
20
21
21
22
struct scmi_powercap_zone {
23
+ bool registered ;
24
+ bool invalid ;
22
25
unsigned int height ;
23
26
struct device * dev ;
24
27
struct scmi_protocol_handle * ph ;
@@ -32,6 +35,7 @@ struct scmi_powercap_root {
32
35
unsigned int num_zones ;
33
36
struct scmi_powercap_zone * spzones ;
34
37
struct list_head * registered_zones ;
38
+ struct list_head scmi_zones ;
35
39
};
36
40
37
41
static struct powercap_control_type * scmi_top_pcntrl ;
@@ -271,12 +275,6 @@ static void scmi_powercap_unregister_all_zones(struct scmi_powercap_root *pr)
271
275
}
272
276
}
273
277
274
- static inline bool
275
- scmi_powercap_is_zone_registered (struct scmi_powercap_zone * spz )
276
- {
277
- return !list_empty (& spz -> node );
278
- }
279
-
280
278
static inline unsigned int
281
279
scmi_powercap_get_zone_height (struct scmi_powercap_zone * spz )
282
280
{
@@ -295,11 +293,46 @@ scmi_powercap_get_parent_zone(struct scmi_powercap_zone *spz)
295
293
return & spz -> spzones [spz -> info -> parent_id ];
296
294
}
297
295
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
+
298
331
/**
299
- * scmi_powercap_register_zone - Register an SCMI powercap zone recursively
332
+ * scmi_zones_register - Register SCMI powercap zones starting from parent zones
300
333
*
334
+ * @dev: A reference to the SCMI device
301
335
* @pr: A reference to the root powercap zones descriptors
302
- * @spz: A reference to the SCMI powercap zone to register
303
336
*
304
337
* When registering SCMI powercap zones with the powercap framework we should
305
338
* 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)
309
342
* zones provided by the SCMI platform firmware is built to comply with such
310
343
* requirement.
311
344
*
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
316
349
* zone, once registered, is stored in the @registered_zones array that is
317
350
* indexed by zone height: this way will be trivial, at unregister time, to walk
318
351
* 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)
330
363
*
331
364
* Return: 0 on Success
332
365
*/
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 )
335
368
{
336
369
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 ;
341
372
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 ;
350
376
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 ;
355
381
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 ;
374
386
} 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 );
381
408
}
382
409
}
383
410
411
+ out :
412
+ kfree (zones_stack );
413
+ dev_info (dev , "Registered %d SCMI Powercap domains !\n" , reg_zones );
414
+
384
415
return ret ;
385
416
}
386
417
@@ -424,6 +455,8 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
424
455
if (!pr -> registered_zones )
425
456
return - ENOMEM ;
426
457
458
+ INIT_LIST_HEAD (& pr -> scmi_zones );
459
+
427
460
for (i = 0 , spz = pr -> spzones ; i < pr -> num_zones ; i ++ , spz ++ ) {
428
461
/*
429
462
* Powercap domains are validate by the protocol layer, i.e.
@@ -438,6 +471,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
438
471
INIT_LIST_HEAD (& spz -> node );
439
472
INIT_LIST_HEAD (& pr -> registered_zones [i ]);
440
473
474
+ list_add_tail (& spz -> node , & pr -> scmi_zones );
441
475
/*
442
476
* Forcibly skip powercap domains using an abstract scale.
443
477
* Note that only leaves domains can be skipped, so this could
@@ -448,7 +482,7 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
448
482
dev_warn (dev ,
449
483
"Abstract power scale not supported. Skip %s.\n" ,
450
484
spz -> info -> name );
451
- spz -> info = NULL ;
485
+ spz -> invalid = true ;
452
486
continue ;
453
487
}
454
488
}
@@ -457,21 +491,12 @@ static int scmi_powercap_probe(struct scmi_device *sdev)
457
491
* Scan array of retrieved SCMI powercap domains and register them
458
492
* recursively starting from the root domains.
459
493
*/
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 ;
470
497
471
498
dev_set_drvdata (dev , pr );
472
499
473
- dev_info (dev , "Registered %d SCMI Powercap domains !\n" , pr -> num_zones );
474
-
475
500
return ret ;
476
501
}
477
502
0 commit comments