Commit d5e4cbfe authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

PM / Domains: Make it possible to use per-device domain callbacks

The current generic PM domains code requires that the same .stop(),
.start() and .active_wakeup() device callback routines be used for
all devices in the given domain, which is inflexible and may not
cover some specific use cases.  For this reason, make it possible to
use device specific .start()/.stop() and .active_wakeup() callback
routines by adding corresponding callback pointers to struct
generic_pm_domain_data.  Add a new helper routine,
pm_genpd_register_callbacks(), that can be used to populate
the new per-device callback pointers.

Modify the shmobile's power domains code to allow drivers to add
their own code to be run during the device stop and start operations
with the help of the new callback pointers.
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
Acked-by: default avatarMagnus Damm <damm@opensource.se>
parent b930c264
...@@ -156,7 +156,10 @@ static void sh7372_a4r_suspend(void) ...@@ -156,7 +156,10 @@ static void sh7372_a4r_suspend(void)
static bool pd_active_wakeup(struct device *dev) static bool pd_active_wakeup(struct device *dev)
{ {
return true; bool (*active_wakeup)(struct device *dev);
active_wakeup = dev_gpd_data(dev)->ops.active_wakeup;
return active_wakeup ? active_wakeup(dev) : true;
} }
static bool sh7372_power_down_forbidden(struct dev_pm_domain *domain) static bool sh7372_power_down_forbidden(struct dev_pm_domain *domain)
...@@ -168,15 +171,44 @@ struct dev_power_governor sh7372_always_on_gov = { ...@@ -168,15 +171,44 @@ struct dev_power_governor sh7372_always_on_gov = {
.power_down_ok = sh7372_power_down_forbidden, .power_down_ok = sh7372_power_down_forbidden,
}; };
static int sh7372_stop_dev(struct device *dev)
{
int (*stop)(struct device *dev);
stop = dev_gpd_data(dev)->ops.stop;
if (stop) {
int ret = stop(dev);
if (ret)
return ret;
}
return pm_clk_suspend(dev);
}
static int sh7372_start_dev(struct device *dev)
{
int (*start)(struct device *dev);
int ret;
ret = pm_clk_resume(dev);
if (ret)
return ret;
start = dev_gpd_data(dev)->ops.start;
if (start)
ret = start(dev);
return ret;
}
void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
{ {
struct generic_pm_domain *genpd = &sh7372_pd->genpd; struct generic_pm_domain *genpd = &sh7372_pd->genpd;
pm_genpd_init(genpd, sh7372_pd->gov, false); pm_genpd_init(genpd, sh7372_pd->gov, false);
genpd->stop_device = pm_clk_suspend; genpd->dev_ops.stop = sh7372_stop_dev;
genpd->start_device = pm_clk_resume; genpd->dev_ops.start = sh7372_start_dev;
genpd->dev_ops.active_wakeup = pd_active_wakeup;
genpd->dev_irq_safe = true; genpd->dev_irq_safe = true;
genpd->active_wakeup = pd_active_wakeup;
genpd->power_off = pd_power_down; genpd->power_off = pd_power_down;
genpd->power_on = pd_power_up; genpd->power_on = pd_power_up;
__pd_power_up(sh7372_pd, false); __pd_power_up(sh7372_pd, false);
......
...@@ -15,6 +15,23 @@ ...@@ -15,6 +15,23 @@
#include <linux/err.h> #include <linux/err.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/export.h>
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
({ \
type (*__routine)(struct device *__d); \
type __ret = (type)0; \
\
__routine = genpd->dev_ops.callback; \
if (__routine) { \
__ret = __routine(dev); \
} else { \
__routine = dev_gpd_data(dev)->ops.callback; \
if (__routine) \
__ret = __routine(dev); \
} \
__ret; \
})
static LIST_HEAD(gpd_list); static LIST_HEAD(gpd_list);
static DEFINE_MUTEX(gpd_list_lock); static DEFINE_MUTEX(gpd_list_lock);
...@@ -29,6 +46,16 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev) ...@@ -29,6 +46,16 @@ static struct generic_pm_domain *dev_to_genpd(struct device *dev)
return pd_to_genpd(dev->pm_domain); return pd_to_genpd(dev->pm_domain);
} }
static int genpd_stop_dev(struct generic_pm_domain *genpd, struct device *dev)
{
return GENPD_DEV_CALLBACK(genpd, int, stop, dev);
}
static int genpd_start_dev(struct generic_pm_domain *genpd, struct device *dev)
{
return GENPD_DEV_CALLBACK(genpd, int, start, dev);
}
static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd) static bool genpd_sd_counter_dec(struct generic_pm_domain *genpd)
{ {
bool ret = false; bool ret = false;
...@@ -199,13 +226,9 @@ static int __pm_genpd_save_device(struct pm_domain_data *pdd, ...@@ -199,13 +226,9 @@ static int __pm_genpd_save_device(struct pm_domain_data *pdd,
mutex_unlock(&genpd->lock); mutex_unlock(&genpd->lock);
if (drv && drv->pm && drv->pm->runtime_suspend) { if (drv && drv->pm && drv->pm->runtime_suspend) {
if (genpd->start_device) genpd_start_dev(genpd, dev);
genpd->start_device(dev);
ret = drv->pm->runtime_suspend(dev); ret = drv->pm->runtime_suspend(dev);
genpd_stop_dev(genpd, dev);
if (genpd->stop_device)
genpd->stop_device(dev);
} }
mutex_lock(&genpd->lock); mutex_lock(&genpd->lock);
...@@ -235,13 +258,9 @@ static void __pm_genpd_restore_device(struct pm_domain_data *pdd, ...@@ -235,13 +258,9 @@ static void __pm_genpd_restore_device(struct pm_domain_data *pdd,
mutex_unlock(&genpd->lock); mutex_unlock(&genpd->lock);
if (drv && drv->pm && drv->pm->runtime_resume) { if (drv && drv->pm && drv->pm->runtime_resume) {
if (genpd->start_device) genpd_start_dev(genpd, dev);
genpd->start_device(dev);
drv->pm->runtime_resume(dev); drv->pm->runtime_resume(dev);
genpd_stop_dev(genpd, dev);
if (genpd->stop_device)
genpd->stop_device(dev);
} }
mutex_lock(&genpd->lock); mutex_lock(&genpd->lock);
...@@ -413,6 +432,7 @@ static void genpd_power_off_work_fn(struct work_struct *work) ...@@ -413,6 +432,7 @@ static void genpd_power_off_work_fn(struct work_struct *work)
static int pm_genpd_runtime_suspend(struct device *dev) static int pm_genpd_runtime_suspend(struct device *dev)
{ {
struct generic_pm_domain *genpd; struct generic_pm_domain *genpd;
int ret;
dev_dbg(dev, "%s()\n", __func__); dev_dbg(dev, "%s()\n", __func__);
...@@ -422,11 +442,9 @@ static int pm_genpd_runtime_suspend(struct device *dev) ...@@ -422,11 +442,9 @@ static int pm_genpd_runtime_suspend(struct device *dev)
might_sleep_if(!genpd->dev_irq_safe); might_sleep_if(!genpd->dev_irq_safe);
if (genpd->stop_device) { ret = genpd_stop_dev(genpd, dev);
int ret = genpd->stop_device(dev); if (ret)
if (ret) return ret;
return ret;
}
/* /*
* If power.irq_safe is set, this routine will be run with interrupts * If power.irq_safe is set, this routine will be run with interrupts
...@@ -502,8 +520,7 @@ static int pm_genpd_runtime_resume(struct device *dev) ...@@ -502,8 +520,7 @@ static int pm_genpd_runtime_resume(struct device *dev)
mutex_unlock(&genpd->lock); mutex_unlock(&genpd->lock);
out: out:
if (genpd->start_device) genpd_start_dev(genpd, dev);
genpd->start_device(dev);
return 0; return 0;
} }
...@@ -534,6 +551,12 @@ static inline void genpd_power_off_work_fn(struct work_struct *work) {} ...@@ -534,6 +551,12 @@ static inline void genpd_power_off_work_fn(struct work_struct *work) {}
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
static bool genpd_dev_active_wakeup(struct generic_pm_domain *genpd,
struct device *dev)
{
return GENPD_DEV_CALLBACK(genpd, bool, active_wakeup, dev);
}
/** /**
* pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters. * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its masters.
* @genpd: PM domain to power off, if possible. * @genpd: PM domain to power off, if possible.
...@@ -590,7 +613,7 @@ static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd) ...@@ -590,7 +613,7 @@ static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
if (!device_can_wakeup(dev)) if (!device_can_wakeup(dev))
return false; return false;
active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev); active_wakeup = genpd_dev_active_wakeup(genpd, dev);
return device_may_wakeup(dev) ? active_wakeup : !active_wakeup; return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
} }
...@@ -646,7 +669,7 @@ static int pm_genpd_prepare(struct device *dev) ...@@ -646,7 +669,7 @@ static int pm_genpd_prepare(struct device *dev)
/* /*
* The PM domain must be in the GPD_STATE_ACTIVE state at this point, * The PM domain must be in the GPD_STATE_ACTIVE state at this point,
* so pm_genpd_poweron() will return immediately, but if the device * so pm_genpd_poweron() will return immediately, but if the device
* is suspended (e.g. it's been stopped by .stop_device()), we need * is suspended (e.g. it's been stopped by genpd_stop_dev()), we need
* to make it operational. * to make it operational.
*/ */
pm_runtime_resume(dev); pm_runtime_resume(dev);
...@@ -714,12 +737,10 @@ static int pm_genpd_suspend_noirq(struct device *dev) ...@@ -714,12 +737,10 @@ static int pm_genpd_suspend_noirq(struct device *dev)
if (ret) if (ret)
return ret; return ret;
if (dev->power.wakeup_path if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
&& genpd->active_wakeup && genpd->active_wakeup(dev))
return 0; return 0;
if (genpd->stop_device) genpd_stop_dev(genpd, dev);
genpd->stop_device(dev);
/* /*
* Since all of the "noirq" callbacks are executed sequentially, it is * Since all of the "noirq" callbacks are executed sequentially, it is
...@@ -761,8 +782,7 @@ static int pm_genpd_resume_noirq(struct device *dev) ...@@ -761,8 +782,7 @@ static int pm_genpd_resume_noirq(struct device *dev)
*/ */
pm_genpd_poweron(genpd); pm_genpd_poweron(genpd);
genpd->suspended_count--; genpd->suspended_count--;
if (genpd->start_device) genpd_start_dev(genpd, dev);
genpd->start_device(dev);
return pm_generic_resume_noirq(dev); return pm_generic_resume_noirq(dev);
} }
...@@ -836,8 +856,7 @@ static int pm_genpd_freeze_noirq(struct device *dev) ...@@ -836,8 +856,7 @@ static int pm_genpd_freeze_noirq(struct device *dev)
if (ret) if (ret)
return ret; return ret;
if (genpd->stop_device) genpd_stop_dev(genpd, dev);
genpd->stop_device(dev);
return 0; return 0;
} }
...@@ -864,8 +883,7 @@ static int pm_genpd_thaw_noirq(struct device *dev) ...@@ -864,8 +883,7 @@ static int pm_genpd_thaw_noirq(struct device *dev)
if (genpd->suspend_power_off) if (genpd->suspend_power_off)
return 0; return 0;
if (genpd->start_device) genpd_start_dev(genpd, dev);
genpd->start_device(dev);
return pm_generic_thaw_noirq(dev); return pm_generic_thaw_noirq(dev);
} }
...@@ -938,12 +956,10 @@ static int pm_genpd_dev_poweroff_noirq(struct device *dev) ...@@ -938,12 +956,10 @@ static int pm_genpd_dev_poweroff_noirq(struct device *dev)
if (ret) if (ret)
return ret; return ret;
if (dev->power.wakeup_path if (dev->power.wakeup_path && genpd_dev_active_wakeup(genpd, dev))
&& genpd->active_wakeup && genpd->active_wakeup(dev))
return 0; return 0;
if (genpd->stop_device) genpd_stop_dev(genpd, dev);
genpd->stop_device(dev);
/* /*
* Since all of the "noirq" callbacks are executed sequentially, it is * Since all of the "noirq" callbacks are executed sequentially, it is
...@@ -993,8 +1009,7 @@ static int pm_genpd_restore_noirq(struct device *dev) ...@@ -993,8 +1009,7 @@ static int pm_genpd_restore_noirq(struct device *dev)
pm_genpd_poweron(genpd); pm_genpd_poweron(genpd);
genpd->suspended_count--; genpd->suspended_count--;
if (genpd->start_device) genpd_start_dev(genpd, dev);
genpd->start_device(dev);
return pm_generic_restore_noirq(dev); return pm_generic_restore_noirq(dev);
} }
...@@ -1279,6 +1294,69 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, ...@@ -1279,6 +1294,69 @@ int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
return ret; return ret;
} }
/**
* pm_genpd_add_callbacks - Add PM domain callbacks to a given device.
* @dev: Device to add the callbacks to.
* @ops: Set of callbacks to add.
*/
int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops)
{
struct pm_domain_data *pdd;
int ret = 0;
if (!(dev && dev->power.subsys_data && ops))
return -EINVAL;
pm_runtime_disable(dev);
device_pm_lock();
pdd = dev->power.subsys_data->domain_data;
if (pdd) {
struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
gpd_data->ops = *ops;
} else {
ret = -EINVAL;
}
device_pm_unlock();
pm_runtime_enable(dev);
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_add_callbacks);
/**
* pm_genpd_remove_callbacks - Remove PM domain callbacks from a given device.
* @dev: Device to remove the callbacks from.
*/
int pm_genpd_remove_callbacks(struct device *dev)
{
struct pm_domain_data *pdd;
int ret = 0;
if (!(dev && dev->power.subsys_data))
return -EINVAL;
pm_runtime_disable(dev);
device_pm_lock();
pdd = dev->power.subsys_data->domain_data;
if (pdd) {
struct generic_pm_domain_data *gpd_data = to_gpd_data(pdd);
gpd_data->ops = (struct gpd_dev_ops){ 0 };
} else {
ret = -EINVAL;
}
device_pm_unlock();
pm_runtime_enable(dev);
return ret;
}
EXPORT_SYMBOL_GPL(pm_genpd_remove_callbacks);
/** /**
* pm_genpd_init - Initialize a generic I/O PM domain object. * pm_genpd_init - Initialize a generic I/O PM domain object.
* @genpd: PM domain object to initialize. * @genpd: PM domain object to initialize.
......
...@@ -23,6 +23,12 @@ struct dev_power_governor { ...@@ -23,6 +23,12 @@ struct dev_power_governor {
bool (*power_down_ok)(struct dev_pm_domain *domain); bool (*power_down_ok)(struct dev_pm_domain *domain);
}; };
struct gpd_dev_ops {
int (*start)(struct device *dev);
int (*stop)(struct device *dev);
bool (*active_wakeup)(struct device *dev);
};
struct generic_pm_domain { struct generic_pm_domain {
struct dev_pm_domain domain; /* PM domain operations */ struct dev_pm_domain domain; /* PM domain operations */
struct list_head gpd_list_node; /* Node in the global PM domains list */ struct list_head gpd_list_node; /* Node in the global PM domains list */
...@@ -45,9 +51,7 @@ struct generic_pm_domain { ...@@ -45,9 +51,7 @@ struct generic_pm_domain {
bool dev_irq_safe; /* Device callbacks are IRQ-safe */ bool dev_irq_safe; /* Device callbacks are IRQ-safe */
int (*power_off)(struct generic_pm_domain *domain); int (*power_off)(struct generic_pm_domain *domain);
int (*power_on)(struct generic_pm_domain *domain); int (*power_on)(struct generic_pm_domain *domain);
int (*start_device)(struct device *dev); struct gpd_dev_ops dev_ops;
int (*stop_device)(struct device *dev);
bool (*active_wakeup)(struct device *dev);
}; };
static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd) static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
...@@ -64,6 +68,7 @@ struct gpd_link { ...@@ -64,6 +68,7 @@ struct gpd_link {
struct generic_pm_domain_data { struct generic_pm_domain_data {
struct pm_domain_data base; struct pm_domain_data base;
struct gpd_dev_ops ops;
bool need_restore; bool need_restore;
}; };
...@@ -73,6 +78,11 @@ static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data * ...@@ -73,6 +78,11 @@ static inline struct generic_pm_domain_data *to_gpd_data(struct pm_domain_data *
} }
#ifdef CONFIG_PM_GENERIC_DOMAINS #ifdef CONFIG_PM_GENERIC_DOMAINS
static inline struct generic_pm_domain_data *dev_gpd_data(struct device *dev)
{
return to_gpd_data(dev->power.subsys_data->domain_data);
}
extern int pm_genpd_add_device(struct generic_pm_domain *genpd, extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
struct device *dev); struct device *dev);
extern int pm_genpd_remove_device(struct generic_pm_domain *genpd, extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
...@@ -81,6 +91,8 @@ extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd, ...@@ -81,6 +91,8 @@ extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *new_subdomain); struct generic_pm_domain *new_subdomain);
extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
struct generic_pm_domain *target); struct generic_pm_domain *target);
extern int pm_genpd_add_callbacks(struct device *dev, struct gpd_dev_ops *ops);
extern int pm_genpd_remove_callbacks(struct device *dev);
extern void pm_genpd_init(struct generic_pm_domain *genpd, extern void pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off); struct dev_power_governor *gov, bool is_off);
extern int pm_genpd_poweron(struct generic_pm_domain *genpd); extern int pm_genpd_poweron(struct generic_pm_domain *genpd);
...@@ -105,6 +117,15 @@ static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd, ...@@ -105,6 +117,15 @@ static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
{ {
return -ENOSYS; return -ENOSYS;
} }
static inline int pm_genpd_add_callbacks(struct device *dev,
struct gpd_dev_ops *ops)
{
return -ENOSYS;
}
static inline int pm_genpd_remove_callbacks(struct device *dev)
{
return -ENOSYS;
}
static inline void pm_genpd_init(struct generic_pm_domain *genpd, static inline void pm_genpd_init(struct generic_pm_domain *genpd,
struct dev_power_governor *gov, bool is_off) {} struct dev_power_governor *gov, bool is_off) {}
static inline int pm_genpd_poweron(struct generic_pm_domain *genpd) static inline int pm_genpd_poweron(struct generic_pm_domain *genpd)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment