Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
PM: domains: Add dev_get_performance_state() callback
Browse files Browse the repository at this point in the history
Add optional dev_get_performance_state() callback that retrieves and
initializes performance state of a device attached to a power domain.
The new callback should return performance and suspend states of the
attached device, which should be read out from hardware.

GENPD core assumes that initially performance state of all devices is
zero and consumer device driver is responsible for management of the
performance state. GENPD core now drops and restores performance state
across RPM suspend/resume of consumer devices, which allowed to move part
of the boilerplate performance management code into GENPD core. The part
that hasn't been moved to GENPD core yet is the initialization of the
performance state.

Hardware may be preprogrammed to a specific performance state which
isn't zero initially, hence the PD's performance state is inconsistent
with hardware. This requires each consumer driver to sync the state
explicitly. For some platforms this is a boilerplate code replicated
by each driver.

The new callback moves out initialization of the performance state
from consumer drivers into GENPD core, allowing to remove the remaining
boilerplate code from the drivers. Now, when consumer device is resumed
for the first time, the GENPD core will set up performance state in
accordance to the hardware state. Still, consumer drivers are free to
override the initial performance state configured by GENPD, if necessary.

Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
digetx committed Aug 28, 2021
1 parent 6fae333 commit a97b2f2
Showing 2 changed files with 76 additions and 13 deletions.
87 changes: 74 additions & 13 deletions drivers/base/power/domain.c
Original file line number Diff line number Diff line change
@@ -2644,12 +2644,82 @@ static void genpd_dev_pm_sync(struct device *dev)
genpd_queue_power_off_work(pd);
}

static int genpd_dev_get_current_performance_state(struct device *dev,
struct device *base_dev,
unsigned int index,
bool *state_default,
bool *suspended)
{
struct generic_pm_domain *pd = dev_to_genpd(dev);
int ret;

ret = of_get_required_opp_performance_state(dev->of_node, index);
if (ret < 0 && ret != -ENODEV && ret != -EOPNOTSUPP) {
dev_err(dev, "failed to get required performance state for power-domain %s: %d\n",
pd->name, ret);
return ret;
} else if (ret >= 0) {
*state_default = true;
return ret;
}

if (pd->dev_get_performance_state) {
ret = pd->dev_get_performance_state(pd, base_dev, suspended);
if (ret < 0)
dev_err(dev, "failed to get performance state for power-domain %s: %d\n",
pd->name, ret);
return ret;
}

return 0;
}

static int genpd_dev_configure_performance_state(struct device *dev,
struct device *base_dev,
unsigned int index)
{
struct generic_pm_domain *pd = dev_to_genpd(dev);
bool state_default = false, suspended = false;
int pstate, err;

pstate = genpd_dev_get_current_performance_state(dev, base_dev, index,
&state_default,
&suspended);
if (pstate <= 0)
return pstate;

/*
* If device is suspended, then it will be activated on RPM-resume.
* Hardware will require the current performance state to be set
* when device is resumed, hence only set rpm_pstate, otherwise extra
* power may be consumed if device won't be resumed by a consumer
* driver.
*
* If device is known to be active at the moment, then the performance
* state should be updated immediately to sync state with hardware.
*/
if (suspended) {
dev_gpd_data(dev)->rpm_pstate = pstate;
} else {
err = dev_pm_genpd_set_performance_state(dev, pstate);
if (err) {
dev_err(dev, "failed to set performance state for power-domain %s: %d\n",
pd->name, err);
return err;
}

if (state_default)
dev_gpd_data(dev)->default_pstate = pstate;
}

return 0;
}

static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
unsigned int index, bool power_on)
{
struct of_phandle_args pd_args;
struct generic_pm_domain *pd;
int pstate;
int ret;

ret = of_parse_phandle_with_args(dev->of_node, "power-domains",
@@ -2693,22 +2763,13 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
return -EPROBE_DEFER;
}

/* Set the default performance state */
pstate = of_get_required_opp_performance_state(dev->of_node, index);
if (pstate < 0 && pstate != -ENODEV && pstate != -EOPNOTSUPP) {
ret = pstate;
ret = genpd_dev_configure_performance_state(dev, base_dev, index);
if (ret)
goto err;
} else if (pstate > 0) {
ret = dev_pm_genpd_set_performance_state(dev, pstate);
if (ret)
goto err;
dev_gpd_data(dev)->default_pstate = pstate;
}

return 1;

err:
dev_err(dev, "failed to set required performance state for power-domain %s: %d\n",
pd->name, ret);
genpd_remove_device(pd, dev);
return ret;
}
2 changes: 2 additions & 0 deletions include/linux/pm_domain.h
Original file line number Diff line number Diff line change
@@ -133,6 +133,8 @@ struct generic_pm_domain {
struct dev_pm_opp *opp);
int (*set_performance_state)(struct generic_pm_domain *genpd,
unsigned int state);
int (*dev_get_performance_state)(struct generic_pm_domain *genpd,
struct device *dev, bool *dev_suspended);
struct gpd_dev_ops dev_ops;
s64 max_off_time_ns; /* Maximum allowed "suspended" time. */
ktime_t next_wakeup; /* Maintained by the domain governor */

0 comments on commit a97b2f2

Please sign in to comment.