aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorUlf Hansson <ulf.hansson@linaro.org>2017-02-10 11:07:27 +0100
committerUlf Hansson <ulf.hansson@linaro.org>2024-02-08 00:25:50 +0100
commit45087e188ddd7279a3d292c8aa695afb311baab2 (patch)
tree7b69f2e4502530f3f93eb7bf7a027af300be3b2c
parent611c498d35a6aa2609fd967378c93e681e5cb9ef (diff)
pmdomain: Add PM domains test driver with perf supportdebug_pm
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
-rw-r--r--drivers/pmdomain/Makefile1
-rw-r--r--drivers/pmdomain/pm_domain_test.c216
2 files changed, 217 insertions, 0 deletions
diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile
index e69db8ccf2a3..335d2ac156c9 100644
--- a/drivers/pmdomain/Makefile
+++ b/drivers/pmdomain/Makefile
@@ -18,3 +18,4 @@ obj-y += ti/
obj-y += xilinx/
obj-y += core.o governor.o
obj-y += pm_test.o
+obj-y += pm_domain_test.o
diff --git a/drivers/pmdomain/pm_domain_test.c b/drivers/pmdomain/pm_domain_test.c
new file mode 100644
index 000000000000..9b2385216b0a
--- /dev/null
+++ b/drivers/pmdomain/pm_domain_test.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019 Linaro Ltd.
+ *
+ * Author: Ulf Hansson <ulf.hansson@linaro.org>
+ *
+ * Implements PM domain test using the generic PM domain.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/pm_domain.h>
+
+static int pd_power_off(struct generic_pm_domain *domain)
+{
+ pr_err("%s: POWER OFF\n", domain->name);
+ return 0;
+}
+
+static int pd_power_on(struct generic_pm_domain *domain)
+{
+ pr_err("%s: POWER ON\n", domain->name);
+ return 0;
+}
+
+static int pd_set_performance_state(struct generic_pm_domain *domain, unsigned int state)
+{
+ pr_err("%s: NEW PERF STATE %u\n", domain->name, state);
+ return 0;
+}
+
+static int pd_remove(struct device_node *np)
+{
+ struct device_node *node;
+ struct generic_pm_domain *pd;
+
+ /* TBD: Respect topology by removing subdomains first */
+
+ for_each_child_of_node(np, node) {
+ if (!of_find_property(node, "#power-domain-cells", NULL))
+ continue;
+
+ of_genpd_del_provider(node);
+ pd = of_genpd_remove_last(node);
+ if (!IS_ERR(pd))
+ kfree(pd->name);
+ else
+ pr_err("%s: FAILED to remove: %pOF\n", __func__, node);
+
+ of_node_put(node);
+ }
+
+ return 0;
+}
+
+static int pd_init_topology(struct device_node *np)
+{
+ struct device_node *node;
+ struct of_phandle_args child, parent;
+ int ret;
+
+ for_each_child_of_node(np, node) {
+ if (of_parse_phandle_with_args(node, "power-domains",
+ "#power-domain-cells", 0, &parent))
+ continue;
+
+ child.np = node;
+ child.args_count = 0;
+
+ ret = of_genpd_add_subdomain(&parent, &child);
+ of_node_put(parent.np);
+ if (ret) {
+ of_node_put(node);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static void pd_free_states(struct genpd_power_state *states,
+ unsigned int state_count)
+{
+ kfree(states);
+}
+
+static int pd_init(struct device *dev, struct device_node *np)
+{
+ struct generic_pm_domain *pd;
+ struct dev_power_governor *pd_gov = NULL;
+ struct genpd_power_state *states = NULL;
+ int state_count = 0, ret;
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+
+ ret = of_genpd_parse_idle_states(np, &states, &state_count);
+ if (ret)
+ dev_err(dev, "%s failed parsing idle states err=%d\n",
+ __func__, ret);
+ if (states) {
+ pd->free_states = pd_free_states;
+ pd->states = states;
+ pd->state_count = state_count;
+ pd_gov = &simple_qos_governor;
+ }
+
+ pd->name = kasprintf(GFP_KERNEL, "%pOF", np);
+ pd->name = kbasename(pd->name);
+ pd->power_off = pd_power_off;
+ pd->power_on = pd_power_on;
+
+ if (strcmp(dev_name(dev), "domain_perf") == 0) {
+ dev_info(dev, "Perf domain set for domain\n");
+ pd->set_performance_state = pd_set_performance_state;
+ }
+
+ if (strcmp(dev_name(dev), "pm_domain_test0") == 0) {
+ dev_info(dev, "Set GENPD_FLAG_IRQ_SAFE for domain\n");
+ pd->flags = GENPD_FLAG_IRQ_SAFE;
+ }
+
+ ret = pm_genpd_init(pd, pd_gov, true);
+ if (ret) {
+ kfree(pd->states);
+ return ret;
+ }
+
+ ret = of_genpd_add_provider_simple(np, pd);
+ if (ret) {
+ kfree(pd->name);
+ pm_genpd_remove(pd);
+ }
+
+ return ret;
+}
+
+static int pm_domain_test_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *node;
+ int ret, pd_count = 0;
+
+ dev_info(&pdev->dev, "%s\n", __func__);
+
+ /*
+ * Parse child nodes for the "#power-domain-cells" property and
+ * initialize a genpd/genpd-of-provider pair when it's found.
+ */
+ for_each_child_of_node(np, node) {
+ if (!of_find_property(node, "#power-domain-cells", NULL))
+ continue;
+
+ ret = pd_init(&pdev->dev, node);
+ if (ret)
+ goto put_node;
+
+ pd_count++;
+ }
+
+ if (!pd_count)
+ return 0;
+
+ /* Create masters/subdomains based on the topology in DT. */
+ ret = pd_init_topology(np);
+ if (ret)
+ goto remove_pd;
+
+ dev_info(&pdev->dev, "%s: %d PM domains initialized\n", __func__,
+ pd_count);
+ return 0;
+
+put_node:
+ of_node_put(node);
+remove_pd:
+ if (pd_count)
+ pd_remove(np);
+ return ret;
+}
+
+static int pm_domain_test_remove(struct platform_device *pdev)
+{
+ dev_info(&pdev->dev, "%s\n", __func__);
+ pd_remove(pdev->dev.of_node);
+ return 0;
+}
+
+static void pm_domain_test_sync_state(struct device *dev)
+{
+ dev_info(dev, "%s!!!\n", __func__);
+}
+
+static const struct of_device_id pm_domain_test_ids[] = {
+ { .compatible = "test,pm-domain-test", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, pm_domain_test_ids);
+
+static struct platform_driver pm_domain_test_driver = {
+ .probe = pm_domain_test_probe,
+ .remove = pm_domain_test_remove,
+ .driver = {
+ .name = "pm-domain-test-drv",
+ .of_match_table = pm_domain_test_ids,
+ .sync_state = pm_domain_test_sync_state,
+ },
+};
+
+module_platform_driver(pm_domain_test_driver);
+MODULE_LICENSE("GPL v2");