linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Daniel Lezcano <daniel.lezcano@linaro.org>
To: daniel.lezcano@linaro.org, rjw@rjwysocki.net
Cc: lukasz.luba@arm.com, robh@kernel.org, heiko@sntech.de,
	arnd@linaro.org, linux-kernel@vger.kernel.org,
	linux-pm@vger.kernel.org, ulf.hansson@linaro.org,
	"Rafael J. Wysocki" <rafael@kernel.org>,
	Daniel Lezcano <daniel.lezcano@kernel.org>
Subject: [PATCH v5 2/6] powercap/drivers/dtpm: Add hierarchy creation
Date: Sat, 18 Dec 2021 14:00:10 +0100	[thread overview]
Message-ID: <20211218130014.4037640-3-daniel.lezcano@linaro.org> (raw)
In-Reply-To: <20211218130014.4037640-1-daniel.lezcano@linaro.org>

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 table
descriptors 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>
---
 drivers/powercap/Kconfig    |   1 +
 drivers/powercap/dtpm.c     | 155 ++++++++++++++++++++++++++++++++++--
 drivers/powercap/dtpm_cpu.c |   2 +-
 include/linux/dtpm.h        |  21 ++++-
 4 files changed, 171 insertions(+), 8 deletions(-)

diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig
index 8242e8c5ed77..b1ca339957e3 100644
--- a/drivers/powercap/Kconfig
+++ b/drivers/powercap/Kconfig
@@ -46,6 +46,7 @@ config IDLE_INJECT
 
 config DTPM
 	bool "Power capping for Dynamic Thermal Power Management (EXPERIMENTAL)"
+	depends on OF
 	help
 	  This enables support for the power capping for the dynamic
 	  thermal power management userspace engine.
diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c
index 0fe70687c198..1611c86de5f5 100644
--- a/drivers/powercap/dtpm.c
+++ b/drivers/powercap/dtpm.c
@@ -23,6 +23,7 @@
 #include <linux/powercap.h>
 #include <linux/slab.h>
 #include <linux/mutex.h>
+#include <linux/of.h>
 
 #define DTPM_POWER_LIMIT_FLAG 0
 
@@ -461,19 +462,163 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent)
 	return 0;
 }
 
