OPP updates for 6.18

- Add support to find OPP for a set of keys (Krishna Chaitanya Chundru).
 
 - Minor optimization to OPP Rust implementation (Onur Özkan).
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEx73Crsp7f6M6scA70rkcPK6BEhwFAmi5LbAACgkQ0rkcPK6B
 EhxcTRAAhecgcxZo7kGK4xbj/QzflGTqfNGeJ93mShTBPmJt+Rjjme14HdI2sNHS
 PW6Zu84IFKWS2H9CVM72JVCq1sGnX6a+E3ciTAMC1PKXoN7UWx1jHxw2+xx55dJc
 4gK146md14Ec8llhCf2oaRfhRbcHhs6v1l/4WgD2MSsEuKEyf/E7oj+RALe2+fSc
 gCgfKjlX+JPyuMeMVqB7XkjtZ9RPrtEz2x9f1F5yD3CgZxfhH/9qvby4CLgPkF7j
 AjjF+zE6YlfUr720h1fnXhk3YE7CE/66bwukyTXLTnjz6S/mE/oFAWjganUBV5DC
 88S7FlkFyMHKV/WcMYqlU+8trcT51be4dQxzrCZm+DGBlrH9wKyYXi50VSMnpokp
 YDZgIZKzbejBBXnXYy9vfQ6qIgn2Q+zyYa2EJBjNTjsUtR9OlLP+EBlshLrdcNLz
 QhTLGbqE+B1UXcxpdCnAswLay1HR3uaDQegTcFTpKYx90b533zo0JAU3nQy3Se+c
 xLtSbSV7nUDOKXserCSK06rPvpOYbDf6OMIrYn54Lq6oIIdoQhYeXmaUjX3/OSQ9
 WI2YBeUVrscEHKw2oOB7CQak8bOzfkqy1A0TS93j/xNa+yLs7YZ+qnDGULDQadmG
 JJ6cWG0sVR0VWg4WLP1qAm7/I/673qLtNQ4Oc5Q5Igedbh1XjuU=
 =sHvS
 -----END PGP SIGNATURE-----

Merge tag 'opp-updates-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Merge OPP (operating performance points) updates for 6.18 from Viresh
Kumar:

"- Add support to find OPP for a set of keys (Krishna Chaitanya Chundru).

 - Minor optimization to OPP Rust implementation (Onur Özkan)."

* tag 'opp-updates-6.18' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  OPP: Add support to find OPP for a set of keys
  rust: opp: use to_result for error handling
This commit is contained in:
Rafael J. Wysocki 2025-09-04 20:38:46 +02:00
commit 8646f111fa
3 changed files with 134 additions and 11 deletions

View File

