mirror of
https://github.com/torvalds/linux.git
synced 2024-11-22 12:11:40 +00:00
Merge branch 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm
Pull operating performance points (OPP) framework changes for v5.3 from Viresh Kumar: "This pull request contains: - OPP core changes to support a wider range of devices, like IO devices (Rajendra Nayak and Stehpen Boyd). - Fixes around genpd_virt_devs (Viresh Kumar). - Fix for platform with set_opp() callback (Dmitry Osipenko)." * 'opp/linux-next' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm: opp: Don't use IS_ERR on invalid supplies opp: Make dev_pm_opp_set_rate() handle freq = 0 to drop performance votes opp: Don't overwrite rounded clk rate opp: Allocate genpd_virt_devs from dev_pm_opp_attach_genpd() opp: Attach genpds to devices from within OPP core
This commit is contained in:
commit
41de256b6f
@ -682,7 +682,7 @@ static int _set_opp_custom(const struct opp_table *opp_table,
|
||||
|
||||
data->old_opp.rate = old_freq;
|
||||
size = sizeof(*old_supply) * opp_table->regulator_count;
|
||||
if (IS_ERR(old_supply))
|
||||
if (!old_supply)
|
||||
memset(data->old_opp.supplies, 0, size);
|
||||
else
|
||||
memcpy(data->old_opp.supplies, old_supply, size);
|
||||
@ -708,7 +708,7 @@ static int _set_required_opps(struct device *dev,
|
||||
|
||||
/* Single genpd case */
|
||||
if (!genpd_virt_devs) {
|
||||
pstate = opp->required_opps[0]->pstate;
|
||||
pstate = likely(opp) ? opp->required_opps[0]->pstate : 0;
|
||||
ret = dev_pm_genpd_set_performance_state(dev, pstate);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set performance state of %s: %d (%d)\n",
|
||||
@ -726,7 +726,7 @@ static int _set_required_opps(struct device *dev,
|
||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
for (i = 0; i < opp_table->required_opp_count; i++) {
|
||||
pstate = opp->required_opps[i]->pstate;
|
||||
pstate = likely(opp) ? opp->required_opps[i]->pstate : 0;
|
||||
|
||||
if (!genpd_virt_devs[i])
|
||||
continue;
|
||||
@ -748,29 +748,37 @@ static int _set_required_opps(struct device *dev,
|
||||
* @dev: device for which we do this operation
|
||||
* @target_freq: frequency to achieve
|
||||
*
|
||||
* This configures the power-supplies and clock source to the levels specified
|
||||
* by the OPP corresponding to the target_freq.
|
||||
* This configures the power-supplies to the levels specified by the OPP
|
||||
* corresponding to the target_freq, and programs the clock to a value <=
|
||||
* target_freq, as rounded by clk_round_rate(). Device wanting to run at fmax
|
||||
* provided by the opp, should have already rounded to the target OPP's
|
||||
* frequency.
|
||||
*/
|
||||
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
unsigned long freq, old_freq;
|
||||
unsigned long freq, old_freq, temp_freq;
|
||||
struct dev_pm_opp *old_opp, *opp;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
if (unlikely(!target_freq)) {
|
||||
dev_err(dev, "%s: Invalid target frequency %lu\n", __func__,
|
||||
target_freq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
opp_table = _find_opp_table(dev);
|
||||
if (IS_ERR(opp_table)) {
|
||||
dev_err(dev, "%s: device opp doesn't exist\n", __func__);
|
||||
return PTR_ERR(opp_table);
|
||||
}
|
||||
|
||||
if (unlikely(!target_freq)) {
|
||||
if (opp_table->required_opp_tables) {
|
||||
ret = _set_required_opps(dev, opp_table, NULL);
|
||||
} else {
|
||||
dev_err(dev, "target frequency can't be 0\n");
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
goto put_opp_table;
|
||||
}
|
||||
|
||||
clk = opp_table->clk;
|
||||
if (IS_ERR(clk)) {
|
||||
dev_err(dev, "%s: No clock available for the device\n",
|
||||
@ -793,13 +801,15 @@ int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq)
|
||||
goto put_opp_table;
|
||||
}
|
||||
|
||||
old_opp = _find_freq_ceil(opp_table, &old_freq);
|
||||
temp_freq = old_freq;
|
||||
old_opp = _find_freq_ceil(opp_table, &temp_freq);
|
||||
if (IS_ERR(old_opp)) {
|
||||
dev_err(dev, "%s: failed to find current OPP for freq %lu (%ld)\n",
|
||||
__func__, old_freq, PTR_ERR(old_opp));
|
||||
}
|
||||
|
||||
opp = _find_freq_ceil(opp_table, &freq);
|
||||
temp_freq = freq;
|
||||
opp = _find_freq_ceil(opp_table, &temp_freq);
|
||||
if (IS_ERR(opp)) {
|
||||
ret = PTR_ERR(opp);
|
||||
dev_err(dev, "%s: failed to find OPP for freq %lu (%d)\n",
|
||||
@ -1741,91 +1751,137 @@ void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_unregister_set_opp_helper);
|
||||
|
||||
static void _opp_detach_genpd(struct opp_table *opp_table)
|
||||
{
|
||||
int index;
|
||||
|
||||
for (index = 0; index < opp_table->required_opp_count; index++) {
|
||||
if (!opp_table->genpd_virt_devs[index])
|
||||
continue;
|
||||
|
||||
dev_pm_domain_detach(opp_table->genpd_virt_devs[index], false);
|
||||
opp_table->genpd_virt_devs[index] = NULL;
|
||||
}
|
||||
|
||||
kfree(opp_table->genpd_virt_devs);
|
||||
opp_table->genpd_virt_devs = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* dev_pm_opp_set_genpd_virt_dev - Set virtual genpd device for an index
|
||||
* @dev: Consumer device for which the genpd device is getting set.
|
||||
* @virt_dev: virtual genpd device.
|
||||
* @index: index.
|
||||
* dev_pm_opp_attach_genpd - Attach genpd(s) for the device and save virtual device pointer
|
||||
* @dev: Consumer device for which the genpd is getting attached.
|
||||
* @names: Null terminated array of pointers containing names of genpd to attach.
|
||||
*
|
||||
* Multiple generic power domains for a device are supported with the help of
|
||||
* virtual genpd devices, which are created for each consumer device - genpd
|
||||
* pair. These are the device structures which are attached to the power domain
|
||||
* and are required by the OPP core to set the performance state of the genpd.
|
||||
* The same API also works for the case where single genpd is available and so
|
||||
* we don't need to support that separately.
|
||||
*
|
||||
* This helper will normally be called by the consumer driver of the device
|
||||
* "dev", as only that has details of the genpd devices.
|
||||
* "dev", as only that has details of the genpd names.
|
||||
*
|
||||
* This helper needs to be called once for each of those virtual devices, but
|
||||
* only if multiple domains are available for a device. Otherwise the original
|
||||
* device structure will be used instead by the OPP core.
|
||||
* This helper needs to be called once with a list of all genpd to attach.
|
||||
* Otherwise the original device structure will be used instead by the OPP core.
|
||||
*/
|
||||
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev,
|
||||
struct device *virt_dev,
|
||||
int index)
|
||||
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
|
||||
{
|
||||
struct opp_table *opp_table;
|
||||
struct device *virt_dev;
|
||||
int index, ret = -EINVAL;
|
||||
const char **name = names;
|
||||
|
||||
opp_table = dev_pm_opp_get_opp_table(dev);
|
||||
if (!opp_table)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
if (unlikely(!opp_table->genpd_virt_devs ||
|
||||
index >= opp_table->required_opp_count ||
|
||||
opp_table->genpd_virt_devs[index])) {
|
||||
|
||||
dev_err(dev, "Invalid request to set required device\n");
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
return ERR_PTR(-EINVAL);
|
||||
/*
|
||||
* If the genpd's OPP table isn't already initialized, parsing of the
|
||||
* required-opps fail for dev. We should retry this after genpd's OPP
|
||||
* table is added.
|
||||
*/
|
||||
if (!opp_table->required_opp_count) {
|
||||
ret = -EPROBE_DEFER;
|
||||
goto put_table;
|
||||
}
|
||||
|
||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
opp_table->genpd_virt_devs = kcalloc(opp_table->required_opp_count,
|
||||
sizeof(*opp_table->genpd_virt_devs),
|
||||
GFP_KERNEL);
|
||||
if (!opp_table->genpd_virt_devs)
|
||||
goto unlock;
|
||||
|
||||
while (*name) {
|
||||
index = of_property_match_string(dev->of_node,
|
||||
"power-domain-names", *name);
|
||||
if (index < 0) {
|
||||
dev_err(dev, "Failed to find power domain: %s (%d)\n",
|
||||
*name, index);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (index >= opp_table->required_opp_count) {
|
||||
dev_err(dev, "Index can't be greater than required-opp-count - 1, %s (%d : %d)\n",
|
||||
*name, opp_table->required_opp_count, index);
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (opp_table->genpd_virt_devs[index]) {
|
||||
dev_err(dev, "Genpd virtual device already set %s\n",
|
||||
*name);
|
||||
goto err;
|
||||
}
|
||||
|
||||
virt_dev = dev_pm_domain_attach_by_name(dev, *name);
|
||||
if (IS_ERR(virt_dev)) {
|
||||
ret = PTR_ERR(virt_dev);
|
||||
dev_err(dev, "Couldn't attach to pm_domain: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
opp_table->genpd_virt_devs[index] = virt_dev;
|
||||
name++;
|
||||
}
|
||||
|
||||
opp_table->genpd_virt_devs[index] = virt_dev;
|
||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
return opp_table;
|
||||
|
||||
err:
|
||||
_opp_detach_genpd(opp_table);
|
||||
unlock:
|
||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
put_table:
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_attach_genpd);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_put_genpd_virt_dev() - Releases resources blocked for genpd device.
|
||||
* @opp_table: OPP table returned by dev_pm_opp_set_genpd_virt_dev().
|
||||
* @virt_dev: virtual genpd device.
|
||||
* dev_pm_opp_detach_genpd() - Detach genpd(s) from the device.
|
||||
* @opp_table: OPP table returned by dev_pm_opp_attach_genpd().
|
||||
*
|
||||
* This releases the resource previously acquired with a call to
|
||||
* dev_pm_opp_set_genpd_virt_dev(). The consumer driver shall call this helper
|
||||
* if it doesn't want OPP core to update performance state of a power domain
|
||||
* anymore.
|
||||
* This detaches the genpd(s), resets the virtual device pointers, and puts the
|
||||
* OPP table.
|
||||
*/
|
||||
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table,
|
||||
struct device *virt_dev)
|
||||
void dev_pm_opp_detach_genpd(struct opp_table *opp_table)
|
||||
{
|
||||
int i;
|
||||
|
||||
/*
|
||||
* Acquire genpd_virt_dev_lock to make sure virt_dev isn't getting
|
||||
* used in parallel.
|
||||
*/
|
||||
mutex_lock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
for (i = 0; i < opp_table->required_opp_count; i++) {
|
||||
if (opp_table->genpd_virt_devs[i] != virt_dev)
|
||||
continue;
|
||||
|
||||
opp_table->genpd_virt_devs[i] = NULL;
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
|
||||
/* Drop the vote */
|
||||
dev_pm_genpd_set_performance_state(virt_dev, 0);
|
||||
break;
|
||||
}
|
||||
|
||||
_opp_detach_genpd(opp_table);
|
||||
mutex_unlock(&opp_table->genpd_virt_dev_lock);
|
||||
|
||||
if (unlikely(i == opp_table->required_opp_count))
|
||||
dev_err(virt_dev, "Failed to find required device entry\n");
|
||||
dev_pm_opp_put_opp_table(opp_table);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dev_pm_opp_detach_genpd);
|
||||
|
||||
/**
|
||||
* dev_pm_opp_xlate_performance_state() - Find required OPP's pstate for src_table.
|
||||
|
@ -138,7 +138,6 @@ err:
|
||||
static void _opp_table_free_required_tables(struct opp_table *opp_table)
|
||||
{
|
||||
struct opp_table **required_opp_tables = opp_table->required_opp_tables;
|
||||
struct device **genpd_virt_devs = opp_table->genpd_virt_devs;
|
||||
int i;
|
||||
|
||||
if (!required_opp_tables)
|
||||
@ -152,10 +151,8 @@ static void _opp_table_free_required_tables(struct opp_table *opp_table)
|
||||
}
|
||||
|
||||
kfree(required_opp_tables);
|
||||
kfree(genpd_virt_devs);
|
||||
|
||||
opp_table->required_opp_count = 0;
|
||||
opp_table->genpd_virt_devs = NULL;
|
||||
opp_table->required_opp_tables = NULL;
|
||||
}
|
||||
|
||||
@ -168,9 +165,8 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
||||
struct device_node *opp_np)
|
||||
{
|
||||
struct opp_table **required_opp_tables;
|
||||
struct device **genpd_virt_devs = NULL;
|
||||
struct device_node *required_np, *np;
|
||||
int count, count_pd, i;
|
||||
int count, i;
|
||||
|
||||
/* Traversing the first OPP node is all we need */
|
||||
np = of_get_next_available_child(opp_np, NULL);
|
||||
@ -183,33 +179,11 @@ static void _opp_table_alloc_required_tables(struct opp_table *opp_table,
|
||||
if (!count)
|
||||
goto put_np;
|
||||
|
||||
/*
|
||||
* Check the number of power-domains to know if we need to deal
|
||||
* with virtual devices. In some cases we have devices with multiple
|
||||
* power domains but with only one of them being scalable, hence
|
||||
* 'count' could be 1, but we still have to deal with multiple genpds
|
||||
* and virtual devices.
|
||||
*/
|
||||
count_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
|
||||
"#power-domain-cells");
|
||||
if (!count_pd)
|
||||
goto put_np;
|
||||
|
||||
if (count_pd > 1) {
|
||||
genpd_virt_devs = kcalloc(count, sizeof(*genpd_virt_devs),
|
||||
GFP_KERNEL);
|
||||
if (!genpd_virt_devs)
|
||||
goto put_np;
|
||||
}
|
||||
|
||||
required_opp_tables = kcalloc(count, sizeof(*required_opp_tables),
|
||||
GFP_KERNEL);
|
||||
if (!required_opp_tables) {
|
||||
kfree(genpd_virt_devs);
|
||||
if (!required_opp_tables)
|
||||
goto put_np;
|
||||
}
|
||||
|
||||
opp_table->genpd_virt_devs = genpd_virt_devs;
|
||||
opp_table->required_opp_tables = required_opp_tables;
|
||||
opp_table->required_opp_count = count;
|
||||
|
||||
|
@ -128,8 +128,8 @@ struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const char * name);
|
||||
void dev_pm_opp_put_clkname(struct opp_table *opp_table);
|
||||
struct opp_table *dev_pm_opp_register_set_opp_helper(struct device *dev, int (*set_opp)(struct dev_pm_set_opp_data *data));
|
||||
void dev_pm_opp_unregister_set_opp_helper(struct opp_table *opp_table);
|
||||
struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index);
|
||||
void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev);
|
||||
struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names);
|
||||
void dev_pm_opp_detach_genpd(struct opp_table *opp_table);
|
||||
int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate);
|
||||
int dev_pm_opp_set_rate(struct device *dev, unsigned long target_freq);
|
||||
int dev_pm_opp_set_sharing_cpus(struct device *cpu_dev, const struct cpumask *cpumask);
|
||||
@ -292,12 +292,12 @@ static inline struct opp_table *dev_pm_opp_set_clkname(struct device *dev, const
|
||||
|
||||
static inline void dev_pm_opp_put_clkname(struct opp_table *opp_table) {}
|
||||
|
||||
static inline struct opp_table *dev_pm_opp_set_genpd_virt_dev(struct device *dev, struct device *virt_dev, int index)
|
||||
static inline struct opp_table *dev_pm_opp_attach_genpd(struct device *dev, const char **names)
|
||||
{
|
||||
return ERR_PTR(-ENOTSUPP);
|
||||
}
|
||||
|
||||
static inline void dev_pm_opp_put_genpd_virt_dev(struct opp_table *opp_table, struct device *virt_dev) {}
|
||||
static inline void dev_pm_opp_detach_genpd(struct opp_table *opp_table) {}
|
||||
|
||||
static inline int dev_pm_opp_xlate_performance_state(struct opp_table *src_table, struct opp_table *dst_table, unsigned int pstate)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user