Skip to content

Commit 3759ec6

Browse files
committed
powercap/drivers/dtpm: Add hierarchy creation
The DTPM framework is available but without a way to configure it. This change provides a way to create a hierarchy of DTPM node where the power consumption reflects the sum of the children's power consumption. It is up to the platform to specify an array of dtpm nodes where each element has a pointer to its parent, except the top most one. The type of the node gives the indication of which initialization callback to call. At this time, we can create a virtual node, where its purpose is to be a parent in the hierarchy, and a DT node where the name describes its path. In order to ensure a nice self-encapsulation, the DTPM subsys array contains a couple of initialization functions, one to setup the DTPM backend and one to initialize it up. With this approach, the DTPM framework has a very few material to export. Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org> Link: https://lore.kernel.org/r/20220128163537.212248-3-daniel.lezcano@linaro.org
1 parent b9794a8 commit 3759ec6

3 files changed

Lines changed: 203 additions & 3 deletions

File tree

drivers/powercap/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ config IDLE_INJECT
4646

4747
config DTPM
4848
bool "Power capping for Dynamic Thermal Power Management (EXPERIMENTAL)"
49+
depends on OF
4950
help
5051
This enables support for the power capping for the dynamic
5152
thermal power management userspace engine.

drivers/powercap/dtpm.c

Lines changed: 187 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/powercap.h>
2424
#include <linux/slab.h>
2525
#include <linux/mutex.h>
26+
#include <linux/of.h>
2627

2728
#include "dtpm_subsys.h"
2829

@@ -463,14 +464,197 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)
463464
return 0;
464465
}
465466

