[PATCH 13/16] OPP: Extend dev_pm_opp_data with OPP provider support

From: Ulf Hansson
Date: Wed Jun 07 2023 - 08:47:54 EST


To allow a dynamically added OPP to be coupled with a specific OPP provider
type, let's add a new enum variable in the struct dev_pm_opp_data.
Moreover, let's add support for a DEV_PM_OPP_TYPE_GENPD type, corresponding
to genpd's performance states support.

More precisely, this allows a genpd provider to dynamically add OPPs when a
device gets attached to it, that later can be used by a consumer driver
when it needs to change the performance level for its corresponding device.

Signed-off-by: Ulf Hansson <ulf.hansson@xxxxxxxxxx>
---
drivers/opp/core.c | 19 +++++++++++++++++++
drivers/opp/opp.h | 1 +
include/linux/pm_opp.h | 7 +++++++
3 files changed, 27 insertions(+)

diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 79b4b44ced3e..81a3418e2eaf 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1112,6 +1112,15 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table,
return ret;
}

+ if (opp->provider == DEV_PM_OPP_TYPE_GENPD) {
+ ret = dev_pm_genpd_set_performance_state(dev, opp->level);
+ if (ret) {
+ dev_err(dev, "Failed to set performance level: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
ret = _set_opp_bw(opp_table, opp, dev);
if (ret) {
dev_err(dev, "Failed to set bw: %d\n", ret);
@@ -1155,6 +1164,15 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table,
return ret;
}

+ if (opp->provider == DEV_PM_OPP_TYPE_GENPD) {
+ ret = dev_pm_genpd_set_performance_state(dev, opp->level);
+ if (ret) {
+ dev_err(dev, "Failed to set performance level: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
ret = _set_required_opps(dev, opp_table, opp, false);
if (ret) {
dev_err(dev, "Failed to set required opps: %d\n", ret);
@@ -1955,6 +1973,7 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
/* populate the opp table */
new_opp->rates[0] = opp->freq;
new_opp->level = opp->level;
+ new_opp->provider = opp->provider;
tol = u_volt * opp_table->voltage_tolerance_v1 / 100;
new_opp->supplies[0].u_volt = u_volt;
new_opp->supplies[0].u_volt_min = u_volt - tol;
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h
index b15770b2305e..ee2b3bd89213 100644
--- a/drivers/opp/opp.h
+++ b/drivers/opp/opp.h
@@ -104,6 +104,7 @@ struct dev_pm_opp {
unsigned int pstate;
unsigned long *rates;
unsigned int level;
+ enum dev_pm_opp_provider_type provider;

struct dev_pm_opp_supply *supplies;
struct dev_pm_opp_icc_bw *bandwidth;
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 2c6f67736579..4c40199c7728 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -26,6 +26,11 @@ enum dev_pm_opp_event {
OPP_EVENT_ADJUST_VOLTAGE,
};

+enum dev_pm_opp_provider_type {
+ DEV_PM_OPP_TYPE_NONE = 0,
+ DEV_PM_OPP_TYPE_GENPD,
+};
+
/**
* struct dev_pm_opp_supply - Power supply voltage/current values
* @u_volt: Target voltage in microvolts corresponding to this OPP
@@ -97,11 +102,13 @@ struct dev_pm_opp_config {
* @level: The performance level for the OPP.
* @freq: The clock rate in Hz for the OPP.
* @u_volt: The voltage in uV for the OPP.
+ * @provider: The type of provider for the OPP.
*/
struct dev_pm_opp_data {
unsigned int level;
unsigned long freq;
unsigned long u_volt;
+ enum dev_pm_opp_provider_type provider;
};

#if defined(CONFIG_PM_OPP)
--
2.34.1