From ed2ccda4fffbe05205b19bea0ac0840b721a1f0a Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Fri, 10 Feb 2017 11:07:27 +0100 Subject: pmdomain: Add PM domains test driver with perf support Signed-off-by: Ulf Hansson --- drivers/pmdomain/Makefile | 1 + drivers/pmdomain/domain_test.c | 216 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 drivers/pmdomain/domain_test.c diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile index 81ef64d277e1..e9542de521f8 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 += rpm_test.o +obj-y += domain_test.o diff --git a/drivers/pmdomain/domain_test.c b/drivers/pmdomain/domain_test.c new file mode 100644 index 000000000000..749fe656fa9c --- /dev/null +++ b/drivers/pmdomain/domain_test.c @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Linaro Ltd. + * + * Author: Ulf Hansson + * + * Implements PM domains test using the generic PM domain. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +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_domains_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_domains_test_remove(struct platform_device *pdev) +{ + dev_info(&pdev->dev, "%s\n", __func__); + pd_remove(pdev->dev.of_node); + return 0; +} + +static void pm_domains_test_sync_state(struct device *dev) +{ + dev_info(dev, "%s!!!\n", __func__); +} + +static const struct of_device_id pm_domains_test_ids[] = { + { .compatible = "test,pm-domains-test", }, + {}, +}; +MODULE_DEVICE_TABLE(of, pm_domains_test_ids); + +static struct platform_driver pm_domains_test_driver = { + .probe = pm_domains_test_probe, + .remove = pm_domains_test_remove, + .driver = { + .name = "pm-domains-test-drv", + .of_match_table = pm_domains_test_ids, + .sync_state = pm_domains_test_sync_state, + }, +}; + +module_platform_driver(pm_domains_test_driver); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3