// 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.h>#include <linux/pm_domain.h>#include <linux/slab.h>#include <linux/export.h>#include <linux/energy_model.h> #include "opp.h" /* OPP tables with uninitialized required OPPs, protected by opp_table_lock */static LIST_HEAD(lazy_opp_tables); /* * 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 __free(device_node) = _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) managed_table = dev_pm_opp_get_opp_table_ref(opp_table); break; } } 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; guard(mutex)(&opp_table->lock); list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->np == opp_np) return dev_pm_opp_get(opp); } 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 __free(device_node) = of_get_parent(opp_np); if (!opp_table_np) return ERR_PTR(-ENODEV); guard(mutex)(&opp_table_lock); list_for_each_entry(opp_table, &opp_tables, node) { if (opp_table_np == opp_table->np) return dev_pm_opp_get_opp_table_ref(opp_table); } 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; guard(mutex)(&opp_table_lock); 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; bool lazy = false; int count, i, size; /* Traversing the first OPP node is all we need */ struct device_node *np __free(device_node) = 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 <= 0) return; size = sizeof(*required_opp_tables) + sizeof(*opp_table->required_devs); required_opp_tables = kcalloc(count, size, GFP_KERNEL); if (!required_opp_tables) return; opp_table->required_opp_tables = required_opp_tables; opp_table->required_devs = (void *)(required_opp_tables + count); opp_table->required_opp_count = count; for (i = 0; i < count; i++) { struct device_node *required_np __free(device_node) = of_parse_required_opp(np, i); if (!required_np) { _opp_table_free_required_tables(opp_table); return; } required_opp_tables[i] = _find_table_of_opp_np(required_np); if (IS_ERR(required_opp_tables[i])) lazy = true; } /* Let's do the linking later on */ if (lazy) { /* * The OPP table is not held while allocating the table, take it * now to avoid corruption to the lazy_opp_tables list. */ guard(mutex)(&opp_table_lock); list_add(&opp_table->lazy, &lazy_opp_tables); }} void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index){ struct device_node *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. */ struct device_node *np __free(device_node) = 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_property_present(np, "#power-domain-cells")) opp_table->is_genpd = true; /* Get OPP table node */ opp_np = _opp_of_get_opp_desc_node(np, index); 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);} void _of_clear_opp_table(struct opp_table *opp_table){ _opp_table_free_required_tables(opp_table); of_node_put(opp_table->np);} /* * Release all resources previously acquired with a call to * _of_opp_alloc_required_opps(). */static 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);} void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp){ _of_opp_free_required_opps(opp_table, opp); of_node_put(opp->np);} static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *required_table, int index){ struct device_node *np __free(device_node) = of_parse_required_opp(opp->np, index); if (unlikely(!np)) return -ENODEV; opp->required_opps[index] = _find_opp_of_np(required_table, 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;} /* 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 opp_table *required_table; int i, ret, count = opp_table->required_opp_count; if (!count) return 0; opp->required_opps = kzalloc_objs(*opp->required_opps, count); if (!opp->required_opps) return -ENOMEM; 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; ret = _link_required_opps(opp, required_table, i); if (ret) 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 dev_pm_opp *opp; int ret; list_for_each_entry(opp, &opp_table->opp_list, node) { ret = _link_required_opps(opp, new_table, index); if (ret) return ret; } 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 dev_pm_opp *opp; int i, ret; guard(mutex)(&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 */ struct device_node *opp_np __free(device_node) = 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 */ struct device_node *required_np __free(device_node) = of_parse_required_opp(opp_np, i); struct device_node *required_table_np __free(device_node) = of_get_parent(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] = dev_pm_opp_get_opp_table_ref(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; } } /* All required opp-tables found, remove from lazy list */ if (!lazy) { list_del_init(&opp_table->lazy); list_for_each_entry(opp, &opp_table->opp_list, node) _required_opps_available(opp, opp_table->required_opp_count); } }} static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table){ struct device_node *opp_np __free(device_node) = NULL; struct property *prop; if (!opp_table) { struct device_node *np __free(device_node) = of_node_get(dev->of_node); if (!np) return -ENODEV; opp_np = _opp_of_get_opp_desc_node(np, 0); } 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 */ struct device_node *np __free(device_node) = of_get_next_available_child(opp_np, NULL); if (!np) { dev_err(dev, "OPP table empty\n"); return -EINVAL; } prop = of_find_property(np, "opp-peak-kBps", NULL); 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 __free(device_node) = of_node_get(dev->of_node); 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; if (!np) return 0; ret = 0; count = of_count_phandle_with_args(np, "interconnects", "#interconnect-cells"); 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 = kzalloc_objs(*paths, num_paths); 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 = dev_err_probe(dev, PTR_ERR(paths[i]), "%s: Unable to get path%d\n", __func__, i); 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,Lines 1–500 of 1598. 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.h> #include <linux/pm_domain.h> #include <linux/slab.h> #include <linux/export.h> #include <linux/energy_model.h> #include "opp.h" /* OPP tables with uninitialized required OPPs, protected by opp_table_lock */ static LIST_HEAD(lazy_opp_tables); /* * 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 __free(device_node) = _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) managed_table = dev_pm_opp_get_opp_table_ref(opp_table); break; } } 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; guard(mutex)(&opp_table->lock); list_for_each_entry(opp, &opp_table->opp_list, node) { if (opp->np == opp_np) return dev_pm_opp_get(opp); } 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 __free(device_node) = of_get_parent(opp_np); if (!opp_table_np) return ERR_PTR(-ENODEV); guard(mutex)(&opp_table_lock); list_for_each_entry(opp_table, &opp_tables, node) { if (opp_table_np == opp_table->np) return dev_pm_opp_get_opp_table_ref(opp_table); } 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; guard(mutex)(&opp_table_lock); 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; bool lazy = false; int count, i, size; /* Traversing the first OPP node is all we need */ struct device_node *np __free(device_node) = 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 <= 0) return; size = sizeof(*required_opp_tables) + sizeof(*opp_table->required_devs); required_opp_tables = kcalloc(count, size, GFP_KERNEL); if (!required_opp_tables) return; opp_table->required_opp_tables = required_opp_tables; opp_table->required_devs = (void *)(required_opp_tables + count); opp_table->required_opp_count = count; for (i = 0; i < count; i++) { struct device_node *required_np __free(device_node) = of_parse_required_opp(np, i); if (!required_np) { _opp_table_free_required_tables(opp_table); return; } required_opp_tables[i] = _find_table_of_opp_np(required_np); if (IS_ERR(required_opp_tables[i])) lazy = true; } /* Let's do the linking later on */ if (lazy) { /* * The OPP table is not held while allocating the table, take it * now to avoid corruption to the lazy_opp_tables list. */ guard(mutex)(&opp_table_lock); list_add(&opp_table->lazy, &lazy_opp_tables); } } void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index) { struct device_node *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. */ struct device_node *np __free(device_node) = 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_property_present(np, "#power-domain-cells")) opp_table->is_genpd = true; /* Get OPP table node */ opp_np = _opp_of_get_opp_desc_node(np, index); 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); } void _of_clear_opp_table(struct opp_table *opp_table) { _opp_table_free_required_tables(opp_table); of_node_put(opp_table->np); } /* * Release all resources previously acquired with a call to * _of_opp_alloc_required_opps(). */ static 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); } void _of_clear_opp(struct opp_table *opp_table, struct dev_pm_opp *opp) { _of_opp_free_required_opps(opp_table, opp); of_node_put(opp->np); } static int _link_required_opps(struct dev_pm_opp *opp, struct opp_table *required_table, int index) { struct device_node *np __free(device_node) = of_parse_required_opp(opp->np, index); if (unlikely(!np)) return -ENODEV; opp->required_opps[index] = _find_opp_of_np(required_table, 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; } /* 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 opp_table *required_table; int i, ret, count = opp_table->required_opp_count; if (!count) return 0; opp->required_opps = kzalloc_objs(*opp->required_opps, count); if (!opp->required_opps) return -ENOMEM; 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; ret = _link_required_opps(opp, required_table, i); if (ret) 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 dev_pm_opp *opp; int ret; list_for_each_entry(opp, &opp_table->opp_list, node) { ret = _link_required_opps(opp, new_table, index); if (ret) return ret; } 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 dev_pm_opp *opp; int i, ret; guard(mutex)(&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 */ struct device_node *opp_np __free(device_node) = 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 */ struct device_node *required_np __free(device_node) = of_parse_required_opp(opp_np, i); struct device_node *required_table_np __free(device_node) = of_get_parent(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] = dev_pm_opp_get_opp_table_ref(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; } } /* All required opp-tables found, remove from lazy list */ if (!lazy) { list_del_init(&opp_table->lazy); list_for_each_entry(opp, &opp_table->opp_list, node) _required_opps_available(opp, opp_table->required_opp_count); } } } static int _bandwidth_supported(struct device *dev, struct opp_table *opp_table) { struct device_node *opp_np __free(device_node) = NULL; struct property *prop; if (!opp_table) { struct device_node *np __free(device_node) = of_node_get(dev->of_node); if (!np) return -ENODEV; opp_np = _opp_of_get_opp_desc_node(np, 0); } 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 */ struct device_node *np __free(device_node) = of_get_next_available_child(opp_np, NULL); if (!np) { dev_err(dev, "OPP table empty\n"); return -EINVAL; } prop = of_find_property(np, "opp-peak-kBps", NULL); 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 __free(device_node) = of_node_get(dev->of_node); 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; if (!np) return 0; ret = 0; count = of_count_phandle_with_args(np, "interconnects", "#interconnect-cells"); 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 = kzalloc_objs(*paths, num_paths); 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 = dev_err_probe(dev, PTR_ERR(paths[i]), "%s: Unable to get path%d\n", __func__, i); 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,