Commit 8646f111 authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files
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
parents b320789d 05db3596
Loading
Loading
Loading
Loading
+99 −0
Original line number Diff line number Diff line
@@ -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
+30 −0
Original line number Diff line number Diff line
@@ -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)
+5 −11
Original line number Diff line number Diff line
@@ -12,7 +12,7 @@
    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 @@ pub fn set(self, dev: &Device) -> Result<ConfigToken> {
        // 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 @@ pub fn opp_count(&self) -> Result<u32> {
        // 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`].