-static int __init init_dtpm(void)
+static struct dtpm *dtpm_setup_virtual(const struct dtpm_node *hierarchy,
+				       struct dtpm *parent)
+{
+	struct dtpm *dtpm;
+	int ret;
+
+	dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL);
+	if (!dtpm)
+		return ERR_PTR(-ENOMEM);
+	dtpm_init(dtpm, NULL);
+
+	ret = dtpm_register(hierarchy->name, dtpm, parent);
+	if (ret) {
+		pr_err("Failed to register dtpm node '%s': %d\n",
+		       hierarchy->name, ret);
+		kfree(dtpm);
+		return ERR_PTR(ret);
+	}
+
+	return dtpm;
+}
+
+static struct dtpm *dtpm_setup_dt(const struct dtpm_node *hierarchy,
+				  struct dtpm *parent)
+{
+	struct dtpm_descr *dtpm_descr;
+	struct device_node *np;
+	int ret;
+
+	np = of_find_node_by_path(hierarchy->name);
+	if (!np) {
+		pr_err("Failed to find '%s'\n", hierarchy->name);
+		return ERR_PTR(-ENXIO);
+	}
+
+	for_each_dtpm_table(dtpm_descr) {
+
+		ret = dtpm_descr->setup(parent, np);
+		if (ret) {
+			pr_err("Failed to setup '%s': %d\n", hierarchy->name, ret);
+			of_node_put(np);
+			return ERR_PTR(ret);
+		}
+
+		of_node_put(np);
+	}
+
+	/*
+	 * By returning a NULL pointer, we let know the caller there
+	 * is no child for us as we are a leaf of the tree
+	 */
+	return NULL;
+}
+
+typedef struct dtpm * (*dtpm_node_callback_t)(const struct dtpm_node *, struct dtpm *);
+
+dtpm_node_callback_t dtpm_node_callback[] = {
+	[DTPM_NODE_VIRTUAL] = dtpm_setup_virtual,
+	[DTPM_NODE_DT] = dtpm_setup_dt,
+};
+
+static int dtpm_for_each_child(const struct dtpm_node *hierarchy,
+			       const struct dtpm_node *it, struct dtpm *parent)
+{
+	struct dtpm *dtpm;
+	int i, ret;
+
+	for (i = 0; hierarchy[i].name; i++) {
+
+		if (hierarchy[i].parent != it)
+			continue;
+
+		dtpm = dtpm_node_callback[hierarchy[i].type](&hierarchy[i], parent);
+		if (!dtpm || IS_ERR(dtpm))
+			continue;
+
+		ret = dtpm_for_each_child(hierarchy, &hierarchy[i], dtpm);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+/**
+ * dtpm_create_hierarchy - Create the dtpm hierarchy
+ * @hierarchy: An array of struct dtpm_node describing the hierarchy
+ *
+ * The function is called by the platform specific code with the
+ * description of the different node in the hierarchy. It creates the
+ * tree in the sysfs filesystem under the powercap dtpm entry.
+ *
+ * The expected tree has the format:
+ *
+ * struct dtpm_node hierarchy[] = {
+ *	[0] { .name = "topmost" },
+ *      [1] { .name = "package", .parent = &hierarchy[0] },
+ *      [2] { .name = "/cpus/cpu0", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
+ *      [3] { .name = "/cpus/cpu1", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
+ *      [4] { .name = "/cpus/cpu2", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
+ *      [5] { .name = "/cpus/cpu3", .type = DTPM_NODE_DT, .parent = &hierarchy[1] },
+ *	[6] { }
+ * };
+ *
+ * The last element is always an empty one and marks the end of the
+ * array.
+ *
+ * Return: zero on success, a negative value in case of error. Errors
+ * are reported back from the underlying functions.
+ */
+int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table)
 {
+	const struct of_device_id *match;
+	const struct dtpm_node *hierarchy;
 	struct dtpm_descr *dtpm_descr;
+	struct device_node *np;
+	int ret;
+
+	np = of_find_node_by_path("/");
+	if (!np)
+		return -ENODEV;
+
+	match = of_match_node(dtpm_match_table, np);
 
+	of_node_put(np);
+
+	if (!match)
+		return -ENODEV;
+
+	hierarchy = match->data;
+	if (!hierarchy)
+		return -EFAULT;
+
+	ret = dtpm_for_each_child(hierarchy, NULL, NULL);
+	if (ret)
+		return ret;
+	
+	for_each_dtpm_table(dtpm_descr) {
+
+		if (!dtpm_descr->init)
+			continue;
+
+		dtpm_descr->init();
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(dtpm_create_hierarchy);
+
+static int __init init_dtpm(void)
+{
 	pct = powercap_register_control_type(NULL, "dtpm", NULL);
 	if (IS_ERR(pct)) {
 		pr_err("Failed to register control type\n");
 		return PTR_ERR(pct);
 	}
 
-	for_each_dtpm_table(dtpm_descr)
-		dtpm_descr->init();
-
 	return 0;
 }
-late_initcall(init_dtpm);
+fs_initcall_sync(init_dtpm);
diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c
index b740866b228d..6bffb44c75aa 100644
--- a/drivers/powercap/dtpm_cpu.c
+++ b/drivers/powercap/dtpm_cpu.c
@@ -269,4 +269,4 @@ static int __init dtpm_cpu_init(void)
 	return 0;
 }
 
-DTPM_DECLARE(dtpm_cpu, dtpm_cpu_init);
+DTPM_DECLARE(dtpm_cpu, dtpm_cpu_init, NULL);
diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h
index d37e5d06a357..5a6b31eaf7e4 100644
--- a/include/linux/dtpm.h
+++ b/include/linux/dtpm.h
@@ -32,23 +32,39 @@ struct dtpm_ops {
 	void (*release)(struct dtpm *);
 };
 
+struct device_node;
+
 typedef int (*dtpm_init_t)(void);
+typedef int (*dtpm_setup_t)(struct dtpm *, struct device_node *);
 
 struct dtpm_descr {
 	dtpm_init_t init;
+	dtpm_setup_t setup;
+};
+
+enum DTPM_NODE_TYPE {
+	DTPM_NODE_VIRTUAL = 0,
+	DTPM_NODE_DT,
+};
+
+struct dtpm_node {
+	enum DTPM_NODE_TYPE type;
+	const char *name;
+	struct dtpm_node *parent;
 };
 
 /* Init section thermal table */
 extern struct dtpm_descr __dtpm_table[];
 extern struct dtpm_descr __dtpm_table_end[];
 
-#define DTPM_TABLE_ENTRY(name, __init)				\
+#define DTPM_TABLE_ENTRY(name, __init, __setup)			\
 	static struct dtpm_descr __dtpm_table_entry_##name	\
 	__used __section("__dtpm_table") = {			\
 		.init = __init,					\
+		.setup = __setup,				\
 	}
 
-#define DTPM_DECLARE(name, init)	DTPM_TABLE_ENTRY(name, init)
+#define DTPM_DECLARE(name, init, setup)	DTPM_TABLE_ENTRY(name, init, setup)
 
 #define for_each_dtpm_table(__dtpm)	\
 	for (__dtpm = __dtpm_table;	\
@@ -70,4 +86,5 @@ void dtpm_unregister(struct dtpm *dtpm);
 
 int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent);
 
+int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table);
 #endif
-- 
2.25.1


  parent reply	other threads:[~2021-12-18 13:00 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-18 13:00 [PATCH v5 0/6] powercap/drivers/dtpm: Create the dtpm hierarchy Daniel Lezcano
2021-12-18 13:00 ` [PATCH v5 1/6] powercap/drivers/dtpm: Move dtpm table from init to data section Daniel Lezcano
2021-12-31 13:33   ` Ulf Hansson
2022-01-04  8:57     ` Daniel Lezcano
2022-01-07 13:15     ` Daniel Lezcano
2022-01-07 14:49       ` Ulf Hansson
2022-01-10 13:33         ` Daniel Lezcano
2021-12-18 13:00 ` Daniel Lezcano [this message]
2021-12-31 13:45   ` [PATCH v5 2/6] powercap/drivers/dtpm: Add hierarchy creation Ulf Hansson
2022-01-05 16:00     ` Daniel Lezcano
2022-01-07 15:54       ` Ulf Hansson
2022-01-10 15:55         ` Daniel Lezcano
2022-01-11  8:28           ` Ulf Hansson
2022-01-11 17:52             ` Daniel Lezcano
2022-01-12 12:00               ` Ulf Hansson
2022-01-14 19:15                 ` Daniel Lezcano
2021-12-18 13:00 ` [PATCH v5 3/6] powercap/drivers/dtpm: Add CPU DT initialization support Daniel Lezcano
2021-12-31 13:46   ` Ulf Hansson
2021-12-18 13:00 ` [PATCH v5 4/6] powercap/drivers/dtpm: Add dtpm devfreq with energy model support Daniel Lezcano
2021-12-18 13:00 ` [PATCH v5 5/6] rockchip/soc/drivers: Add DTPM description for rk3399 Daniel Lezcano
2021-12-31 13:57   ` Ulf Hansson
2022-01-04  9:29     ` Geert Uytterhoeven
2022-01-05  9:21       ` Daniel Lezcano
2022-01-05 11:25     ` Daniel Lezcano
2021-12-18 13:00 ` [PATCH v5 6/6] qcom/soc/drivers: Add DTPM description for sdm845 Daniel Lezcano
2021-12-18 19:47   ` Steev Klimaszewski
2021-12-18 20:11     ` Daniel Lezcano
2021-12-19 18:44       ` Steev Klimaszewski
2021-12-19 20:27         ` Daniel Lezcano
2022-01-07 19:27   ` Bjorn Andersson
2022-01-07 22:07     ` Daniel Lezcano
2022-01-07 23:51       ` Bjorn Andersson
2021-12-23 13:20 ` [PATCH v5 0/6] powercap/drivers/dtpm: Create the dtpm hierarchy Daniel Lezcano
2021-12-23 13:32   ` Ulf Hansson
2021-12-23 13:42     ` Daniel Lezcano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211218130014.4037640-3-daniel.lezcano@linaro.org \
    --to=daniel.lezcano@linaro.org \
    --cc=arnd@linaro.org \
    --cc=daniel.lezcano@kernel.org \
    --cc=heiko@sntech.de \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pm@vger.kernel.org \
    --cc=lukasz.luba@arm.com \
    --cc=rafael@kernel.org \
    --cc=rjw@rjwysocki.net \
    --cc=robh@kernel.org \
    --cc=ulf.hansson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).