diff options
author | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-04-07 21:11:28 +0200 |
---|---|---|
committer | Daniel Lezcano <daniel.lezcano@linaro.org> | 2021-06-29 00:06:38 +0200 |
commit | 7525abb234695d07a0094b2f511d5fe8bea0a979 (patch) | |
tree | 43b369c43dcf794311b6c3369d4711223bff5db0 | |
parent | a0d4daeccca8ee82b36fd7c18c7570065013ec5a (diff) | |
download | linux-7525abb234695d07a0094b2f511d5fe8bea0a979.tar.gz |
powercap: DT parsing
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r-- | drivers/powercap/Makefile | 2 | ||||
-rw-r--r-- | drivers/powercap/dtpm.c | 5 | ||||
-rw-r--r-- | drivers/powercap/dtpm_of.c | 177 | ||||
-rw-r--r-- | include/linux/dtpm.h | 14 |
4 files changed, 195 insertions, 3 deletions
diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index fabcf388a8d3..616aa10c929f 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_DTPM) += dtpm.o +obj-$(CONFIG_DTPM) += dtpm.o dtpm_of.o obj-$(CONFIG_DTPM_CPU) += dtpm_cpu.o obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 4789c397d31a..61978fd9d3cb 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -465,7 +465,7 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) } EXPORT_SYMBOL_GPL(dtpm_register); -int __init dtpm_probe(void) +int dtpm_probe(void) { struct dtpm_descr *dtpm_descr; @@ -480,4 +480,5 @@ int __init dtpm_probe(void) return 0; } -late_initcall(dtpm_probe); +EXPORT_SYMBOL_GPL(dtpm_probe); + diff --git a/drivers/powercap/dtpm_of.c b/drivers/powercap/dtpm_of.c new file mode 100644 index 000000000000..b8e384cf30ca --- /dev/null +++ b/drivers/powercap/dtpm_of.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Linaro Limited + * + * Author: Daniel Lezcano <daniel.lezcano@linaro.org> + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include <linux/dtpm.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/slab.h> + +struct dtpm_node { + int method; + struct device_node *np; + struct dtpm *dtpm; + struct list_head node; +}; + +static LIST_HEAD(dtpm_list); + +static DEFINE_MUTEX(dtpm_lock); + +static struct dtpm *__dtpm_find(struct device_node *np, int method) +{ + struct dtpm_node *node; + + list_for_each_entry(node, &dtpm_list, node) { + if (np == node->np && method == node->method) + return node->dtpm; + } + + return NULL; +} + +struct dtpm *dtpm_of_find(struct device_node *np, int method) +{ + struct dtpm *dtpm; + + mutex_lock(&dtpm_lock); + dtpm = __dtpm_find(np, method); + mutex_unlock(&dtpm_lock); + + return dtpm; +} + +/** + * dtpm_of_add - Add the dtpm in the dtpm list + * @name: a name used as an identifier + * @dtpm: the dtpm node to be registered + * + * Stores the dtpm device in a list. The list contains all the devices + * which are power capable in terms of limitation and power + * consumption measurements. Even if conceptually, a power capable + * device won't register itself twice, the function will check if it + * was already registered in order to prevent a misuse of the API. + * + * Return: 0 on success, -EEXIST if the device name is already present + * in the list, -ENOMEM in case of memory allocation failure. + */ +int dtpm_of_add(struct device *dev, int method, struct dtpm *dtpm) +{ + struct dtpm_node *node; + int ret; + + WARN_ON(!dev); + + mutex_lock(&dtpm_lock); + + ret = -EEXIST; + if (__dtpm_find(dev_of_node(dev), method)) + goto out_unlock; + + ret = -ENOMEM; + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + goto out_unlock; + + node->np = dev_of_node(dev); + node->method = method; + node->dtpm = dtpm; + + list_add(&node->node, &dtpm_list); + + pr_debug("Registered dtpm device '%pOF'\n", node); + + ret = 0; +out_unlock: + mutex_unlock(&dtpm_lock); + + return ret; +} +EXPORT_SYMBOL_GPL(dtpm_of_add); + +/** + * dtpm_of_del - Remove the dtpm device from the list + * @name: the dtpm device name to be removed + * + * Remove the dtpm device from the list of the registered devices. + */ +void dtpm_of_del(struct device *dev, int method) +{ + struct dtpm_node *node; + + mutex_lock(&dtpm_lock); + + list_for_each_entry(node, &dtpm_list, node) { + + if (dev_of_node(dev) != node->np || + method != node->method) + continue; + + list_del(&node->node); + kfree(node); + + break; + } + + mutex_unlock(&dtpm_lock); +} +EXPORT_SYMBOL_GPL(dtpm_of_del); + +/** + * dtpm_of_for_each - Browse all powerzone properties + * @name: the property name to act on + * @parent: the parent to hook the new dtpm zones + * @cb: user specific callback in charge of the dtpm setup + * + * This function will find all the powerzone properties matching @name + * and will call the @cb with the corresponding device tree node. The + * callback is responsible of creating the dtpm node and attach it to + * the @parent. + * + * Return: 0 on success, -EINVAL, -ENODATA, -EILSEQ conforming to the + * of_property_read_string_index return code or any value non zero + * returned by the callback. + */ +int dtpm_of_for_each(const char *name, struct dtpm *parent, dtpm_of_cb cb) +{ + struct device_node *node; + int ret = 0; + + for_each_node_with_property(node, "powerzone") { + + const char *prop_name; + int i, nr_strings; + + nr_strings = of_property_count_strings(node, "powerzone"); + + for (i = 0; i < nr_strings; i++) { + + ret = of_property_read_string_index(node, "powerzone", + i, &prop_name); + if (ret) + goto out_err; + + pr_debug("Found powerzone property '%s'\n", prop_name); + + if (strcmp(name, prop_name)) + continue; + + ret = cb(node, name, parent); + if (ret) + goto out_err; + } + + of_node_put(node); + } + + return 0; + +out_err: + of_node_put(node); + return ret; +} +EXPORT_SYMBOL_GPL(dtpm_of_for_each); diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h index 50679c48adab..aa574b26778a 100644 --- a/include/linux/dtpm.h +++ b/include/linux/dtpm.h @@ -33,11 +33,17 @@ struct dtpm_ops { }; typedef int (*dtpm_init_t)(void); +typedef int (*dtpm_of_cb)(struct device_node *node, const char *name, + struct dtpm *parent); struct dtpm_descr { dtpm_init_t init; }; +enum { + DTPM_EM_DVFS, +}; + /* Init section thermal table */ extern struct dtpm_descr __dtpm_table[]; extern struct dtpm_descr __dtpm_table_end[]; @@ -74,4 +80,12 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent); int dtpm_register_cpu(struct dtpm *parent); +int dtpm_of_add(struct device *dev, int method, struct dtpm *dtpm); + +void dtpm_of_del(struct device *dev, int method); + +struct dtpm *dtpm_of_find(struct device_node *np, int method); + +int dtpm_of_for_each(const char *name, struct dtpm *parent, dtpm_of_cb cb); + #endif |