// SPDX-License-Identifier: GPL-2.0-only/* * Generic OPP OF helpers * * Copyright (C) 2009-2010 Texas Instruments Incorporated. * Nishanth Menon * Romit Dasgupta * Kevin Hilman */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/cpu.h>#include <linux/errno.h>#include <linux/device.h>#include <linux/of_device.h>#include <linux/pm_domain.h>#include <linux/slab.h>#include <linux/export.h>#include <linux/energy_model.h> #include "opp.h" /* * 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; 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) { /* * Multiple devices can point to the same OPP table and * so will have same node-pointer, np. * * But the OPPs will be considered as shared only if the * OPP table contains a "opp-shared" property. */ if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) { _get_opp_table_kref(opp_table); managed_table = opp_table; } break; } } of_node_put(np); return managed_table;} /* 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 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){ struct device_node *required_np; required_np = of_parse_phandle(np, "required-opps", index); if (unlikely(!required_np)) { pr_err("%s: Unable to parse required-opps: %pOF, index: %d\n", __func__, np, index); } return required_np;} /* 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; 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); mutex_lock(&opp_table_lock); list_for_each_entry(opp_table, &opp_tables, node) { if (opp_table_np == opp_table->np) { _get_opp_table_kref(opp_table); mutex_unlock(&opp_table_lock); return opp_table; } } mutex_unlock(&opp_table_lock); 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])) continue; 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; list_del(&opp_table->lazy);} /* * 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; bool lazy = false; int count, i; /* Traversing the first OPP node is all we need */ np = of_get_next_available_child(opp_np, NULL); if (!np) { dev_warn(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])) { lazy = true; continue; } /* * 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; } } /* Let's do the linking later on */ if (lazy) list_add(&opp_table->lazy, &lazy_opp_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) 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); 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]) continue; /* Put the reference back */ dev_pm_opp_put(required_opps[i]); } opp->required_opps = NULL; kfree(required_opps);} /* 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]; /* Required table not added yet, we will link later */ if (IS_ERR_OR_NULL(required_table)) continue; 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;} /* Link required OPPs for an individual OPP */static int lazy_link_required_opps(struct opp_table *opp_table, struct opp_table *new_table, int index){ struct device_node *required_np; struct dev_pm_opp *opp; list_for_each_entry(opp, &opp_table->opp_list, node) { required_np = of_parse_required_opp(opp->np, index); if (unlikely(!required_np)) return -ENODEV; opp->required_opps[index] = _find_opp_of_np(new_table, required_np); of_node_put(required_np); if (!opp->required_opps[index]) { pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", __func__, opp->np, index); return -ENODEV; } } return 0;} /* Link required OPPs for all OPPs of the newly added OPP table */static void lazy_link_required_opp_table(struct opp_table *new_table){ struct opp_table *opp_table, *temp, **required_opp_tables; struct device_node *required_np, *opp_np, *required_table_np; struct dev_pm_opp *opp; int i, ret; /* * We only support genpd's OPPs in the "required-opps" for now, * as we don't know much about other cases. */ if (!new_table->is_genpd) return; mutex_lock(&opp_table_lock); list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) { bool lazy = false; /* opp_np can't be invalid here */ opp_np = of_get_next_available_child(opp_table->np, NULL); for (i = 0; i < opp_table->required_opp_count; i++) { required_opp_tables = opp_table->required_opp_tables; /* Required opp-table is already parsed */ if (!IS_ERR(required_opp_tables[i])) continue; /* required_np can't be invalid here */ required_np = of_parse_required_opp(opp_np, i); required_table_np = of_get_parent(required_np); of_node_put(required_table_np); of_node_put(required_np); /* * Newly added table isn't the required opp-table for * opp_table. */ if (required_table_np != new_table->np) { lazy = true; continue; } required_opp_tables[i] = new_table; _get_opp_table_kref(new_table); /* Link OPPs now */ ret = lazy_link_required_opps(opp_table, new_table, i); if (ret) { /* The OPPs will be marked unusable */ lazy = false; break; } } of_node_put(opp_np); /* All required opp-tables found, remove from lazy list */ if (!lazy) { list_del(&opp_table->lazy); INIT_LIST_HEAD(&opp_table->lazy); list_for_each_entry(opp, &opp_table->opp_list, node) _required_opps_available(opp, opp_table->required_opp_count); } } mutex_unlock(&opp_table_lock);} 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); if (!np) { dev_err(dev, "OPP table empty\n"); return -EINVAL; } of_node_put(opp_np); 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 == -EINVAL) return 0; /* Empty OPP table is a valid corner-case, let's not fail */ else if (ret <= 0) return ret; ret = 0; np = of_node_get(dev->of_node); if (!np)Lines 1–500 of 1504. Load more
// SPDX-License-Identifier: GPL-2.0-only /* * Generic OPP OF helpers * * Copyright (C) 2009-2010 Texas Instruments Incorporated. * Nishanth Menon * Romit Dasgupta * Kevin Hilman */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/cpu.h> #include <linux/errno.h> #include <linux/device.h> #include <linux/of_device.h> #include <linux/pm_domain.h> #include <linux/slab.h> #include <linux/export.h> #include <linux/energy_model.h> #include "opp.h" /* * 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; 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) { /* * Multiple devices can point to the same OPP table and * so will have same node-pointer, np. * * But the OPPs will be considered as shared only if the * OPP table contains a "opp-shared" property. */ if (opp_table->shared_opp == OPP_TABLE_ACCESS_SHARED) { _get_opp_table_kref(opp_table); managed_table = opp_table; } break; } } of_node_put(np); return managed_table; } /* 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 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) { struct device_node *required_np; required_np = of_parse_phandle(np, "required-opps", index); if (unlikely(!required_np)) { pr_err("%s: Unable to parse required-opps: %pOF, index: %d\n", __func__, np, index); } return required_np; } /* 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; 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); mutex_lock(&opp_table_lock); list_for_each_entry(opp_table, &opp_tables, node) { if (opp_table_np == opp_table->np) { _get_opp_table_kref(opp_table); mutex_unlock(&opp_table_lock); return opp_table; } } mutex_unlock(&opp_table_lock); 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])) continue; 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; list_del(&opp_table->lazy); } /* * 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; bool lazy = false; int count, i; /* Traversing the first OPP node is all we need */ np = of_get_next_available_child(opp_np, NULL); if (!np) { dev_warn(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])) { lazy = true; continue; } /* * 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; } } /* Let's do the linking later on */ if (lazy) list_add(&opp_table->lazy, &lazy_opp_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) 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); 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]) continue; /* Put the reference back */ dev_pm_opp_put(required_opps[i]); } opp->required_opps = NULL; kfree(required_opps); } /* 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]; /* Required table not added yet, we will link later */ if (IS_ERR_OR_NULL(required_table)) continue; 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; } /* Link required OPPs for an individual OPP */ static int lazy_link_required_opps(struct opp_table *opp_table, struct opp_table *new_table, int index) { struct device_node *required_np; struct dev_pm_opp *opp; list_for_each_entry(opp, &opp_table->opp_list, node) { required_np = of_parse_required_opp(opp->np, index); if (unlikely(!required_np)) return -ENODEV; opp->required_opps[index] = _find_opp_of_np(new_table, required_np); of_node_put(required_np); if (!opp->required_opps[index]) { pr_err("%s: Unable to find required OPP node: %pOF (%d)\n", __func__, opp->np, index); return -ENODEV; } } return 0; } /* Link required OPPs for all OPPs of the newly added OPP table */ static void lazy_link_required_opp_table(struct opp_table *new_table) { struct opp_table *opp_table, *temp, **required_opp_tables; struct device_node *required_np, *opp_np, *required_table_np; struct dev_pm_opp *opp; int i, ret; /* * We only support genpd's OPPs in the "required-opps" for now, * as we don't know much about other cases. */ if (!new_table->is_genpd) return; mutex_lock(&opp_table_lock); list_for_each_entry_safe(opp_table, temp, &lazy_opp_tables, lazy) { bool lazy = false; /* opp_np can't be invalid here */ opp_np = of_get_next_available_child(opp_table->np, NULL); for (i = 0; i < opp_table->required_opp_count; i++) { required_opp_tables = opp_table->required_opp_tables; /* Required opp-table is already parsed */ if (!IS_ERR(required_opp_tables[i])) continue; /* required_np can't be invalid here */ required_np = of_parse_required_opp(opp_np, i); required_table_np = of_get_parent(required_np); of_node_put(required_table_np); of_node_put(required_np); /* * Newly added table isn't the required opp-table for * opp_table. */ if (required_table_np != new_table->np) { lazy = true; continue; } required_opp_tables[i] = new_table; _get_opp_table_kref(new_table); /* Link OPPs now */ ret = lazy_link_required_opps(opp_table, new_table, i); if (ret) { /* The OPPs will be marked unusable */ lazy = false; break; } } of_node_put(opp_np); /* All required opp-tables found, remove from lazy list */ if (!lazy) { list_del(&opp_table->lazy); INIT_LIST_HEAD(&opp_table->lazy); list_for_each_entry(opp, &opp_table->opp_list, node) _required_opps_available(opp, opp_table->required_opp_count); } } mutex_unlock(&opp_table_lock); } 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); if (!np) { dev_err(dev, "OPP table empty\n"); return -EINVAL; } of_node_put(opp_np); 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 == -EINVAL) return 0; /* Empty OPP table is a valid corner-case, let's not fail */ else if (ret <= 0) return ret; ret = 0; np = of_node_get(dev->of_node); if (!np)