@ -476,6 +476,16 @@ static unsigned long _read_bw(struct dev_pm_opp *opp, int index)
return opp->bandwidth[index].peak;
}
static unsigned long _read_opp_key(struct dev_pm_opp *opp, int index,
struct dev_pm_opp_key *key)
{
key->bw = opp->bandwidth ? opp->bandwidth[index].peak : 0;
key->freq = opp->rates[index];
key->level = opp->level;
return true;
}
/* Generic comparison helpers */
static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
unsigned long opp_key, unsigned long key)
@ -509,6 +519,22 @@ static bool _compare_floor(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
return false;
}
static bool _compare_opp_key_exact(struct dev_pm_opp **opp,
struct dev_pm_opp *temp_opp, struct dev_pm_opp_key *opp_key,
struct dev_pm_opp_key *key)
{
bool level_match = (key->level == OPP_LEVEL_UNSET || opp_key->level == key->level);
bool freq_match = (key->freq == 0 || opp_key->freq == key->freq);
bool bw_match = (key->bw == 0 || opp_key->bw == key->bw);
if (freq_match && level_match && bw_match) {
*opp = temp_opp;
return true;
}
return false;
}
/* Generic key finding helpers */
static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table,
unsigned long *key, int index, bool available,
@ -541,6 +567,37 @@ static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table,
return opp;
}
static struct dev_pm_opp *_opp_table_find_opp_key(struct opp_table *opp_table,
struct dev_pm_opp_key *key, bool available,
unsigned long (*read)(struct dev_pm_opp *opp, int index,
struct dev_pm_opp_key *key),
bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
struct dev_pm_opp_key *opp_key, struct dev_pm_opp_key *key),
bool (*assert)(struct opp_table *opp_table, unsigned int index))
{
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
struct dev_pm_opp_key temp_key;
/* Assert that the requirement is met */
if (!assert(opp_table, 0))
return ERR_PTR(-EINVAL);
guard(mutex)(&opp_table->lock);
list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
if (temp_opp->available == available) {
read(temp_opp, 0, &temp_key);
if (compare(&opp, temp_opp, &temp_key, key)) {
/* Increment the reference count of OPP */
dev_pm_opp_get(opp);
break;
}
}
}
return opp;
}
static struct dev_pm_opp *
_find_key(struct device *dev, unsigned long *key, int index, bool available,
unsigned long (*read)(struct dev_pm_opp *opp, int index),
@ -632,6 +689,48 @@ 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_key_exact() - Search for an OPP with exact key set
* @dev: Device for which the OPP is being searched
* @key: OPP key set to match
* @available: true/false - match for available OPP
*
* Search for an exact match of the key set in the OPP table.
*
* Return: A matching opp on success, else ERR_PTR in case of error.
* Possible error values:
* EINVAL: for bad pointers
* ERANGE: no match found for search
* ENODEV: if device not found in list of registered devices
*
* Note: 'available' is a modifier for the search. If 'available' == true,
* then the match is for exact matching key and is available in the stored
* OPP table. If false, the match is for exact key which is not available.
*
* This provides a mechanism to enable an OPP which is not available currently
* or the opposite as well.
*
* The callers are required to call dev_pm_opp_put() for the returned OPP after
* use.
*/
struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
struct dev_pm_opp_key *key,
bool available)
{
struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev);
if (IS_ERR(opp_table)) {
dev_err(dev, "%s: OPP table not found (%ld)\n", __func__,
PTR_ERR(opp_table));
return ERR_CAST(opp_table);
}
return _opp_table_find_opp_key(opp_table, key, available,
_read_opp_key, _compare_opp_key_exact,
assert_single_clk);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_key_exact);
/**
* dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the
* clock corresponding to the index

View File

@ -98,6 +98,25 @@ struct dev_pm_opp_data {
unsigned long u_volt;
};
/**
* struct dev_pm_opp_key - Key used to identify OPP entries
* @freq: Frequency in Hz. Use 0 if frequency is not to be matched.
* @level: Performance level associated with the OPP entry.
* Use OPP_LEVEL_UNSET if level is not to be matched.
* @bw: Bandwidth associated with the OPP entry.
* Use 0 if bandwidth is not to be matched.
*
* This structure is used to uniquely identify an OPP entry based on
* frequency, performance level, and bandwidth. Each field can be
* selectively ignored during matching by setting it to its respective
* NOP value.
*/
struct dev_pm_opp_key {
unsigned long freq;
unsigned int level;
u32 bw;
};
#if defined(CONFIG_PM_OPP)
struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
@ -131,6 +150,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_key_exact(struct device *dev,
struct dev_pm_opp_key *key,
bool available);
struct dev_pm_opp *
dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq,
u32 index, bool available);
@ -289,6 +312,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_key_exact(struct device *dev,
struct dev_pm_opp_key *key,
bool available)
{
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)

View File

@ -12,7 +12,7 @@ use crate::{
clk::Hertz,
cpumask::{Cpumask, CpumaskVar},
device::Device,
error::{code::*, from_err_ptr, from_result, to_result, Error, Result, VTABLE_DEFAULT_ERROR},
error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR},
ffi::c_ulong,
prelude::*,
str::CString,
@ -500,11 +500,8 @@ impl<T: ConfigOps + Default> Config<T> {
// requirements. The OPP core guarantees not to access fields of [`Config`] after this call
// and so we don't need to save a copy of them for future use.
let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) };
if ret < 0 {
Err(Error::from_errno(ret))
} else {
Ok(ConfigToken(ret))
}
to_result(ret).map(|()| ConfigToken(ret))
}
/// Config's clk callback.
@ -713,11 +710,8 @@ impl Table {
// SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety
// requirements.
let ret = unsafe { bindings::dev_pm_opp_get_opp_count(self.dev.as_raw()) };
if ret < 0 {
Err(Error::from_errno(ret))
} else {
Ok(ret as u32)
}
to_result(ret).map(|()| ret as u32)
}
/// Returns max clock latency (in nanoseconds) of the [`OPP`]s in the [`Table`].