From 4dff070117cc522251024ccc504255785664fb74 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 21 Jul 2023 13:34:54 +0530 Subject: [PATCH 1/7] OPP: Rearrange entries in pm_opp.h [ Upstream commit 754833b3194c30dad5af0145e25192a8e29521ab ] Rearrange the helper function declarations / definitions to keep them in order of freq, level and then bw. Signed-off-by: Viresh Kumar Stable-dep-of: b44b9bc7cab2 ("OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized") Signed-off-by: Sasha Levin --- include/linux/pm_opp.h | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index dc1fb5890792..3821f50b9b89 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -121,17 +121,19 @@ unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev); struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available); + struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); -struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, - unsigned int level); -struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, - unsigned int *level); - struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq); +struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, + unsigned int level); + +struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, + unsigned int *level); + struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, unsigned int *bw, int index); @@ -247,18 +249,6 @@ static inline unsigned long dev_pm_opp_get_suspend_opp_freq(struct device *dev) return 0; } -static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, - unsigned int level) -{ - return ERR_PTR(-EOPNOTSUPP); -} - -static inline struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, - unsigned int *level) -{ - return ERR_PTR(-EOPNOTSUPP); -} - static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available) { @@ -277,6 +267,18 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, + unsigned int level) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline struct dev_pm_opp *dev_pm_opp_find_level_ceil(struct device *dev, + unsigned int *level) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, unsigned int *bw, int index) { From 92fcb46659d5dbfdad0422a503e289085990a5d0 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 20 Jul 2023 11:10:52 +0530 Subject: [PATCH 2/7] OPP: Introduce dev_pm_opp_find_freq_{ceil/floor}_indexed() APIs [ Upstream commit 142e17c1c2b48e3fb4f024e62ab6dee18f268694 ] In the case of devices with multiple clocks, drivers need to specify the clock index for the OPP framework to find the OPP corresponding to the floor/ceil of the supplied frequency. So let's introduce the two new APIs accepting the clock index as an argument. These APIs use the exising _find_key_ceil() helper by supplying the clock index to it. Signed-off-by: Manivannan Sadhasivam [ Viresh: Rearranged definitions in pm_opp.h ] Signed-off-by: Viresh Kumar Stable-dep-of: b44b9bc7cab2 ("OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized") Signed-off-by: Sasha Levin --- drivers/opp/core.c | 56 ++++++++++++++++++++++++++++++++++++++++++ include/linux/pm_opp.h | 18 ++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 71d3e3ba909a..8775f9d71f90 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -653,6 +653,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil); +/** + * dev_pm_opp_find_freq_ceil_indexed() - Search for a rounded ceil freq for the + * clock corresponding to the index + * @dev: Device for which we do this operation + * @freq: Start frequency + * @index: Clock index + * + * Search for the matching ceil *available* OPP for the clock corresponding to + * the specified index from a starting freq for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp * +dev_pm_opp_find_freq_ceil_indexed(struct device *dev, unsigned long *freq, + u32 index) +{ + return _find_key_ceil(dev, freq, index, true, _read_freq, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_indexed); + /** * dev_pm_opp_find_freq_floor() - Search for a rounded floor freq * @dev: device for which we do this operation @@ -678,6 +706,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor); +/** + * dev_pm_opp_find_freq_floor_indexed() - Search for a rounded floor freq for the + * clock corresponding to the index + * @dev: Device for which we do this operation + * @freq: Start frequency + * @index: Clock index + * + * Search for the matching floor *available* OPP for the clock corresponding to + * the specified index from a starting freq for a device. + * + * Return: matching *opp and refreshes *freq accordingly, else returns + * ERR_PTR in case of error and should be handled using IS_ERR. Error return + * values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp * +dev_pm_opp_find_freq_floor_indexed(struct device *dev, unsigned long *freq, + u32 index) +{ + return _find_key_floor(dev, freq, index, true, _read_freq, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor_indexed); + /** * dev_pm_opp_find_level_exact() - search for an exact level * @dev: device for which we do this operation diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 3821f50b9b89..2617f2c51f29 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -125,9 +125,15 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); +struct dev_pm_opp *dev_pm_opp_find_freq_floor_indexed(struct device *dev, + unsigned long *freq, u32 index); + struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq); +struct dev_pm_opp *dev_pm_opp_find_freq_ceil_indexed(struct device *dev, + unsigned long *freq, u32 index); + struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level); @@ -261,12 +267,24 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +static inline struct dev_pm_opp * +dev_pm_opp_find_freq_floor_indexed(struct device *dev, unsigned long *freq, u32 index) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_ceil(struct device *dev, unsigned long *freq) { return ERR_PTR(-EOPNOTSUPP); } +static inline struct dev_pm_opp * +dev_pm_opp_find_freq_ceil_indexed(struct device *dev, unsigned long *freq, u32 index) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_level_exact(struct device *dev, unsigned int level) { From 7f73098bc6104eb06f85104a512a6728c9e6eaa2 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 20 Jul 2023 11:10:53 +0530 Subject: [PATCH 3/7] OPP: Introduce dev_pm_opp_get_freq_indexed() API [ Upstream commit 5f756d03e2c7db63c1df7148d7b1739f29ff1532 ] In the case of devices with multiple clocks, drivers need to specify the frequency index for the OPP framework to get the specific frequency within the required OPP. So let's introduce the dev_pm_opp_get_freq_indexed() API accepting the frequency index as an argument. Signed-off-by: Manivannan Sadhasivam [ Viresh: Fixed potential access to NULL opp pointer ] Signed-off-by: Viresh Kumar Stable-dep-of: b44b9bc7cab2 ("OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized") Signed-off-by: Sasha Levin --- drivers/opp/core.c | 20 ++++++++++++++++++++ include/linux/pm_opp.h | 7 +++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 8775f9d71f90..1483d10627fb 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -200,6 +200,26 @@ unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); +/** + * dev_pm_opp_get_freq_indexed() - Gets the frequency corresponding to an + * available opp with specified index + * @opp: opp for which frequency has to be returned for + * @index: index of the frequency within the required opp + * + * Return: frequency in hertz corresponding to the opp with specified index, + * else return 0 + */ +unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index) +{ + if (IS_ERR_OR_NULL(opp) || index >= opp->opp_table->clk_count) { + pr_err("%s: Invalid parameters\n", __func__); + return 0; + } + + return opp->rates[index]; +} +EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq_indexed); + /** * dev_pm_opp_get_level() - Gets the level corresponding to an available opp * @opp: opp for which level value has to be returned for diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 2617f2c51f29..a13a1705df57 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -105,6 +105,8 @@ unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp); unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); +unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index); + unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp); unsigned int dev_pm_opp_get_required_pstate(struct dev_pm_opp *opp, @@ -213,6 +215,11 @@ static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) return 0; } +static inline unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index) +{ + return 0; +} + static inline unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp) { return 0; From 7baa59f83ff365bcc9a0dd4f7877a98e62d138e1 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 21 Jul 2023 13:41:20 +0530 Subject: [PATCH 4/7] OPP: Add dev_pm_opp_find_freq_exact_indexed() [ Upstream commit a5893928bb179d67ca1d44a8f66c990480ba541d ] The indexed version of the API is added for other floor and ceil, add the same for exact as well for completeness. Signed-off-by: Viresh Kumar Stable-dep-of: b44b9bc7cab2 ("OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized") Signed-off-by: Sasha Levin --- drivers/opp/core.c | 28 ++++++++++++++++++++++++++++ include/linux/pm_opp.h | 11 +++++++++++ 2 files changed, 39 insertions(+) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 1483d10627fb..e1d9eddf2683 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -641,6 +641,34 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); +/** + * dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the + * clock corresponding to the index + * @dev: Device for which we do this operation + * @freq: frequency to search for + * @index: Clock index + * @available: true/false - match for available opp + * + * Search for the matching exact OPP for the clock corresponding to the + * specified index from a starting freq for a device. + * + * Return: matching *opp , else returns ERR_PTR in case of error and should be + * handled using IS_ERR. Error return values can be: + * EINVAL: for bad pointer + * ERANGE: no match found for search + * ENODEV: if device not found in list of registered devices + * + * The callers are required to call dev_pm_opp_put() for the returned OPP after + * use. + */ +struct dev_pm_opp * +dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, + u32 index, bool available) +{ + return _find_key_exact(dev, freq, index, available, _read_freq, NULL); +} +EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact_indexed); + static noinline struct dev_pm_opp *_find_freq_ceil(struct opp_table *opp_table, unsigned long *freq) { diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index a13a1705df57..23e4e4eaaa42 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -124,6 +124,10 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, unsigned long freq, bool available); +struct dev_pm_opp * +dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, + u32 index, bool available); + struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq); @@ -268,6 +272,13 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, return ERR_PTR(-EOPNOTSUPP); } +static inline struct dev_pm_opp * +dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, + u32 index, bool available) +{ + return ERR_PTR(-EOPNOTSUPP); +} + static inline struct dev_pm_opp *dev_pm_opp_find_freq_floor(struct device *dev, unsigned long *freq) { From e20fd4d3a4b5f5310f2db5ed06d930021ee9acf3 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 21 Jul 2023 14:55:07 +0530 Subject: [PATCH 5/7] OPP: Reuse dev_pm_opp_get_freq_indexed() [ Upstream commit 746de8255076c9924ffa51baad9822adddccb94e ] Reuse dev_pm_opp_get_freq_indexed() from dev_pm_opp_get_freq(). Signed-off-by: Viresh Kumar Acked-by: Manivannan Sadhasivam Stable-dep-of: b44b9bc7cab2 ("OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized") Signed-off-by: Sasha Levin --- drivers/opp/core.c | 21 --------------------- include/linux/pm_opp.h | 12 +++++------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index e1d9eddf2683..83ff32b7f479 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -179,27 +179,6 @@ unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp) } EXPORT_SYMBOL_GPL(dev_pm_opp_get_power); -/** - * dev_pm_opp_get_freq() - Gets the frequency corresponding to an available opp - * @opp: opp for which frequency has to be returned for - * - * Return: frequency in hertz corresponding to the opp, else - * return 0 - */ -unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) -{ - if (IS_ERR_OR_NULL(opp)) { - pr_err("%s: Invalid parameters\n", __func__); - return 0; - } - - if (!assert_single_clk(opp->opp_table)) - return 0; - - return opp->rates[0]; -} -EXPORT_SYMBOL_GPL(dev_pm_opp_get_freq); - /** * dev_pm_opp_get_freq_indexed() - Gets the frequency corresponding to an * available opp with specified index diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h index 23e4e4eaaa42..91f87d7e807c 100644 --- a/include/linux/pm_opp.h +++ b/include/linux/pm_opp.h @@ -103,8 +103,6 @@ int dev_pm_opp_get_supplies(struct dev_pm_opp *opp, struct dev_pm_opp_supply *su unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp); -unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp); - unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index); unsigned int dev_pm_opp_get_level(struct dev_pm_opp *opp); @@ -214,11 +212,6 @@ static inline unsigned long dev_pm_opp_get_power(struct dev_pm_opp *opp) return 0; } -static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) -{ - return 0; -} - static inline unsigned long dev_pm_opp_get_freq_indexed(struct dev_pm_opp *opp, u32 index) { return 0; @@ -669,4 +662,9 @@ static inline void dev_pm_opp_put_prop_name(int token) dev_pm_opp_clear_config(token); } +static inline unsigned long dev_pm_opp_get_freq(struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_freq_indexed(opp, 0); +} + #endif /* __LINUX_OPP_H__ */ From 774dd6f0f0a61c9c3848e025d7d9eeed1a7ca4cd Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 3 Dec 2024 09:12:59 +0100 Subject: [PATCH 6/7] OPP: add index check to assert to avoid buffer overflow in _read_freq() [ Upstream commit d659bc68ed489022ea33342cfbda2911a81e7a0d ] Pass the freq index to the assert function to make sure we do not read a freq out of the opp->rates[] table when called from the indexed variants: dev_pm_opp_find_freq_exact_indexed() or dev_pm_opp_find_freq_ceil/floor_indexed(). Add a secondary parameter to the assert function, unused for assert_single_clk() then add assert_clk_index() which will check for the clock index when called from the _indexed() find functions. Fixes: 142e17c1c2b4 ("OPP: Introduce dev_pm_opp_find_freq_{ceil/floor}_indexed() APIs") Fixes: a5893928bb17 ("OPP: Add dev_pm_opp_find_freq_exact_indexed()") Signed-off-by: Neil Armstrong Signed-off-by: Viresh Kumar Stable-dep-of: b44b9bc7cab2 ("OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized") Signed-off-by: Sasha Levin --- drivers/opp/core.c | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 83ff32b7f479..53286063df55 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -104,11 +104,21 @@ struct opp_table *_find_opp_table(struct device *dev) * representation in the OPP table and manage the clock configuration themselves * in an platform specific way. */ -static bool assert_single_clk(struct opp_table *opp_table) +static bool assert_single_clk(struct opp_table *opp_table, + unsigned int __always_unused index) { return !WARN_ON(opp_table->clk_count > 1); } +/* + * Returns true if clock table is large enough to contain the clock index. + */ +static bool assert_clk_index(struct opp_table *opp_table, + unsigned int index) +{ + return opp_table->clk_count > index; +} + /** * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp * @opp: opp for which voltage has to be returned for @@ -496,12 +506,12 @@ static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table, unsigned long (*read)(struct dev_pm_opp *opp, int index), bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, unsigned long opp_key, unsigned long key), - bool (*assert)(struct opp_table *opp_table)) + bool (*assert)(struct opp_table *opp_table, unsigned int index)) { struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); /* Assert that the requirement is met */ - if (assert && !assert(opp_table)) + if (assert && !assert(opp_table, index)) return ERR_PTR(-EINVAL); mutex_lock(&opp_table->lock); @@ -529,7 +539,7 @@ _find_key(struct device *dev, unsigned long *key, int index, bool available, unsigned long (*read)(struct dev_pm_opp *opp, int index), bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, unsigned long opp_key, unsigned long key), - bool (*assert)(struct opp_table *opp_table)) + bool (*assert)(struct opp_table *opp_table, unsigned int index)) { struct opp_table *opp_table; struct dev_pm_opp *opp; @@ -552,7 +562,7 @@ _find_key(struct device *dev, unsigned long *key, int index, bool available, static struct dev_pm_opp *_find_key_exact(struct device *dev, unsigned long key, int index, bool available, unsigned long (*read)(struct dev_pm_opp *opp, int index), - bool (*assert)(struct opp_table *opp_table)) + bool (*assert)(struct opp_table *opp_table, unsigned int index)) { /* * The value of key will be updated here, but will be ignored as the @@ -565,7 +575,7 @@ static struct dev_pm_opp *_find_key_exact(struct device *dev, static struct dev_pm_opp *_opp_table_find_key_ceil(struct opp_table *opp_table, unsigned long *key, int index, bool available, unsigned long (*read)(struct dev_pm_opp *opp, int index), - bool (*assert)(struct opp_table *opp_table)) + bool (*assert)(struct opp_table *opp_table, unsigned int index)) { return _opp_table_find_key(opp_table, key, index, available, read, _compare_ceil, assert); @@ -574,7 +584,7 @@ static struct dev_pm_opp *_opp_table_find_key_ceil(struct opp_table *opp_table, static struct dev_pm_opp *_find_key_ceil(struct device *dev, unsigned long *key, int index, bool available, unsigned long (*read)(struct dev_pm_opp *opp, int index), - bool (*assert)(struct opp_table *opp_table)) + bool (*assert)(struct opp_table *opp_table, unsigned int index)) { return _find_key(dev, key, index, available, read, _compare_ceil, assert); @@ -583,7 +593,7 @@ static struct dev_pm_opp *_find_key_ceil(struct device *dev, unsigned long *key, static struct dev_pm_opp *_find_key_floor(struct device *dev, unsigned long *key, int index, bool available, unsigned long (*read)(struct dev_pm_opp *opp, int index), - bool (*assert)(struct opp_table *opp_table)) + bool (*assert)(struct opp_table *opp_table, unsigned int index)) { return _find_key(dev, key, index, available, read, _compare_floor, assert); @@ -644,7 +654,8 @@ struct dev_pm_opp * dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, u32 index, bool available) { - return _find_key_exact(dev, freq, index, available, _read_freq, NULL); + return _find_key_exact(dev, freq, index, available, _read_freq, + assert_clk_index); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact_indexed); @@ -704,7 +715,8 @@ struct dev_pm_opp * dev_pm_opp_find_freq_ceil_indexed(struct device *dev, unsigned long *freq, u32 index) { - return _find_key_ceil(dev, freq, index, true, _read_freq, NULL); + return _find_key_ceil(dev, freq, index, true, _read_freq, + assert_clk_index); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_ceil_indexed); @@ -757,7 +769,7 @@ struct dev_pm_opp * dev_pm_opp_find_freq_floor_indexed(struct device *dev, unsigned long *freq, u32 index) { - return _find_key_floor(dev, freq, index, true, _read_freq, NULL); + return _find_key_floor(dev, freq, index, true, _read_freq, assert_clk_index); } EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_floor_indexed); @@ -1671,7 +1683,7 @@ void dev_pm_opp_remove(struct device *dev, unsigned long freq) if (IS_ERR(opp_table)) return; - if (!assert_single_clk(opp_table)) + if (!assert_single_clk(opp_table, 0)) goto put_table; mutex_lock(&opp_table->lock); @@ -2022,7 +2034,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long tol; int ret; - if (!assert_single_clk(opp_table)) + if (!assert_single_clk(opp_table, 0)) return -EINVAL; new_opp = _opp_allocate(opp_table); @@ -2878,7 +2890,7 @@ static int _opp_set_availability(struct device *dev, unsigned long freq, return r; } - if (!assert_single_clk(opp_table)) { + if (!assert_single_clk(opp_table, 0)) { r = -EINVAL; goto put_table; } @@ -2954,7 +2966,7 @@ int dev_pm_opp_adjust_voltage(struct device *dev, unsigned long freq, return r; } - if (!assert_single_clk(opp_table)) { + if (!assert_single_clk(opp_table, 0)) { r = -EINVAL; goto put_table; } From 8532fd078d2a5286915d03bb0a0893ee1955acef Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Tue, 3 Dec 2024 09:13:00 +0100 Subject: [PATCH 7/7] OPP: fix dev_pm_opp_find_bw_*() when bandwidth table not initialized [ Upstream commit b44b9bc7cab2967c3d6a791b1cd542c89fc07f0e ] If a driver calls dev_pm_opp_find_bw_ceil/floor() the retrieve bandwidth from the OPP table but the bandwidth table was not created because the interconnect properties were missing in the OPP consumer node, the kernel will crash with: Unable to handle kernel NULL pointer dereference at virtual address 0000000000000004 ... pc : _read_bw+0x8/0x10 lr : _opp_table_find_key+0x9c/0x174 ... Call trace: _read_bw+0x8/0x10 (P) _opp_table_find_key+0x9c/0x174 (L) _find_key+0x98/0x168 dev_pm_opp_find_bw_ceil+0x50/0x88 ... In order to fix the crash, create an assert function to check if the bandwidth table was created before trying to get a bandwidth with _read_bw(). Fixes: add1dc094a74 ("OPP: Use generic key finding helpers for bandwidth key") Signed-off-by: Neil Armstrong Signed-off-by: Viresh Kumar Signed-off-by: Sasha Levin --- drivers/opp/core.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/opp/core.c b/drivers/opp/core.c index 53286063df55..4211cfb27350 100644 --- a/drivers/opp/core.c +++ b/drivers/opp/core.c @@ -119,6 +119,15 @@ static bool assert_clk_index(struct opp_table *opp_table, return opp_table->clk_count > index; } +/* + * Returns true if bandwidth table is large enough to contain the bandwidth index. + */ +static bool assert_bandwidth_index(struct opp_table *opp_table, + unsigned int index) +{ + return opp_table->path_count > index; +} + /** * dev_pm_opp_get_voltage() - Gets the voltage corresponding to an opp * @opp: opp for which voltage has to be returned for @@ -847,7 +856,8 @@ struct dev_pm_opp *dev_pm_opp_find_bw_ceil(struct device *dev, unsigned int *bw, unsigned long temp = *bw; struct dev_pm_opp *opp; - opp = _find_key_ceil(dev, &temp, index, true, _read_bw, NULL); + opp = _find_key_ceil(dev, &temp, index, true, _read_bw, + assert_bandwidth_index); *bw = temp; return opp; } @@ -878,7 +888,8 @@ struct dev_pm_opp *dev_pm_opp_find_bw_floor(struct device *dev, unsigned long temp = *bw; struct dev_pm_opp *opp; - opp = _find_key_floor(dev, &temp, index, true, _read_bw, NULL); + opp = _find_key_floor(dev, &temp, index, true, _read_bw, + assert_bandwidth_index); *bw = temp; return opp; }