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

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Krishna Chaitanya Chundru and committed by
Viresh Kumar
05db3596 22763c35

+129
+99
drivers/opp/core.c
··· 476 476 return opp->bandwidth[index].peak; 477 477 } 478 478 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 + 479 489 /* Generic comparison helpers */ 480 490 static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, 481 491 unsigned long opp_key, unsigned long key) ··· 519 509 return false; 520 510 } 521 511 512 + static bool _compare_opp_key_exact(struct dev_pm_opp **opp, 513 + struct dev_pm_opp *temp_opp, struct dev_pm_opp_key *opp_key, 514 + struct dev_pm_opp_key *key) 515 + { 516 + bool level_match = (key->level == OPP_LEVEL_UNSET || opp_key->level == key->level); 517 + bool freq_match = (key->freq == 0 || opp_key->freq == key->freq); 518 + bool bw_match = (key->bw == 0 || opp_key->bw == key->bw); 519 + 520 + if (freq_match && level_match && bw_match) { 521 + *opp = temp_opp; 522 + return true; 523 + } 524 + 525 + return false; 526 + } 527 + 522 528 /* Generic key finding helpers */ 523 529 static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table, 524 530 unsigned long *key, int index, bool available, ··· 562 536 if (!IS_ERR(opp)) { 563 537 *key = read(opp, index); 564 538 dev_pm_opp_get(opp); 539 + } 540 + 541 + return opp; 542 + } 543 + 544 + static struct dev_pm_opp *_opp_table_find_opp_key(struct opp_table *opp_table, 545 + struct dev_pm_opp_key *key, bool available, 546 + unsigned long (*read)(struct dev_pm_opp *opp, int index, 547 + struct dev_pm_opp_key *key), 548 + bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp, 549 + struct dev_pm_opp_key *opp_key, struct dev_pm_opp_key *key), 550 + bool (*assert)(struct opp_table *opp_table, unsigned int index)) 551 + { 552 + struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE); 553 + struct dev_pm_opp_key temp_key; 554 + 555 + /* Assert that the requirement is met */ 556 + if (!assert(opp_table, 0)) 557 + return ERR_PTR(-EINVAL); 558 + 559 + guard(mutex)(&opp_table->lock); 560 + 561 + list_for_each_entry(temp_opp, &opp_table->opp_list, node) { 562 + if (temp_opp->available == available) { 563 + read(temp_opp, 0, &temp_key); 564 + if (compare(&opp, temp_opp, &temp_key, key)) { 565 + /* Increment the reference count of OPP */ 566 + dev_pm_opp_get(opp); 567 + break; 568 + } 569 + } 565 570 } 566 571 567 572 return opp; ··· 688 631 assert_single_clk); 689 632 } 690 633 EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact); 634 + 635 + /** 636 + * dev_pm_opp_find_key_exact() - Search for an OPP with exact key set 637 + * @dev: Device for which the OPP is being searched 638 + * @key: OPP key set to match 639 + * @available: true/false - match for available OPP 640 + * 641 + * Search for an exact match of the key set in the OPP table. 642 + * 643 + * Return: A matching opp on success, else ERR_PTR in case of error. 644 + * Possible error values: 645 + * EINVAL: for bad pointers 646 + * ERANGE: no match found for search 647 + * ENODEV: if device not found in list of registered devices 648 + * 649 + * Note: 'available' is a modifier for the search. If 'available' == true, 650 + * then the match is for exact matching key and is available in the stored 651 + * OPP table. If false, the match is for exact key which is not available. 652 + * 653 + * This provides a mechanism to enable an OPP which is not available currently 654 + * or the opposite as well. 655 + * 656 + * The callers are required to call dev_pm_opp_put() for the returned OPP after 657 + * use. 658 + */ 659 + struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, 660 + struct dev_pm_opp_key *key, 661 + bool available) 662 + { 663 + struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev); 664 + 665 + if (IS_ERR(opp_table)) { 666 + dev_err(dev, "%s: OPP table not found (%ld)\n", __func__, 667 + PTR_ERR(opp_table)); 668 + return ERR_CAST(opp_table); 669 + } 670 + 671 + return _opp_table_find_opp_key(opp_table, key, available, 672 + _read_opp_key, _compare_opp_key_exact, 673 + assert_single_clk); 674 + } 675 + EXPORT_SYMBOL_GPL(dev_pm_opp_find_key_exact); 691 676 692 677 /** 693 678 * dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the
+30
include/linux/pm_opp.h
··· 98 98 unsigned long u_volt; 99 99 }; 100 100 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 + 101 120 #if defined(CONFIG_PM_OPP) 102 121 103 122 struct opp_table *dev_pm_opp_get_opp_table(struct device *dev); ··· 149 130 struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, 150 131 unsigned long freq, 151 132 bool available); 133 + 134 + struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, 135 + struct dev_pm_opp_key *key, 136 + bool available); 152 137 153 138 struct dev_pm_opp * 154 139 dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq, ··· 308 285 309 286 static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev, 310 287 unsigned long freq, bool available) 288 + { 289 + return ERR_PTR(-EOPNOTSUPP); 290 + } 291 + 292 + static inline struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev, 293 + struct dev_pm_opp_key *key, 294 + bool available) 311 295 { 312 296 return ERR_PTR(-EOPNOTSUPP); 313 297 }