Skip to content

Commit 05db359

Browse files
krishnachaitanya-linuxvireshk
authored andcommitted
OPP: Add support to find OPP for a set of keys
Some clients, such as PCIe, may operate at the same clock frequency across different data rates by varying link width. In such cases, frequency alone is not sufficient to uniquely identify an OPP. To support these scenarios, introduce a new API dev_pm_opp_find_key_exact() that allows OPP lookup with different set of keys like freq, level & bandwidth. Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com> [ Viresh: Minor cleanups ] Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
1 parent 22763c3 commit 05db359

2 files changed

Lines changed: 129 additions & 0 deletions

File tree

drivers/opp/core.c

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,6 +476,16 @@ static unsigned long _read_bw(struct dev_pm_opp *opp, int index)
476476
return opp->bandwidth[index].peak;
477477
}
478478

479+
static unsigned long _read_opp_key(struct dev_pm_opp *opp, int index,
480+
struct dev_pm_opp_key *key)
481+
{
482+
key->bw = opp->bandwidth ? opp->bandwidth[index].peak : 0;
483+
key->freq = opp->rates[index];
484+
key->level = opp->level;
485+
486+
return true;
487+
}
488+
479489
/* Generic comparison helpers */
480490
static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
481491
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,
509519
return false;
510520
}
511521

522+
static bool _compare_opp_key_exact(struct dev_pm_opp **opp,
523+
struct dev_pm_opp *temp_opp, struct dev_pm_opp_key *opp_key,
524+
struct dev_pm_opp_key *key)
525+
{
526+
bool level_match = (key->level == OPP_LEVEL_UNSET || opp_key->level == key->level);
527+
bool freq_match = (key->freq == 0 || opp_key->freq == key->freq);
528+
bool bw_match = (key->bw == 0 || opp_key->bw == key->bw);
529+
530+
if (freq_match && level_match && bw_match) {
531+
*opp = temp_opp;
532+
return true;
533+
}
534+
535+
return false;
536+
}
537+
512538
/* Generic key finding helpers */
513539
static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table,
514540
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,
541567
return opp;
542568
}
543569

570+
static struct dev_pm_opp *_opp_table_find_opp_key(struct opp_table *opp_table,
571+
struct dev_pm_opp_key *key, bool available,
572+
unsigned long (*read)(struct dev_pm_opp *opp, int index,
573+
struct dev_pm_opp_key *key),
574+
bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
575+
struct dev_pm_opp_key *opp_key, struct dev_pm_opp_key *key),
576+
bool (*assert)(struct opp_table *opp_table, unsigned int index))
577+
{
578+
struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
579+
struct dev_pm_opp_key temp_key;
580+
581+
/* Assert that the requirement is met */
582+
if (!assert(opp_table, 0))
583+
return ERR_PTR(-EINVAL);
584+
585+
guard(mutex)(&opp_table->lock);
586+
587+
list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
588+
if (temp_opp->available == available) {
589+
read(temp_opp, 0, &temp_key);
590+
if (compare(&opp, temp_opp, &temp_key, key)) {
591+
/* Increment the reference count of OPP */
592+
dev_pm_opp_get(opp);
593+
break;
594+
}
595+
}
596+
}
597+
598+
return opp;
599+
}
600+
544601
static struct dev_pm_opp *
545602
_find_key(struct device *dev, unsigned long *key, int index, bool available,
546603
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,
632689
}
633690
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);
634691

692+
/**
693+
* dev_pm_opp_find_key_exact() - Search for an OPP with exact key set
694+
* @dev: Device for which the OPP is being searched
695+
* @key: OPP key set to match
696+
* @available: true/false - match for available OPP
697+
*
698+
* Search for an exact match of the key set in the OPP table.
699+
*
700+
* Return: A matching opp on success, else ERR_PTR in case of error.
701+
* Possible error values:
702+
* EINVAL: for bad pointers
703+
* ERANGE: no match found for search
704+
* ENODEV: if device not found in list of registered devices
705+
*
706+
* Note: 'available' is a modifier for the search. If 'available' == true,
707+
* then the match is for exact matching key and is available in the stored
708+
* OPP table. If false, the match is for exact key which is not available.
709+
*
710+
* This provides a mechanism to enable an OPP which is not available currently
711+
* or the opposite as well.
712+
*
713+
* The callers are required to call dev_pm_opp_put() for the returned OPP after
714+
* use.
715+
*/
716+
struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
717+
struct dev_pm_opp_key *key,
718+
bool available)
719+
{
720+
struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev);
721+
722+
if (IS_ERR(opp_table)) {
723+
dev_err(dev, "%s: OPP table not found (%ld)\n", __func__,
724+
PTR_ERR(opp_table));
725+
return ERR_CAST(opp_table);
726+
}
727+
728+
return _opp_table_find_opp_key(opp_table, key, available,
729+
_read_opp_key, _compare_opp_key_exact,
730+
assert_single_clk);
731+
}
732+
EXPORT_SYMBOL_GPL(dev_pm_opp_find_key_exact);
733+
635734
/**
636735
* dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the
637736
* clock corresponding to the index

include/linux/pm_opp.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,25 @@ struct dev_pm_opp_data {
9898
unsigned long u_volt;
9999
};
100100

101+
/**
102+
* struct dev_pm_opp_key - Key used to identify OPP entries
103+
* @freq: Frequency in Hz. Use 0 if frequency is not to be matched.
104+
* @level: Performance level associated with the OPP entry.
105+
* Use OPP_LEVEL_UNSET if level is not to be matched.
106+
* @bw: Bandwidth associated with the OPP entry.
107+
* Use 0 if bandwidth is not to be matched.
108+
*
109+
* This structure is used to uniquely identify an OPP entry based on
110+
* frequency, performance level, and bandwidth. Each field can be
111+
* selectively ignored during matching by setting it to its respective
112+
* NOP value.
113+
*/
114+
struct dev_pm_opp_key {
115+
unsigned long freq;
116+
unsigned int level;
117+
u32 bw;
118+
};
119+
101120
#if defined(CONFIG_PM_OPP)
102121

103122
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,
131150
unsigned long freq,
132151
bool available);
133152

153+
struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
154+
struct dev_pm_opp_key *key,
155+
bool available);
156+
134157
struct dev_pm_opp *
135158
dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq,
136159
u32 index, bool available);
@@ -289,6 +312,13 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
289312
return ERR_PTR(-EOPNOTSUPP);
290313
}
291314

315+
static inline struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
316+
struct dev_pm_opp_key *key,
317+
bool available)
318+
{
319+
return ERR_PTR(-EOPNOTSUPP);
320+
}
321+
292322
static inline struct dev_pm_opp *
293323
dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq,
294324
u32 index, bool available)

0 commit comments

Comments
 (0)