[PATCH V2 03/12] PM / Domains: Add on-off notifiers

Rafael J. Wysocki rjw at sisk.pl
Sat Mar 30 19:17:57 EDT 2013


On Thursday, March 28, 2013 05:11:29 PM Rickard Andersson wrote:
> Some drivers needs to restore their context directly
> when a power domain is activated. For example a driver
> handling system bus settings must be able to restore
> context before the bus is being used for the first time.
> Other examples could be clock settings hardware blocks and
> special debugging hardware blocks which should be restored
> as early as possible in order to be able to debug direcly
> from waking up. Typically these notifers are needed in
> systems with CPU coupled power domains. The notifiers
> are intended to be trigged by cpuidle driver or the
> suspend ops hooks.
> 
> The drivers that needs to use these notifiers are
> some special cases. Most drivers should not use this
> method and instead control their context via the
> pm_runtime interface.
> 
> Signed-off-by: Rickard Andersson <rickard.andersson at stericsson.com>
> ---
> I believe notifiers are the best solution to the problem of
> restoring context early when waking up after sleep.

I don't.

> It is similar to what is done in /kernel/cpu_pm.c

Which is causing tons of problems to happen.

> but but for a generic power domain instead.

Which has a potential of being even worse.

> An alternative solution could be to try to call the drivers
> runtime_resume/runtime_suspend callbacks instead via genpd
> from cpuidle or suspend/resume. I do not recommend that
> solution for the following reasons:
> 
> * Altered use of runtime PM. As it is today the driver itself
>   controls when to runtime-suspend/runtime-resume.

That's not correct.

> * Runtime PM is disabled during late stages of suspend.
> * Complicates runtime PM for a few special cases.

Well, quite frankly, it is hard to say what problem this is supposed to
address from your description.  Care to give some more details?

Rafael


> ---
>  drivers/base/power/domain.c | 63 +++++++++++++++++++++++++++++++++++++++++++++
>  include/linux/pm_domain.h   | 22 ++++++++++++++++
>  2 files changed, 85 insertions(+)
> 
> diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c
> index acc3a8d..91e3b7d 100644
> --- a/drivers/base/power/domain.c
> +++ b/drivers/base/power/domain.c
> @@ -1956,6 +1956,68 @@ int pm_genpd_name_detach_cpuidle(const char *name)
>  	return pm_genpd_detach_cpuidle(pm_genpd_lookup_name(name));
>  }
>  
> +/**
> + * pm_genpd_register_on_off_notifier - Register to early power on /
> + * late power off notifications.
> + * Only use this function if absolutly needed. It is only intended to be
> + * used for power domains that are coupled with the CPU, that is, power
> + * domains being controlled from cpuidle and the platform suspend ops hooks.
> + * Also only devices that needs their context to be restored directly when
> + * leaving a sleep state should use this method. Most devices should be
> + * fine handling their context and power domains via pm_runtime.
> + * @dev: Device to register.
> + * @nb: Notifier block to be registered.
> + */
> +int pm_genpd_register_on_off_notifier(struct device *dev,
> +				      struct notifier_block *nb)
> +{
> +	struct generic_pm_domain *genpd;
> +
> +	dev_dbg(dev, "%s()\n", __func__);
> +
> +	genpd = dev_to_genpd(dev);
> +
> +	return atomic_notifier_chain_register(&genpd->on_off_notifier, nb);
> +}
> +
> +/**
> + * pm_genpd_unregister_on_off_notifier - Unregister to early power on /
> + * late power off notification.
> + * @dev: Device to unregister.
> + * @nb: Notifier block to be unregistered.
> + */
> +int pm_genpd_unregister_on_off_notifier(struct device *dev,
> +					struct notifier_block *nb)
> +{
> +	struct generic_pm_domain *genpd;
> +
> +	dev_dbg(dev, "%s()\n", __func__);
> +
> +	genpd = dev_to_genpd(dev);
> +
> +	return atomic_notifier_chain_unregister(&genpd->on_off_notifier, nb);
> +}
> +
> +/**
> + * pm_genpd_notify_power_on_off - Notify that the CPU coupled power
> + * domain is going to be powered off or has been powered on.5D
> + * Intended users of this function are cpuidle drivers and the platform
> + * suspend operations implementation.
> + * @genpd: pm domain that will change state.
> + * @power_on_off: true = has been powered on, false = will power off.
> + */
> +int pm_genpd_notify_power_on_off(struct generic_pm_domain *genpd,
> +				 bool power_on_off)
> +{
> +	if (IS_ERR_OR_NULL(genpd))
> +		return -EINVAL;
> +
> +	atomic_notifier_call_chain((&genpd->on_off_notifier),
> +				   power_on_off, NULL);
> +
> +	return 0;
> +}
> +
>  /* Default device callbacks for generic PM domains. */
>  
>  /**
> @@ -2137,6 +2199,7 @@ void pm_genpd_init(struct generic_pm_domain *genpd,
>  	atomic_set(&genpd->sd_count, 0);
>  	genpd->status = is_off ? GPD_STATE_POWER_OFF : GPD_STATE_ACTIVE;
>  	init_waitqueue_head(&genpd->status_wait_queue);
> +	ATOMIC_INIT_NOTIFIER_HEAD(&genpd->on_off_notifier);
>  	genpd->poweroff_task = NULL;
>  	genpd->resume_count = 0;
>  	genpd->device_count = 0;
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index 7c1d252..bf5e7d514 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -82,6 +82,7 @@ struct generic_pm_domain {
>  	bool cached_power_down_ok;
>  	struct device_node *of_node; /* Node in device tree */
>  	struct gpd_cpu_data *cpu_data;
> +	struct atomic_notifier_head on_off_notifier;
>  };
>  
>  static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
> @@ -159,6 +160,12 @@ extern int pm_genpd_attach_cpuidle(struct generic_pm_domain *genpd, int state);
>  extern int pm_genpd_name_attach_cpuidle(const char *name, int state);
>  extern int pm_genpd_detach_cpuidle(struct generic_pm_domain *genpd);
>  extern int pm_genpd_name_detach_cpuidle(const char *name);
> +extern int pm_genpd_register_on_off_notifier(struct device *dev,
> +					     struct notifier_block *nb);
> +extern int pm_genpd_unregister_on_off_notifier(struct device *dev,
> +					struct notifier_block *nb);
> +extern int pm_genpd_notify_power_on_off(struct generic_pm_domain *genpd,
> +				  bool power_on_off);
>  extern void pm_genpd_init(struct generic_pm_domain *genpd,
>  			  struct dev_power_governor *gov, bool is_off);
>  
> @@ -243,6 +250,21 @@ static inline int pm_genpd_name_detach_cpuidle(const char *name)
>  {
>  	return -ENOSYS;
>  }
> +static inline int pm_genpd_register_on_off_notifier(struct device *dev,
> +						    struct notifier_block *nb)
> +{
> +	return -ENOSYS;
> +}
> +static inline int pm_genpd_unregister_on_off_notifier(struct device *dev,
> +						      struct notifier_block *nb)
> +{
> +	return -ENOSYS;
> +}
> +static inline int pm_genpd_notify_power_on_off(struct generic_pm_domain *genpd,
> +					       bool power_on_off)
> +{
> +	return -ENOSYS;
> +}
>  static inline void pm_genpd_init(struct generic_pm_domain *genpd,
>  				 struct dev_power_governor *gov, bool is_off)
>  {
> 
-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.



More information about the linux-arm-kernel mailing list