From 1c055e55a242a33e574e48be530e06770a210dcd Mon Sep 17 00:00:00 2001 From: hc <hc@nodka.com> Date: Mon, 19 Feb 2024 03:26:26 +0000 Subject: [PATCH] add r8169 read mac form eeprom --- kernel/drivers/opp/of.c | 946 ++++++++++++++++++++++++++++++++++++++++++++-------------- 1 files changed, 717 insertions(+), 229 deletions(-) diff --git a/kernel/drivers/opp/of.c b/kernel/drivers/opp/of.c index c672cdd..b163f5d 100644 --- a/kernel/drivers/opp/of.c +++ b/kernel/drivers/opp/of.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Generic OPP OF helpers * @@ -5,10 +6,6 @@ * Nishanth Menon * Romit Dasgupta * Kevin Hilman - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -20,14 +17,36 @@ #include <linux/pm_domain.h> #include <linux/slab.h> #include <linux/export.h> +#include <linux/energy_model.h> #include "opp.h" -static struct opp_table *_managed_opp(const struct device_node *np) +/* + * Returns opp descriptor node for a device node, caller must + * do of_node_put(). + */ +static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np, + int index) +{ + /* "operating-points-v2" can be an array for power domain providers */ + return of_parse_phandle(np, "operating-points-v2", index); +} + +/* Returns opp descriptor node for a device, caller must do of_node_put() */ +struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev) +{ + return _opp_of_get_opp_desc_node(dev->of_node, 0); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node); + +struct opp_table *_managed_opp(struct device *dev, int index) { struct opp_table *opp_table, *managed_table = NULL; + struct device_node *np; - mutex_lock(&opp_table_lock); + np = _opp_of_get_opp_desc_node(dev->of_node, index); + if (!np) + return NULL; list_for_each_entry(opp_table, &opp_tables, node) { if (opp_table->np == np) { @@ -47,37 +66,369 @@ } } - mutex_unlock(&opp_table_lock); + of_node_put(np); return managed_table; } -void _of_init_opp_table(struct opp_table *opp_table, struct device *dev) +/* The caller must call dev_pm_opp_put() after the OPP is used */ +static struct dev_pm_opp *_find_opp_of_np(struct opp_table *opp_table, + struct device_node *opp_np) { - struct device_node *np; + struct dev_pm_opp *opp; + + mutex_lock(&opp_table->lock); + + list_for_each_entry(opp, &opp_table->opp_list, node) { + if (opp->np == opp_np) { + dev_pm_opp_get(opp); + mutex_unlock(&opp_table->lock); + return opp; + } + } + + mutex_unlock(&opp_table->lock); + + return NULL; +} + +static struct device_node *of_parse_required_opp(struct device_node *np, + int index) +{ + return of_parse_phandle(np, "required-opps", index); +} + +/* The caller must call dev_pm_opp_put_opp_table() after the table is used */ +static struct opp_table *_find_table_of_opp_np(struct device_node *opp_np) +{ + struct opp_table *opp_table; + struct device_node *opp_table_np; + + lockdep_assert_held(&opp_table_lock); + + opp_table_np = of_get_parent(opp_np); + if (!opp_table_np) + goto err; + + /* It is safe to put the node now as all we need now is its address */ + of_node_put(opp_table_np); + + list_for_each_entry(opp_table, &opp_tables, node) { + if (opp_table_np == opp_table->np) { + _get_opp_table_kref(opp_table); + return opp_table; + } + } + +err: + return ERR_PTR(-ENODEV); +} + +/* Free resources previously acquired by _opp_table_alloc_required_tables() */ +static void _opp_table_free_required_tables(struct opp_table *opp_table) +{ + struct opp_table **required_opp_tables = opp_table->required_opp_tables; + int i; + + if (!required_opp_tables) + return; + + for (i = 0; i < opp_table->required_opp_count; i++) { + if (IS_ERR_OR_NULL(required_opp_tables[i])) + break; + + dev_pm_opp_put_opp_table(required_opp_tables[i]); + } + + kfree(required_opp_tables); + + opp_table->required_opp_count = 0; + opp_table->required_opp_tables = NULL; +} + +/* + * Populate all devices and opp tables which are part of "required-opps" list. + * Checking only the first OPP node should be enough. + */ +static void _opp_table_alloc_required_tables(struct opp_table *opp_table, + struct device *dev, + struct device_node *opp_np) +{ + struct opp_table **required_opp_tables; + struct device_node *required_np, *np; + int count, i; + + /* Traversing the first OPP node is all we need */ + np = of_get_next_available_child(opp_np, NULL); + if (!np) { + dev_err(dev, "Empty OPP table\n"); + return; + } + + count = of_count_phandle_with_args(np, "required-opps", NULL); + if (!count) + goto put_np; + + required_opp_tables = kcalloc(count, sizeof(*required_opp_tables), + GFP_KERNEL); + if (!required_opp_tables) + goto put_np; + + opp_table->required_opp_tables = required_opp_tables; + opp_table->required_opp_count = count; + + for (i = 0; i < count; i++) { + required_np = of_parse_required_opp(np, i); + if (!required_np) + goto free_required_tables; + + required_opp_tables[i] = _find_table_of_opp_np(required_np); + of_node_put(required_np); + + if (IS_ERR(required_opp_tables[i])) + goto free_required_tables; + + /* + * We only support genpd's OPPs in the "required-opps" for now, + * as we don't know how much about other cases. Error out if the + * required OPP doesn't belong to a genpd. + */ + if (!required_opp_tables[i]->is_genpd) { + dev_err(dev, "required-opp doesn't belong to genpd: %pOF\n", + required_np); + goto free_required_tables; + } + } + + goto put_np; + +free_required_tables: + _opp_table_free_required_tables(opp_table); +put_np: + of_node_put(np); +} + +void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, + int index) +{ + struct device_node *np, *opp_np; + u32 val; /* * Only required for backward compatibility with v1 bindings, but isn't * harmful for other cases. And so we do it unconditionally. */ np = of_node_get(dev->of_node); - if (np) { - u32 val; + if (!np) + return; - if (!of_property_read_u32(np, "clock-latency", &val)) - opp_table->clock_latency_ns_max = val; - of_property_read_u32(np, "voltage-tolerance", - &opp_table->voltage_tolerance_v1); - of_node_put(np); - } + if (!of_property_read_u32(np, "clock-latency", &val)) + opp_table->clock_latency_ns_max = val; + of_property_read_u32(np, "voltage-tolerance", + &opp_table->voltage_tolerance_v1); + + if (of_find_property(np, "#power-domain-cells", NULL)) + opp_table->is_genpd = true; + + /* Get OPP table node */ + opp_np = _opp_of_get_opp_desc_node(np, index); + of_node_put(np); + + if (!opp_np) + return; + + if (of_property_read_bool(opp_np, "opp-shared")) + opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED; + else + opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE; + + opp_table->np = opp_np; + + _opp_table_alloc_required_tables(opp_table, dev, opp_np); + of_node_put(opp_np); } + +void _of_clear_opp_table(struct opp_table *opp_table) +{ + _opp_table_free_required_tables(opp_table); +} + +/* + * Release all resources previously acquired with a call to + * _of_opp_alloc_required_opps(). + */ +void _of_opp_free_required_opps(struct opp_table *opp_table, + struct dev_pm_opp *opp) +{ + struct dev_pm_opp **required_opps = opp->required_opps; + int i; + + if (!required_opps) + return; + + for (i = 0; i < opp_table->required_opp_count; i++) { + if (!required_opps[i]) + break; + + /* Put the reference back */ + dev_pm_opp_put(required_opps[i]); + } + + kfree(required_opps); + opp->required_opps = NULL; +} + +/* Populate all required OPPs which are part of "required-opps" list */ +static int _of_opp_alloc_required_opps(struct opp_table *opp_table, + struct dev_pm_opp *opp) +{ + struct dev_pm_opp **required_opps; + struct opp_table *required_table; + struct device_node *np; + int i, ret, count = opp_table->required_opp_count; + + if (!count) + return 0; + + required_opps = kcalloc(count, sizeof(*required_opps), GFP_KERNEL); + if (!required_opps) + return -ENOMEM; + + opp->required_opps = required_opps; + + for (i = 0; i < count; i++) { + required_table = opp_table->required_opp_tables[i]; + + np = of_parse_required_opp(opp->np, i); + if (unlikely(!np)) { + ret = -ENODEV; + goto free_required_opps; + } + + required_opps[i] = _find_opp_of_np(required_table, np); + of_node_put(np); + + if (!required_opps[i]) { + pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", + __func__, opp->np, i); + ret = -ENODEV; + goto free_required_opps; + } + } + + return 0; + +free_required_opps: + _of_opp_free_required_opps(opp_table, opp); + + return ret; +} + +static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table) +{ + struct device_node *np, *opp_np; + struct property *prop; + + if (!opp_table) { + np = of_node_get(dev->of_node); + if (!np) + return -ENODEV; + + opp_np = _opp_of_get_opp_desc_node(np, 0); + of_node_put(np); + } else { + opp_np = of_node_get(opp_table->np); + } + + /* Lets not fail in case we are parsing opp-v1 bindings */ + if (!opp_np) + return 0; + + /* Checking only first OPP is sufficient */ + np = of_get_next_available_child(opp_np, NULL); + of_node_put(opp_np); + if (!np) { + dev_err(dev, "OPP table empty\n"); + return -EINVAL; + } + + prop = of_find_property(np, "opp-peak-kBps", NULL); + of_node_put(np); + + if (!prop || !prop->length) + return 0; + + return 1; +} + +int dev_pm_opp_of_find_icc_paths(struct device *dev, + struct opp_table *opp_table) +{ + struct device_node *np; + int ret, i, count, num_paths; + struct icc_path **paths; + + ret = _bandwidth_supported(dev, opp_table); + if (ret <= 0) + return ret; + + ret = 0; + + np = of_node_get(dev->of_node); + if (!np) + return 0; + + count = of_count_phandle_with_args(np, "interconnects", + "#interconnect-cells"); + of_node_put(np); + if (count < 0) + return 0; + + /* two phandles when #interconnect-cells = <1> */ + if (count % 2) { + dev_err(dev, "%s: Invalid interconnects values\n", __func__); + return -EINVAL; + } + + num_paths = count / 2; + paths = kcalloc(num_paths, sizeof(*paths), GFP_KERNEL); + if (!paths) + return -ENOMEM; + + for (i = 0; i < num_paths; i++) { + paths[i] = of_icc_get_by_index(dev, i); + if (IS_ERR(paths[i])) { + ret = PTR_ERR(paths[i]); + if (ret != -EPROBE_DEFER) { + dev_err(dev, "%s: Unable to get path%d: %d\n", + __func__, i, ret); + } + goto err; + } + } + + if (opp_table) { + opp_table->paths = paths; + opp_table->path_count = num_paths; + return 0; + } + +err: + while (i--) + icc_put(paths[i]); + + kfree(paths); + + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_find_icc_paths); static bool _opp_is_supported(struct device *dev, struct opp_table *opp_table, struct device_node *np) { - unsigned int count = opp_table->supported_hw_count; - u32 version; - int ret; + unsigned int levels = opp_table->supported_hw_count; + int count, versions, ret, i, j; + u32 val; if (!opp_table->supported_hw) { /* @@ -92,21 +443,40 @@ return true; } - while (count--) { - ret = of_property_read_u32_index(np, "opp-supported-hw", count, - &version); - if (ret) { - dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", - __func__, count, ret); - return false; - } - - /* Both of these are bitwise masks of the versions */ - if (!(version & opp_table->supported_hw[count])) - return false; + count = of_property_count_u32_elems(np, "opp-supported-hw"); + if (count <= 0 || count % levels) { + dev_err(dev, "%s: Invalid opp-supported-hw property (%d)\n", + __func__, count); + return false; } - return true; + versions = count / levels; + + /* All levels in at least one of the versions should match */ + for (i = 0; i < versions; i++) { + bool supported = true; + + for (j = 0; j < levels; j++) { + ret = of_property_read_u32_index(np, "opp-supported-hw", + i * levels + j, &val); + if (ret) { + dev_warn(dev, "%s: failed to read opp-supported-hw property at index %d: %d\n", + __func__, i * levels + j, ret); + return false; + } + + /* Check if the level is supported */ + if (!(val & opp_table->supported_hw[j])) { + supported = false; + break; + } + } + + if (supported) + return true; + } + + return false; } static int opp_parse_supplies(struct dev_pm_opp *opp, struct device *dev, @@ -257,25 +627,93 @@ */ void dev_pm_opp_of_remove_table(struct device *dev) { - _dev_pm_opp_find_and_remove_table(dev, false); + dev_pm_opp_remove_table(dev); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_remove_table); -/* Returns opp descriptor node for a device node, caller must - * do of_node_put() */ -static struct device_node *_opp_of_get_opp_desc_node(struct device_node *np, - int index) +static int _read_bw(struct dev_pm_opp *new_opp, struct opp_table *table, + struct device_node *np, bool peak) { - /* "operating-points-v2" can be an array for power domain providers */ - return of_parse_phandle(np, "operating-points-v2", index); + const char *name = peak ? "opp-peak-kBps" : "opp-avg-kBps"; + struct property *prop; + int i, count, ret; + u32 *bw; + + prop = of_find_property(np, name, NULL); + if (!prop) + return -ENODEV; + + count = prop->length / sizeof(u32); + if (table->path_count != count) { + pr_err("%s: Mismatch between %s and paths (%d %d)\n", + __func__, name, count, table->path_count); + return -EINVAL; + } + + bw = kmalloc_array(count, sizeof(*bw), GFP_KERNEL); + if (!bw) + return -ENOMEM; + + ret = of_property_read_u32_array(np, name, bw, count); + if (ret) { + pr_err("%s: Error parsing %s: %d\n", __func__, name, ret); + goto out; + } + + for (i = 0; i < count; i++) { + if (peak) + new_opp->bandwidth[i].peak = kBps_to_icc(bw[i]); + else + new_opp->bandwidth[i].avg = kBps_to_icc(bw[i]); + } + +out: + kfree(bw); + return ret; } -/* Returns opp descriptor node for a device, caller must do of_node_put() */ -struct device_node *dev_pm_opp_of_get_opp_desc_node(struct device *dev) +static int _read_opp_key(struct dev_pm_opp *new_opp, struct opp_table *table, + struct device_node *np, bool *rate_not_available) { - return _opp_of_get_opp_desc_node(dev->of_node, 0); + bool found = false; + u64 rate; + int ret; + + ret = of_property_read_u64(np, "opp-hz", &rate); + if (!ret) { + /* + * Rate is defined as an unsigned long in clk API, and so + * casting explicitly to its type. Must be fixed once rate is 64 + * bit guaranteed in clk API. + */ + new_opp->rate = (unsigned long)rate; + found = true; + } + *rate_not_available = !!ret; + + /* + * Bandwidth consists of peak and average (optional) values: + * opp-peak-kBps = <path1_value path2_value>; + * opp-avg-kBps = <path1_value path2_value>; + */ + ret = _read_bw(new_opp, table, np, true); + if (!ret) { + found = true; + ret = _read_bw(new_opp, table, np, false); + } + + /* The properties were found but we failed to parse them */ + if (ret && ret != -ENODEV) + return ret; + + if (!of_property_read_u32(np, "opp-level", &new_opp->level)) + found = true; + + if (found) + return 0; + + return ret; } -EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node); /** * _opp_add_static_v2() - Allocate static OPPs (As per 'v2' DT bindings) @@ -288,48 +726,41 @@ * removed by dev_pm_opp_remove. * * Return: - * 0 On success OR + * Valid OPP pointer: + * On success + * NULL: * Duplicate OPPs (both freq and volt are same) and opp->available - * -EEXIST Freq are same and volt are different OR + * OR if the OPP is not supported by hardware. + * ERR_PTR(-EEXIST): + * Freq are same and volt are different OR * Duplicate OPPs (both freq and volt are same) and !opp->available - * -ENOMEM Memory allocation failure - * -EINVAL Failed parsing the OPP node + * ERR_PTR(-ENOMEM): + * Memory allocation failure + * ERR_PTR(-EINVAL): + * Failed parsing the OPP node */ -static int _opp_add_static_v2(struct opp_table *opp_table, struct device *dev, - struct device_node *np) +static struct dev_pm_opp *_opp_add_static_v2(struct opp_table *opp_table, + struct device *dev, struct device_node *np) { struct dev_pm_opp *new_opp; - u64 rate = 0; u32 val; int ret; bool rate_not_available = false; new_opp = _opp_allocate(opp_table); if (!new_opp) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - ret = of_property_read_u64(np, "opp-hz", &rate); - if (ret < 0) { - /* "opp-hz" is optional for devices like power domains. */ - if (!of_find_property(dev->of_node, "#power-domain-cells", - NULL)) { - dev_err(dev, "%s: opp-hz not found\n", __func__); - goto free_opp; - } - - rate_not_available = true; - } else { - /* - * Rate is defined as an unsigned long in clk API, and so - * casting explicitly to its type. Must be fixed once rate is 64 - * bit guaranteed in clk API. - */ - new_opp->rate = (unsigned long)rate; + ret = _read_opp_key(new_opp, opp_table, np, &rate_not_available); + if (ret < 0 && !opp_table->is_genpd) { + dev_err(dev, "%s: opp key field not found\n", __func__); + goto free_opp; } /* Check if the OPP supports hardware's hierarchy of versions or not */ if (!_opp_is_supported(dev, opp_table, np)) { - dev_dbg(dev, "OPP not supported by hardware: %llu\n", rate); + dev_dbg(dev, "OPP not supported by hardware: %lu\n", + new_opp->rate); goto free_opp; } @@ -339,29 +770,37 @@ new_opp->dynamic = false; new_opp->available = true; + ret = _of_opp_alloc_required_opps(opp_table, new_opp); + if (ret) + goto free_opp; + if (!of_property_read_u32(np, "clock-latency-ns", &val)) new_opp->clock_latency_ns = val; - new_opp->pstate = of_genpd_opp_to_performance_state(dev, np); - ret = opp_parse_supplies(new_opp, dev, opp_table); if (ret) - goto free_opp; + goto free_required_opps; + + if (opp_table->is_genpd) + new_opp->pstate = pm_genpd_opp_to_performance_state(dev, new_opp); ret = _opp_add(dev, new_opp, opp_table, rate_not_available); if (ret) { /* Don't return error for duplicate OPPs */ if (ret == -EBUSY) ret = 0; - goto free_opp; + goto free_required_opps; } /* OPP to select on device suspend */ if (of_property_read_bool(np, "opp-suspend")) { if (opp_table->suspend_opp) { - dev_warn(dev, "%s: Multiple suspend OPPs found (%lu %lu)\n", - __func__, opp_table->suspend_opp->rate, - new_opp->rate); + /* Pick the OPP with higher rate as suspend OPP */ + if (new_opp->rate > opp_table->suspend_opp->rate) { + opp_table->suspend_opp->suspend = false; + new_opp->suspend = true; + opp_table->suspend_opp = new_opp; + } } else { new_opp->suspend = true; opp_table->suspend_opp = new_opp; @@ -381,45 +820,45 @@ * frequency/voltage list. */ blocking_notifier_call_chain(&opp_table->head, OPP_EVENT_ADD, new_opp); - return 0; + return new_opp; +free_required_opps: + _of_opp_free_required_opps(opp_table, new_opp); free_opp: _opp_free(new_opp); - return ret; + return ret ? ERR_PTR(ret) : NULL; } /* Initializes OPP tables based on new bindings */ -static int _of_add_opp_table_v2(struct device *dev, struct device_node *opp_np) +static int _of_add_opp_table_v2(struct device *dev, struct opp_table *opp_table) { struct device_node *np; - struct opp_table *opp_table; - int ret = 0, count = 0, pstate_count = 0; + int ret, count = 0; struct dev_pm_opp *opp; - opp_table = _managed_opp(opp_np); - if (opp_table) { - /* OPPs are already managed */ - if (!_add_opp_dev(dev, opp_table)) - ret = -ENOMEM; - goto put_opp_table; + /* OPP table is already initialized for the device */ + mutex_lock(&opp_table->lock); + if (opp_table->parsed_static_opps) { + opp_table->parsed_static_opps++; + mutex_unlock(&opp_table->lock); + return 0; } - opp_table = dev_pm_opp_get_opp_table(dev); - if (!opp_table) - return -ENOMEM; + opp_table->parsed_static_opps = 1; + mutex_unlock(&opp_table->lock); /* We have opp-table node now, iterate over it and add OPPs */ - for_each_available_child_of_node(opp_np, np) { - count++; - - ret = _opp_add_static_v2(opp_table, dev, np); - if (ret) { + for_each_available_child_of_node(opp_table->np, np) { + opp = _opp_add_static_v2(opp_table, dev, np); + if (IS_ERR(opp)) { + ret = PTR_ERR(opp); dev_err(dev, "%s: Failed to add OPP, %d\n", __func__, ret); - _dev_pm_opp_remove_table(opp_table, dev, false); of_node_put(np); - goto put_opp_table; + goto remove_static_opp; + } else if (opp) { + count++; } } @@ -427,49 +866,51 @@ if (!count) { dev_err(dev, "%s: no supported OPPs", __func__); ret = -ENOENT; - goto put_opp_table; + goto remove_static_opp; } - list_for_each_entry(opp, &opp_table->opp_list, node) - pstate_count += !!opp->pstate; - - /* Either all or none of the nodes shall have performance state set */ - if (pstate_count && pstate_count != count) { - dev_err(dev, "Not all nodes have performance state set (%d: %d)\n", - count, pstate_count); - ret = -ENOENT; - _dev_pm_opp_remove_table(opp_table, dev, false); - goto put_opp_table; + list_for_each_entry(opp, &opp_table->opp_list, node) { + /* Any non-zero performance state would enable the feature */ + if (opp->pstate) { + opp_table->genpd_performance_state = true; + break; + } } - if (pstate_count) - opp_table->genpd_performance_state = true; + return 0; - opp_table->np = opp_np; - if (of_property_read_bool(opp_np, "opp-shared")) - opp_table->shared_opp = OPP_TABLE_ACCESS_SHARED; - else - opp_table->shared_opp = OPP_TABLE_ACCESS_EXCLUSIVE; - -put_opp_table: - dev_pm_opp_put_opp_table(opp_table); +remove_static_opp: + _opp_remove_all_static(opp_table); return ret; } /* Initializes OPP tables based on old-deprecated bindings */ -static int _of_add_opp_table_v1(struct device *dev) +static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table) { - struct opp_table *opp_table; const struct property *prop; const __be32 *val; int nr, ret = 0; + mutex_lock(&opp_table->lock); + if (opp_table->parsed_static_opps) { + opp_table->parsed_static_opps++; + mutex_unlock(&opp_table->lock); + return 0; + } + + opp_table->parsed_static_opps = 1; + mutex_unlock(&opp_table->lock); + prop = of_find_property(dev->of_node, "operating-points", NULL); - if (!prop) - return -ENODEV; - if (!prop->value) - return -ENODATA; + if (!prop) { + ret = -ENODEV; + goto remove_static_opp; + } + if (!prop->value) { + ret = -ENODATA; + goto remove_static_opp; + } /* * Each OPP is a set of tuples consisting of frequency and @@ -478,12 +919,9 @@ nr = prop->length / sizeof(u32); if (nr % 2) { dev_err(dev, "%s: Invalid OPP table\n", __func__); - return -EINVAL; + ret = -EINVAL; + goto remove_static_opp; } - - opp_table = dev_pm_opp_get_opp_table(dev); - if (!opp_table) - return -ENOMEM; val = prop->value; while (nr) { @@ -494,13 +932,16 @@ if (ret) { dev_err(dev, "%s: Failed to add OPP %ld (%d)\n", __func__, freq, ret); - _dev_pm_opp_remove_table(opp_table, dev, false); - break; + goto remove_static_opp; } nr -= 2; } - dev_pm_opp_put_opp_table(opp_table); + return 0; + +remove_static_opp: + _opp_remove_all_static(opp_table); + return ret; } @@ -523,24 +964,24 @@ */ int dev_pm_opp_of_add_table(struct device *dev) { - struct device_node *opp_np; + struct opp_table *opp_table; int ret; - /* - * OPPs have two version of bindings now. The older one is deprecated, - * try for the new binding first. - */ - opp_np = dev_pm_opp_of_get_opp_desc_node(dev); - if (!opp_np) { - /* - * Try old-deprecated bindings for backward compatibility with - * older dtbs. - */ - return _of_add_opp_table_v1(dev); - } + opp_table = dev_pm_opp_get_opp_table_indexed(dev, 0); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); - ret = _of_add_opp_table_v2(dev, opp_np); - of_node_put(opp_np); + /* + * OPPs have two version of bindings now. Also try the old (v1) + * bindings for backward compatibility with older dtbs. + */ + if (opp_table->np) + ret = _of_add_opp_table_v2(dev, opp_table); + else + ret = _of_add_opp_table_v1(dev, opp_table); + + if (ret) + dev_pm_opp_put_opp_table(opp_table); return ret; } @@ -567,28 +1008,27 @@ */ int dev_pm_opp_of_add_table_indexed(struct device *dev, int index) { - struct device_node *opp_np; + struct opp_table *opp_table; int ret, count; -again: - opp_np = _opp_of_get_opp_desc_node(dev->of_node, index); - if (!opp_np) { + if (index) { /* * If only one phandle is present, then the same OPP table * applies for all index requests. */ count = of_count_phandle_with_args(dev->of_node, "operating-points-v2", NULL); - if (count == 1 && index) { + if (count == 1) index = 0; - goto again; - } - - return -ENODEV; } - ret = _of_add_opp_table_v2(dev, opp_np); - of_node_put(opp_np); + opp_table = dev_pm_opp_get_opp_table_indexed(dev, index); + if (IS_ERR(opp_table)) + return PTR_ERR(opp_table); + + ret = _of_add_opp_table_v2(dev, opp_table); + if (ret) + dev_pm_opp_put_opp_table(opp_table); return ret; } @@ -605,7 +1045,7 @@ */ void dev_pm_opp_of_cpumask_remove_table(const struct cpumask *cpumask) { - _dev_pm_opp_cpumask_remove_table(cpumask, true); + _dev_pm_opp_cpumask_remove_table(cpumask, -1); } EXPORT_SYMBOL_GPL(dev_pm_opp_of_cpumask_remove_table); @@ -618,16 +1058,18 @@ int dev_pm_opp_of_cpumask_add_table(const struct cpumask *cpumask) { struct device *cpu_dev; - int cpu, ret = 0; + int cpu, ret; - WARN_ON(cpumask_empty(cpumask)); + if (WARN_ON(cpumask_empty(cpumask))) + return -ENODEV; for_each_cpu(cpu, cpumask) { cpu_dev = get_cpu_device(cpu); if (!cpu_dev) { pr_err("%s: failed to get cpu%d device\n", __func__, cpu); - continue; + ret = -ENODEV; + goto remove_table; } ret = dev_pm_opp_of_add_table(cpu_dev); @@ -639,11 +1081,15 @@ pr_debug("%s: couldn't find opp table for cpu:%d, %d\n", __func__, cpu, ret); - /* Free all other OPPs */ - dev_pm_opp_of_cpumask_remove_table(cpumask); - break; + goto remove_table; } } + + return 0; + +remove_table: + /* Free all other OPPs */ + _dev_pm_opp_cpumask_remove_table(cpumask, cpu); return ret; } @@ -720,58 +1166,48 @@ EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_sharing_cpus); /** - * of_dev_pm_opp_find_required_opp() - Search for required OPP. - * @dev: The device whose OPP node is referenced by the 'np' DT node. + * of_get_required_opp_performance_state() - Search for required OPP and return its performance state. * @np: Node that contains the "required-opps" property. + * @index: Index of the phandle to parse. * - * Returns the OPP of the device 'dev', whose phandle is present in the "np" - * node. Although the "required-opps" property supports having multiple - * phandles, this helper routine only parses the very first phandle in the list. + * Returns the performance state of the OPP pointed out by the "required-opps" + * property at @index in @np. * - * Return: Matching opp, else returns ERR_PTR in case of error and should be - * handled using IS_ERR. - * - * The callers are required to call dev_pm_opp_put() for the returned OPP after - * use. + * Return: Zero or positive performance state on success, otherwise negative + * value on errors. */ -struct dev_pm_opp *of_dev_pm_opp_find_required_opp(struct device *dev, - struct device_node *np) +int of_get_required_opp_performance_state(struct device_node *np, int index) { - struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ENODEV); + struct dev_pm_opp *opp; struct device_node *required_np; struct opp_table *opp_table; + int pstate = -EINVAL; - opp_table = _find_opp_table(dev); - if (IS_ERR(opp_table)) - return ERR_CAST(opp_table); + required_np = of_parse_required_opp(np, index); + if (!required_np) + return -ENODEV; - required_np = of_parse_phandle(np, "required-opps", 0); - if (unlikely(!required_np)) { - dev_err(dev, "Unable to parse required-opps\n"); - goto put_opp_table; + opp_table = _find_table_of_opp_np(required_np); + if (IS_ERR(opp_table)) { + pr_err("%s: Failed to find required OPP table %pOF: %ld\n", + __func__, np, PTR_ERR(opp_table)); + goto put_required_np; } - mutex_lock(&opp_table->lock); - - list_for_each_entry(temp_opp, &opp_table->opp_list, node) { - if (temp_opp->available && temp_opp->np == required_np) { - opp = temp_opp; - - /* Increment the reference count of OPP */ - dev_pm_opp_get(opp); - break; - } + opp = _find_opp_of_np(opp_table, required_np); + if (opp) { + pstate = opp->pstate; + dev_pm_opp_put(opp); } - mutex_unlock(&opp_table->lock); - - of_node_put(required_np); -put_opp_table: dev_pm_opp_put_opp_table(opp_table); - return opp; +put_required_np: + of_node_put(required_np); + + return pstate; } -EXPORT_SYMBOL_GPL(of_dev_pm_opp_find_required_opp); +EXPORT_SYMBOL_GPL(of_get_required_opp_performance_state); /** * dev_pm_opp_get_of_node() - Gets the DT node corresponding to an opp @@ -792,37 +1228,29 @@ } EXPORT_SYMBOL_GPL(dev_pm_opp_get_of_node); -/** - * of_dev_pm_opp_get_cpu_power() - Estimates the power of a CPU - * @mW: pointer to the power estimate in milli-watts - * @KHz: pointer to the OPP's frequency, in kilo-hertz - * @cpu: CPU for which power needs to be estimated - * - * Computes the power estimated by @CPU at the first OPP above @KHz (ceil), - * and updates @KHz and @mW accordingly. - * - * The power is estimated as P = C * V^2 * f, with C the CPU's capacitance - * (read from the 'dynamic-power-coefficient' devicetree binding) and V and f +/* + * Callback function provided to the Energy Model framework upon registration. + * This computes the power estimated by @dev at @kHz if it is the frequency + * of an existing OPP, or at the frequency of the first OPP above @kHz otherwise + * (see dev_pm_opp_find_freq_ceil()). This function updates @kHz to the ceiled + * frequency and @mW to the associated power. The power is estimated as + * P = C * V^2 * f with C being the device's capacitance and V and f * respectively the voltage and frequency of the OPP. * - * Return: -ENODEV if the CPU device cannot be found, -EINVAL if the power - * calculation failed because of missing parameters, 0 otherwise. + * Returns -EINVAL if the power calculation failed because of missing + * parameters, 0 otherwise. */ -int of_dev_pm_opp_get_cpu_power(unsigned long *mW, unsigned long *KHz, int cpu) +static int __maybe_unused _get_power(unsigned long *mW, unsigned long *kHz, + struct device *dev) { - unsigned long mV, Hz, MHz; - struct device *cpu_dev; struct dev_pm_opp *opp; struct device_node *np; + unsigned long mV, Hz; u32 cap; u64 tmp; int ret; - cpu_dev = get_cpu_device(cpu); - if (!cpu_dev) - return -ENODEV; - - np = of_node_get(cpu_dev->of_node); + np = of_node_get(dev->of_node); if (!np) return -EINVAL; @@ -831,8 +1259,8 @@ if (ret) return -EINVAL; - Hz = *KHz * 1000; - opp = dev_pm_opp_find_freq_ceil(cpu_dev, &Hz); + Hz = *kHz * 1000; + opp = dev_pm_opp_find_freq_ceil(dev, &Hz); if (IS_ERR(opp)) return -EINVAL; @@ -841,13 +1269,73 @@ if (!mV) return -EINVAL; - MHz = Hz / 1000000; - tmp = (u64)cap * mV * mV * MHz; + tmp = (u64)cap * mV * mV * (Hz / 1000000); do_div(tmp, 1000000000); *mW = (unsigned long)tmp; - *KHz = Hz / 1000; + *kHz = Hz / 1000; return 0; } -EXPORT_SYMBOL_GPL(of_dev_pm_opp_get_cpu_power); + +/** + * dev_pm_opp_of_register_em() - Attempt to register an Energy Model + * @dev : Device for which an Energy Model has to be registered + * @cpus : CPUs for which an Energy Model has to be registered. For + * other type of devices it should be set to NULL. + * + * This checks whether the "dynamic-power-coefficient" devicetree property has + * been specified, and tries to register an Energy Model with it if it has. + * Having this property means the voltages are known for OPPs and the EM + * might be calculated. + */ +int dev_pm_opp_of_register_em(struct device *dev, struct cpumask *cpus) +{ + struct em_data_callback em_cb = EM_DATA_CB(_get_power); + struct device_node *np; + int ret, nr_opp; + u32 cap; + + if (IS_ERR_OR_NULL(dev)) { + ret = -EINVAL; + goto failed; + } + + nr_opp = dev_pm_opp_get_opp_count(dev); + if (nr_opp <= 0) { + ret = -EINVAL; + goto failed; + } + + np = of_node_get(dev->of_node); + if (!np) { + ret = -EINVAL; + goto failed; + } + + /* + * Register an EM only if the 'dynamic-power-coefficient' property is + * set in devicetree. It is assumed the voltage values are known if that + * property is set since it is useless otherwise. If voltages are not + * known, just let the EM registration fail with an error to alert the + * user about the inconsistent configuration. + */ + ret = of_property_read_u32(np, "dynamic-power-coefficient", &cap); + of_node_put(np); + if (ret || !cap) { + dev_dbg(dev, "Couldn't find proper 'dynamic-power-coefficient' in DT\n"); + ret = -EINVAL; + goto failed; + } + + ret = em_dev_register_perf_domain(dev, nr_opp, &em_cb, cpus, true); + if (ret) + goto failed; + + return 0; + +failed: + dev_dbg(dev, "Couldn't register Energy Model %d\n", ret); + return ret; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_of_register_em); -- Gitblit v1.6.2