466-
static int __init init_dtpm(void)
467+
static struct dtpm *dtpm_setup_virtual(const struct dtpm_node *hierarchy,
468+
struct dtpm *parent)
467469
{
470+
struct dtpm *dtpm;
471+
int ret;
472+
473+
dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL);
474+
if (!dtpm)
475+
return ERR_PTR(-ENOMEM);
476+
dtpm_init(dtpm, NULL);
477+
478+
ret = dtpm_register(hierarchy->name, dtpm, parent);
479+
if (ret) {
480+
pr_err("Failed to register dtpm node '%s': %d\n",
481+
hierarchy->name, ret);
482+
kfree(dtpm);
483+
return ERR_PTR(ret);
484+
}
485+
486+
return dtpm;
487+
}
488+
489+
static struct dtpm *dtpm_setup_dt(const struct dtpm_node *hierarchy,
490+
struct dtpm *parent)
491+
{
492+
struct device_node *np;
493+
int i, ret;
494+
495+
np = of_find_node_by_path(hierarchy->name);
496+
if (!np) {
497+
pr_err("Failed to find '%s'\n", hierarchy->name);
498+
return ERR_PTR(-ENXIO);
499+
}
500+
501+
for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) {
502+
503+
if (!dtpm_subsys[i]->setup)
504+
continue;
505+
506+
ret = dtpm_subsys[i]->setup(parent, np);
507+
if (ret) {
508+
pr_err("Failed to setup '%s': %d\n", dtpm_subsys[i]->name, ret);
509+
of_node_put(np);
510+
return ERR_PTR(ret);
511+
}
512+
}
513+
514+
of_node_put(np);
515+
516+
/*
517+
* By returning a NULL pointer, we let know the caller there
518+
* is no child for us as we are a leaf of the tree
519+
*/
520+
return NULL;
521+
}
522+
523+
typedef struct dtpm * (*dtpm_node_callback_t)(const struct dtpm_node *, struct dtpm *);
524+
525+
dtpm_node_callback_t dtpm_node_callback[] = {
526+
[DTPM_NODE_VIRTUAL] = dtpm_setup_virtual,
527+
[DTPM_NODE_DT] = dtpm_setup_dt,
528+
};
529+
530+
static int dtpm_for_each_child(const struct dtpm_node *hierarchy,
531+
const struct dtpm_node *it, struct dtpm *parent)
532+
{
533+
struct dtpm *dtpm;
534+
int i, ret;
535+
536+
for (i = 0; hierarchy[i].name; i++) {
537+
538+
if (hierarchy[i].parent != it)
539+
continue;
540+
541+
dtpm = dtpm_node_callback[hierarchy[i].type](&hierarchy[i], parent);
542+
543+
/*
544+
* A NULL pointer means there is no children, hence we
545+
* continue without going deeper in the recursivity.
546+
*/
547+
if (!dtpm)
548+
continue;
549+
550+
/*
551+
* There are multiple reasons why the callback could
552+
* fail. The generic glue is abstracting the backend
553+
* and therefore it is not possible to report back or
554+
* take a decision based on the error. In any case,
555+
* if this call fails, it is not critical in the
556+
* hierarchy creation, we can assume the underlying
557+
* service is not found, so we continue without this
558+
* branch in the tree but with a warning to log the
559+
* information the node was not created.
560+
*/
561+
if (IS_ERR(dtpm)) {
562+
pr_warn("Failed to create '%s' in the hierarchy\n",
563+
hierarchy[i].name);
564+
continue;
565+
}
566+
567+
ret = dtpm_for_each_child(hierarchy, &hierarchy[i], dtpm);
568+
if (ret)
569+
return ret;
570+
}
571+
572+
return 0;
573+
}
574+
575+
/**
576+
* dtpm_create_hierarchy - Create the dtpm hierarchy
577+
* @hierarchy: An array of struct dtpm_node describing the hierarchy
578+
*
579+
* The function is called by the platform specific code with the
580+
* description of the different node in the hierarchy. It creates the
581+
* tree in the sysfs filesystem under the powercap dtpm entry.
582+
*
583+
* The expected tree has the format:
584+
*
585+
* struct dtpm_node hierarchy[] = {
586+
* [0] { .name = "topmost", type = DTPM_NODE_VIRTUAL },
587+
* [1] { .name = "package", .type = DTPM_NODE_VIRTUAL, .parent = &hierarchy[0] },
588+
* [2] { .name = "/cpus/cpu0", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
589+
* [3] { .name = "/cpus/cpu1", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
590+
* [4] { .name = "/cpus/cpu2", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
591+
* [5] { .name = "/cpus/cpu3", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
592+
* [6] { }
593+
* };
594+
*
595+
* The last element is always an empty one and marks the end of the
596+
* array.
597+
*
598+
* Return: zero on success, a negative value in case of error. Errors
599+
* are reported back from the underlying functions.
600+
*/
601+
int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table)
602+
{
603+
const struct of_device_id *match;
604+
const struct dtpm_node *hierarchy;
605+
struct device_node *np;
606+
int i, ret;
607+
608+
if (pct)
609+
return -EBUSY;
610+
468611
pct = powercap_register_control_type(NULL, "dtpm", NULL);
469612
if (IS_ERR(pct)) {
470613
pr_err("Failed to register control type\n");
471-
return PTR_ERR(pct);
614+
ret = PTR_ERR(pct);
615+
goto out_pct;
616+
}
617+
618+
ret = -ENODEV;
619+
np = of_find_node_by_path("/");
620+
if (!np)
621+
goto out_err;
622+
623+
match = of_match_node(dtpm_match_table, np);
624+
625+
of_node_put(np);
626+
627+
if (!match)
628+
goto out_err;
629+
630+
hierarchy = match->data;
631+
if (!hierarchy) {
632+
ret = -EFAULT;
633+
goto out_err;
634+
}
635+
636+
ret = dtpm_for_each_child(hierarchy, NULL, NULL);
637+
if (ret)
638+
goto out_err;
639+
640+
for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) {
641+
642+
if (!dtpm_subsys[i]->init)
643+
continue;
644+
645+
ret = dtpm_subsys[i]->init();
646+
if (ret)
647+
pr_info("Failed to initialze '%s': %d",
648+
dtpm_subsys[i]->name, ret);
472649
}
473650

474651
return 0;
652+
653+
out_err:
654+
powercap_unregister_control_type(pct);
655+
out_pct:
656+
pct = NULL;
657+
658+
return ret;
475659
}
476-
late_initcall(init_dtpm);
660+
EXPORT_SYMBOL_GPL(dtpm_create_hierarchy);

include/linux/dtpm.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,23 @@ struct dtpm_ops {
3232
void (*release)(struct dtpm *);
3333
};
3434

35+
struct device_node;
36+
3537
struct dtpm_subsys_ops {
3638
const char *name;
3739
int (*init)(void);
40+
int (*setup)(struct dtpm *, struct device_node *);
41+
};
42+
43+
enum DTPM_NODE_TYPE {
44+
DTPM_NODE_VIRTUAL = 0,
45+
DTPM_NODE_DT,
46+
};
47+
48+
struct dtpm_node {
49+
enum DTPM_NODE_TYPE type;
50+
const char *name;
51+
struct dtpm_node *parent;
3852
};
3953

4054
static inline struct dtpm *to_dtpm(struct powercap_zone *zone)
@@ -52,4 +66,5 @@ void dtpm_unregister(struct dtpm *dtpm);
5266

5367
int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent);
5468

69+
int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table);
5570
#endif

0 commit comments

Comments
 (0)