summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSudeep Holla <sudeep.holla@arm.com>2023-10-06 09:40:10 +0100
committerSudeep Holla <sudeep.holla@arm.com>2023-10-06 09:40:10 +0100
commit0a30c0e9ca293a6680b1a124684cf80c142b1fa0 (patch)
tree13164e8813405e625155bd63b2d038e8c55a40e4
parent8b6022be4c6e3e0d37c3e1378c9ff0a2c8717b09 (diff)
parent0025ff64ffcf6bd6ece5484e7818401f77bf115f (diff)
Merge branch 'opp/pm-domain-scmi' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm into for-next/scmi/updates
This is the merge of immutable point in PM OPP tree shared with SCMI so that the SCMI changes based on these OPP changes can be merged via the SCMI tree. * 'opp/pm-domain-scmi' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: OPP: Extend support for the opp-level beyond required-opps OPP: Switch to use dev_pm_domain_set_performance_state() OPP: Extend dev_pm_opp_data with a level OPP: Add dev_pm_opp_add_dynamic() to allow more flexibility PM: domains: Implement the ->set_performance_state() callback for genpd PM: domains: Introduce dev_pm_domain_set_performance_state()
-rw-r--r--drivers/base/power/common.c21
-rw-r--r--drivers/base/power/domain.c33
-rw-r--r--drivers/opp/core.c60
-rw-r--r--drivers/opp/of.c10
-rw-r--r--drivers/opp/opp.h2
-rw-r--r--include/linux/pm.h2
-rw-r--r--include/linux/pm_domain.h6
-rw-r--r--include/linux/pm_opp.h31
8 files changed, 131 insertions, 34 deletions
diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 72115917e0bd..44ec20918a4d 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -228,3 +228,24 @@ void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd)
device_pm_check_callbacks(dev);
}
EXPORT_SYMBOL_GPL(dev_pm_domain_set);
+
+/**
+ * dev_pm_domain_set_performance_state - Request a new performance state.
+ * @dev: The device to make the request for.
+ * @state: Target performance state for the device.
+ *
+ * This function should be called when a new performance state needs to be
+ * requested for a device that is attached to a PM domain. Note that, the
+ * support for performance scaling for PM domains is optional.
+ *
+ * Returns 0 on success and when performance scaling isn't supported, negative
+ * error code on failure.
+ */
+int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state)
+{
+ if (dev->pm_domain && dev->pm_domain->set_performance_state)
+ return dev->pm_domain->set_performance_state(dev, state);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_set_performance_state);
diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
index c74edf80417f..da1777e39eaa 100644
--- a/drivers/base/power/domain.c
+++ b/drivers/base/power/domain.c
@@ -420,6 +420,25 @@ static void genpd_restore_performance_state(struct device *dev,
genpd_set_performance_state(dev, state);
}
+static int genpd_dev_pm_set_performance_state(struct device *dev,
+ unsigned int state)
+{
+ struct generic_pm_domain *genpd = dev_to_genpd(dev);
+ int ret = 0;
+
+ genpd_lock(genpd);
+ if (pm_runtime_suspended(dev)) {
+ dev_gpd_data(dev)->rpm_pstate = state;
+ } else {
+ ret = genpd_set_performance_state(dev, state);
+ if (!ret)
+ dev_gpd_data(dev)->rpm_pstate = 0;
+ }
+ genpd_unlock(genpd);
+
+ return ret;
+}
+
/**
* dev_pm_genpd_set_performance_state- Set performance state of device's power
* domain.
@@ -438,7 +457,6 @@ static void genpd_restore_performance_state(struct device *dev,
int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
{
struct generic_pm_domain *genpd;
- int ret = 0;
genpd = dev_to_genpd_safe(dev);
if (!genpd)
@@ -448,17 +466,7 @@ int dev_pm_genpd_set_performance_state(struct device *dev, unsigned int state)
!dev->power.subsys_data->domain_data))
return -EINVAL;
- genpd_lock(genpd);
- if (pm_runtime_suspended(dev)) {
- dev_gpd_data(dev)->rpm_pstate = state;
- } else {
- ret = genpd_set_performance_state(dev, state);
- if (!ret)
- dev_gpd_data(dev)->rpm_pstate = 0;
- }
- genpd_unlock(genpd);
-
- return ret;
+ return genpd_dev_pm_set_performance_state(dev, state);
}
EXPORT_SYMBOL_GPL(dev_pm_genpd_set_performance_state);
@@ -2080,6 +2088,7 @@ int pm_genpd_init(struct generic_pm_domain *genpd,
genpd->domain.ops.restore_noirq = genpd_restore_noirq;
genpd->domain.ops.complete = genpd_complete;
genpd->domain.start = genpd_dev_pm_start;
+ genpd->domain.set_performance_state = genpd_dev_pm_set_performance_state;
if (genpd->flags & GENPD_FLAG_PM_CLK) {
genpd->dev_ops.stop = pm_clk_suspend;
diff --git a/drivers/opp/core.c b/drivers/opp/core.c
index 919cc53bc02e..8978e94c9ca1 100644
--- a/drivers/opp/core.c
+++ b/drivers/opp/core.c
@@ -1030,7 +1030,7 @@ static int _set_performance_state(struct device *dev, struct device *pd_dev,
if (!pd_dev)
return 0;
- ret = dev_pm_genpd_set_performance_state(pd_dev, pstate);
+ ret = dev_pm_domain_set_performance_state(pd_dev, pstate);
if (ret) {
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
dev_name(pd_dev), pstate, ret);
@@ -1107,6 +1107,28 @@ void _update_set_required_opps(struct opp_table *opp_table)
opp_table->set_required_opps = _opp_set_required_opps_generic;
}
+static int _set_opp_level(struct device *dev, struct opp_table *opp_table,
+ struct dev_pm_opp *opp)
+{
+ unsigned int level = 0;
+ int ret = 0;
+
+ if (opp) {
+ if (!opp->level)
+ return 0;
+
+ level = opp->level;
+ }
+
+ /* Request a new performance state through the device's PM domain. */
+ ret = dev_pm_domain_set_performance_state(dev, level);
+ if (ret)
+ dev_err(dev, "Failed to set performance state %u (%d)\n", level,
+ ret);
+
+ return ret;
+}
+
static void _find_current_opp(struct device *dev, struct opp_table *opp_table)
{
struct dev_pm_opp *opp = ERR_PTR(-ENODEV);
@@ -1154,8 +1176,13 @@ static int _disable_opp_table(struct device *dev, struct opp_table *opp_table)
if (opp_table->regulators)
regulator_disable(opp_table->regulators[0]);
+ ret = _set_opp_level(dev, opp_table, NULL);
+ if (ret)
+ goto out;
+
ret = _set_required_opps(dev, opp_table, NULL, false);
+out:
opp_table->enabled = false;
return ret;
}
@@ -1198,6 +1225,10 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table,
return ret;
}
+ ret = _set_opp_level(dev, opp_table, opp);
+ if (ret)
+ return ret;
+
ret = _set_opp_bw(opp_table, opp, dev);
if (ret) {
dev_err(dev, "Failed to set bw: %d\n", ret);
@@ -1241,6 +1272,10 @@ static int _set_opp(struct device *dev, struct opp_table *opp_table,
return ret;
}
+ ret = _set_opp_level(dev, opp_table, opp);
+ if (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);
@@ -2002,8 +2037,7 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
* _opp_add_v1() - Allocate a OPP based on v1 bindings.
* @opp_table: OPP table
* @dev: device for which we do this operation
- * @freq: Frequency in Hz for this OPP
- * @u_volt: Voltage in uVolts for this OPP
+ * @data: The OPP data for the OPP to add
* @dynamic: Dynamically added OPPs.
*
* This function adds an opp definition to the opp table and returns status.
@@ -2021,10 +2055,10 @@ int _opp_add(struct device *dev, struct dev_pm_opp *new_opp,
* -ENOMEM Memory allocation failure
*/
int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
- unsigned long freq, long u_volt, bool dynamic)
+ struct dev_pm_opp_data *data, bool dynamic)
{
struct dev_pm_opp *new_opp;
- unsigned long tol;
+ unsigned long tol, u_volt = data->u_volt;
int ret;
if (!assert_single_clk(opp_table))
@@ -2035,7 +2069,8 @@ int _opp_add_v1(struct opp_table *opp_table, struct device *dev,
return -ENOMEM;
/* populate the opp table */
- new_opp->rates[0] = freq;
+ new_opp->rates[0] = data->freq;
+ new_opp->level = data->level;
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;
@@ -2825,10 +2860,9 @@ unlock:
}
/**
- * dev_pm_opp_add() - Add an OPP table from a table definitions
- * @dev: device for which we do this operation
- * @freq: Frequency in Hz for this OPP
- * @u_volt: Voltage in uVolts for this OPP
+ * dev_pm_opp_add_dynamic() - Add an OPP table from a table definitions
+ * @dev: The device for which we do this operation
+ * @data: The OPP data for the OPP to add
*
* This function adds an opp definition to the opp table and returns status.
* The opp is made available by default and it can be controlled using
@@ -2841,7 +2875,7 @@ unlock:
* Duplicate OPPs (both freq and volt are same) and !opp->available
* -ENOMEM Memory allocation failure
*/
-int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
+int dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *data)
{
struct opp_table *opp_table;
int ret;
@@ -2853,13 +2887,13 @@ int dev_pm_opp_add(struct device *dev, unsigned long freq, unsigned long u_volt)
/* Fix regulator count for dynamic OPPs */
opp_table->regulator_count = 1;
- ret = _opp_add_v1(opp_table, dev, freq, u_volt, true);
+ ret = _opp_add_v1(opp_table, dev, data, true);
if (ret)
dev_pm_opp_put_opp_table(opp_table);
return ret;
}
-EXPORT_SYMBOL_GPL(dev_pm_opp_add);
+EXPORT_SYMBOL_GPL(dev_pm_opp_add_dynamic);
/**
* _opp_set_availability() - helper to set the availability of an opp
diff --git a/drivers/opp/of.c b/drivers/opp/of.c
index ada4963c7cfa..ade6d42cae46 100644
--- a/drivers/opp/of.c
+++ b/drivers/opp/of.c
@@ -1077,13 +1077,15 @@ static int _of_add_opp_table_v1(struct device *dev, struct opp_table *opp_table)
val = prop->value;
while (nr) {
- unsigned long freq = be32_to_cpup(val++) * 1000;
- unsigned long volt = be32_to_cpup(val++);
+ struct dev_pm_opp_data data = {
+ .freq = be32_to_cpup(val++) * 1000,
+ .u_volt = be32_to_cpup(val++),
+ };
- ret = _opp_add_v1(opp_table, dev, freq, volt, false);
+ ret = _opp_add_v1(opp_table, dev, &data, false);
if (ret) {
dev_err(dev, "%s: Failed to add OPP %ld (%d)\n",
- __func__, freq, ret);
+ __func__, data.freq, ret);
goto remove_static_opp;
}
nr -= 2;
diff --git a/drivers/opp/opp.h b/drivers/opp/opp.h
index 8a5ea38f3a3d..fefdf9845692 100644
--- a/drivers/opp/opp.h
+++ b/drivers/opp/opp.h
@@ -251,7 +251,7 @@ struct dev_pm_opp *_opp_allocate(struct opp_table *opp_table);
void _opp_free(struct dev_pm_opp *opp);
int _opp_compare_key(struct opp_table *opp_table, struct dev_pm_opp *opp1, struct dev_pm_opp *opp2);
int _opp_add(struct device *dev, struct dev_pm_opp *new_opp, struct opp_table *opp_table);
-int _opp_add_v1(struct opp_table *opp_table, struct device *dev, unsigned long freq, long u_volt, bool dynamic);
+int _opp_add_v1(struct opp_table *opp_table, struct device *dev, struct dev_pm_opp_data *data, bool dynamic);
void _dev_pm_opp_cpumask_remove_table(const struct cpumask *cpumask, int last_cpu);
struct opp_table *_add_opp_table_indexed(struct device *dev, int index, bool getclk);
void _put_opp_list_kref(struct opp_table *opp_table);
diff --git a/include/linux/pm.h b/include/linux/pm.h
index 1400c37b29c7..4c9f571609c8 100644
--- a/include/linux/pm.h
+++ b/include/linux/pm.h
@@ -719,6 +719,7 @@ extern void dev_pm_put_subsys_data(struct device *dev);
* @activate: Called before executing probe routines for bus types and drivers.
* @sync: Called after successful driver probe.
* @dismiss: Called after unsuccessful driver probe and after driver removal.
+ * @set_performance_state: Called to request a new performance state.
*
* Power domains provide callbacks that are executed during system suspend,
* hibernation, system resume and during runtime PM transitions instead of
@@ -731,6 +732,7 @@ struct dev_pm_domain {
int (*activate)(struct device *dev);
void (*sync)(struct device *dev);
void (*dismiss)(struct device *dev);
+ int (*set_performance_state)(struct device *dev, unsigned int state);
};
/*
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index 05ad8cefdff1..34663d0d5c55 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -435,6 +435,7 @@ struct device *dev_pm_domain_attach_by_name(struct device *dev,
void dev_pm_domain_detach(struct device *dev, bool power_off);
int dev_pm_domain_start(struct device *dev);
void dev_pm_domain_set(struct device *dev, struct dev_pm_domain *pd);
+int dev_pm_domain_set_performance_state(struct device *dev, unsigned int state);
#else
static inline int dev_pm_domain_attach(struct device *dev, bool power_on)
{
@@ -457,6 +458,11 @@ static inline int dev_pm_domain_start(struct device *dev)
}
static inline void dev_pm_domain_set(struct device *dev,
struct dev_pm_domain *pd) {}
+static inline int dev_pm_domain_set_performance_state(struct device *dev,
+ unsigned int state)
+{
+ return 0;
+}
#endif
#endif /* _LINUX_PM_DOMAIN_H */
diff --git a/include/linux/pm_opp.h b/include/linux/pm_opp.h
index 91f87d7e807c..9ad168f4cbf1 100644
--- a/include/linux/pm_opp.h
+++ b/include/linux/pm_opp.h
@@ -92,6 +92,18 @@ struct dev_pm_opp_config {
struct device ***virt_devs;
};
+/**
+ * struct dev_pm_opp_data - The data to use to initialize an OPP.
+ * @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.
+ */
+struct dev_pm_opp_data {
+ unsigned int level;
+ unsigned long freq;
+ unsigned long u_volt;
+};
+
#if defined(CONFIG_PM_OPP)
struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
@@ -152,8 +164,8 @@ struct dev_pm_opp *dev_pm_opp_find_bw_floor(struct device *dev,
void dev_pm_opp_put(struct dev_pm_opp *opp);
-int dev_pm_opp_add(struct device *dev, unsigned long freq,
- unsigned long u_volt);
+int dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *opp);
+
void dev_pm_opp_remove(struct device *dev, unsigned long freq);
void dev_pm_opp_remove_all_dynamic(struct device *dev);
@@ -322,8 +334,8 @@ static inline struct dev_pm_opp *dev_pm_opp_find_bw_floor(struct device *dev,
static inline void dev_pm_opp_put(struct dev_pm_opp *opp) {}
-static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,
- unsigned long u_volt)
+static inline int
+dev_pm_opp_add_dynamic(struct device *dev, struct dev_pm_opp_data *opp)
{
return -EOPNOTSUPP;
}
@@ -519,6 +531,17 @@ static inline int dev_pm_opp_of_find_icc_paths(struct device *dev, struct opp_ta
/* OPP Configuration helpers */
+static inline int dev_pm_opp_add(struct device *dev, unsigned long freq,
+ unsigned long u_volt)
+{
+ struct dev_pm_opp_data data = {
+ .freq = freq,
+ .u_volt = u_volt,
+ };
+
+ return dev_pm_opp_add_dynamic(dev, &data);
+}
+
/* Regulators helpers */
static inline int dev_pm_opp_set_regulators(struct device *dev,
const char * const names[])