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()
This commit is contained in:
Sudeep Holla 2023-10-06 09:40:10 +01:00
commit 0a30c0e9ca
8 changed files with 131 additions and 34 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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);
};
/*

View File

@ -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 */

View File

@ -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[])