Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

OPP: Prevent creating multiple OPP tables for devices sharing OPP nodes

When two or more devices are sharing their clock and voltage rails, they
share the same OPP table. But there are some corner cases where the OPP
core incorrectly creates separate OPP tables for them.

For example, CPU 0 and 1 share clock/voltage rails. The platform
specific code calls dev_pm_opp_set_regulators() for CPU0 and the OPP
core creates an OPP table for it (the individual OPPs aren't initialized
as of now). The same is repeated for CPU1 then. Because
_opp_get_opp_table() doesn't compare DT node pointers currently, it
fails to find the link between CPU0 and CPU1 and so creates a new OPP
table.

Fix this by calling _managed_opp() from _opp_get_opp_table().
_managed_opp() gain an additional argument (index) to get the right node
pointer. This resulted in simplifying code in _of_add_opp_table_v2() as
well.

Tested-by: Niklas Cassel <niklas.cassel@linaro.org>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>

+37 -25
+22 -3
drivers/opp/core.c
··· 759 759 kfree(opp_dev); 760 760 } 761 761 762 - struct opp_device *_add_opp_dev(const struct device *dev, 763 - struct opp_table *opp_table) 762 + static struct opp_device *_add_opp_dev_unlocked(const struct device *dev, 763 + struct opp_table *opp_table) 764 764 { 765 765 struct opp_device *opp_dev; 766 766 int ret; ··· 772 772 /* Initialize opp-dev */ 773 773 opp_dev->dev = dev; 774 774 775 - mutex_lock(&opp_table->lock); 776 775 list_add(&opp_dev->node, &opp_table->dev_list); 777 776 778 777 /* Create debugfs entries for the opp_table */ ··· 779 780 if (ret) 780 781 dev_err(dev, "%s: Failed to register opp debugfs (%d)\n", 781 782 __func__, ret); 783 + 784 + return opp_dev; 785 + } 786 + 787 + struct opp_device *_add_opp_dev(const struct device *dev, 788 + struct opp_table *opp_table) 789 + { 790 + struct opp_device *opp_dev; 791 + 792 + mutex_lock(&opp_table->lock); 793 + opp_dev = _add_opp_dev_unlocked(dev, opp_table); 782 794 mutex_unlock(&opp_table->lock); 783 795 784 796 return opp_dev; ··· 853 843 opp_table = _find_opp_table_unlocked(dev); 854 844 if (!IS_ERR(opp_table)) 855 845 goto unlock; 846 + 847 + opp_table = _managed_opp(dev, index); 848 + if (opp_table) { 849 + if (!_add_opp_dev_unlocked(dev, opp_table)) { 850 + dev_pm_opp_put_opp_table(opp_table); 851 + opp_table = NULL; 852 + } 853 + goto unlock; 854 + } 856 855 857 856 opp_table = _allocate_opp_table(dev, index); 858 857
+13 -22
drivers/opp/of.c
··· 41 41 } 42 42 EXPORT_SYMBOL_GPL(dev_pm_opp_of_get_opp_desc_node); 43 43 44 - static struct opp_table *_managed_opp(const struct device_node *np) 44 + struct opp_table *_managed_opp(struct device *dev, int index) 45 45 { 46 46 struct opp_table *opp_table, *managed_table = NULL; 47 + struct device_node *np; 47 48 48 - mutex_lock(&opp_table_lock); 49 + np = _opp_of_get_opp_desc_node(dev->of_node, index); 50 + if (!np) 51 + return NULL; 49 52 50 53 list_for_each_entry(opp_table, &opp_tables, node) { 51 54 if (opp_table->np == np) { ··· 68 65 } 69 66 } 70 67 71 - mutex_unlock(&opp_table_lock); 68 + of_node_put(np); 72 69 73 70 return managed_table; 74 71 } ··· 404 401 { 405 402 struct device_node *np; 406 403 struct opp_table *opp_table; 407 - int ret = 0, count = 0, pstate_count = 0; 404 + int ret, count = 0, pstate_count = 0; 408 405 struct dev_pm_opp *opp; 409 - 410 - opp_table = _managed_opp(opp_np); 411 - if (opp_table) { 412 - /* OPPs are already managed */ 413 - if (!_add_opp_dev(dev, opp_table)) { 414 - ret = -ENOMEM; 415 - goto put_opp_table; 416 - } 417 - 418 - if (opp_table->parsed_static_opps) { 419 - kref_get(&opp_table->list_kref); 420 - return 0; 421 - } 422 - 423 - goto initialize_static_opps; 424 - } 425 406 426 407 opp_table = dev_pm_opp_get_opp_table_indexed(dev, index); 427 408 if (!opp_table) 428 409 return -ENOMEM; 429 410 430 - initialize_static_opps: 411 + /* OPP table is already initialized for the device */ 412 + if (opp_table->parsed_static_opps) { 413 + kref_get(&opp_table->list_kref); 414 + return 0; 415 + } 416 + 431 417 kref_init(&opp_table->list_kref); 432 418 433 419 /* We have opp-table node now, iterate over it and add OPPs */ ··· 458 466 459 467 put_list_kref: 460 468 _put_opp_list_kref(opp_table); 461 - put_opp_table: 462 469 dev_pm_opp_put_opp_table(opp_table); 463 470 464 471 return ret;
+2
drivers/opp/opp.h
··· 206 206 207 207 #ifdef CONFIG_OF 208 208 void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index); 209 + struct opp_table *_managed_opp(struct device *dev, int index); 209 210 #else 210 211 static inline void _of_init_opp_table(struct opp_table *opp_table, struct device *dev, int index) {} 212 + static inline struct opp_table *_managed_opp(struct device *dev, int index) { return NULL; } 211 213 #endif 212 214 213 215 #ifdef CONFIG_DEBUG_FS