Commit 85eb8c8d authored by Rafael J. Wysocki's avatar Rafael J. Wysocki

PM / Runtime: Generic clock manipulation rountines for runtime PM (v6)

Many different platforms and subsystems may want to disable device
clocks during suspend and enable them during resume which is going to
be done in a very similar way in all those cases.  For this reason,
provide generic routines for the manipulation of device clocks during
suspend and resume.

Convert the ARM shmobile platform to using the new routines.
Signed-off-by: default avatarRafael J. Wysocki <rjw@sisk.pl>
parent 1d2b71f6
...@@ -21,70 +21,6 @@ ...@@ -21,70 +21,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#ifdef CONFIG_PM_RUNTIME #ifdef CONFIG_PM_RUNTIME
#define BIT_ONCE 0
#define BIT_ACTIVE 1
#define BIT_CLK_ENABLED 2
struct pm_runtime_data {
unsigned long flags;
struct clk *clk;
};
static struct pm_runtime_data *__to_prd(struct device *dev)
{
return dev ? dev->power.subsys_data : NULL;
}
static void platform_pm_runtime_init(struct device *dev,
struct pm_runtime_data *prd)
{
if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags)) {
prd->clk = clk_get(dev, NULL);
if (!IS_ERR(prd->clk)) {
set_bit(BIT_ACTIVE, &prd->flags);
dev_info(dev, "clocks managed by runtime pm\n");
}
}
}
static void platform_pm_runtime_bug(struct device *dev,
struct pm_runtime_data *prd)
{
if (prd && !test_and_set_bit(BIT_ONCE, &prd->flags))
dev_err(dev, "runtime pm suspend before resume\n");
}
static int default_platform_runtime_suspend(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);
dev_dbg(dev, "%s()\n", __func__);
platform_pm_runtime_bug(dev, prd);
if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
clk_disable(prd->clk);
clear_bit(BIT_CLK_ENABLED, &prd->flags);
}
return 0;
}
static int default_platform_runtime_resume(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);
dev_dbg(dev, "%s()\n", __func__);
platform_pm_runtime_init(dev, prd);
if (prd && test_bit(BIT_ACTIVE, &prd->flags)) {
clk_enable(prd->clk);
set_bit(BIT_CLK_ENABLED, &prd->flags);
}
return 0;
}
static int default_platform_runtime_idle(struct device *dev) static int default_platform_runtime_idle(struct device *dev)
{ {
...@@ -94,87 +30,29 @@ static int default_platform_runtime_idle(struct device *dev) ...@@ -94,87 +30,29 @@ static int default_platform_runtime_idle(struct device *dev)
static struct dev_power_domain default_power_domain = { static struct dev_power_domain default_power_domain = {
.ops = { .ops = {
.runtime_suspend = default_platform_runtime_suspend, .runtime_suspend = pm_runtime_clk_suspend,
.runtime_resume = default_platform_runtime_resume, .runtime_resume = pm_runtime_clk_resume,
.runtime_idle = default_platform_runtime_idle, .runtime_idle = default_platform_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS USE_PLATFORM_PM_SLEEP_OPS
}, },
}; };
static int platform_bus_notify(struct notifier_block *nb, #define DEFAULT_PWR_DOMAIN_PTR (&default_power_domain)
unsigned long action, void *data)
{
struct device *dev = data;
struct pm_runtime_data *prd;
dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
switch (action) {
case BUS_NOTIFY_BIND_DRIVER:
prd = kzalloc(sizeof(*prd), GFP_KERNEL);
if (prd) {
dev->power.subsys_data = prd;
dev->pwr_domain = &default_power_domain;
} else {
dev_err(dev, "unable to alloc memory for runtime pm\n");
}
break;
case BUS_NOTIFY_UNBOUND_DRIVER:
prd = __to_prd(dev);
if (prd) {
if (test_bit(BIT_CLK_ENABLED, &prd->flags))
clk_disable(prd->clk);
if (test_bit(BIT_ACTIVE, &prd->flags)) #else
clk_put(prd->clk);
}
break;
}
return 0; #define DEFAULT_PWR_DOMAIN_PTR NULL
}
#else /* CONFIG_PM_RUNTIME */
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
struct device *dev = data;
struct clk *clk;
dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
switch (action) {
case BUS_NOTIFY_BIND_DRIVER:
clk = clk_get(dev, NULL);
if (!IS_ERR(clk)) {
clk_enable(clk);
clk_put(clk);
dev_info(dev, "runtime pm disabled, clock forced on\n");
}
break;
case BUS_NOTIFY_UNBOUND_DRIVER:
clk = clk_get(dev, NULL);
if (!IS_ERR(clk)) {
clk_disable(clk);
clk_put(clk);
dev_info(dev, "runtime pm disabled, clock forced off\n");
}
break;
}
return 0;
}
#endif /* CONFIG_PM_RUNTIME */ #endif /* CONFIG_PM_RUNTIME */
static struct notifier_block platform_bus_notifier = { static struct pm_clk_notifier_block platform_bus_notifier = {
.notifier_call = platform_bus_notify .pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
.con_ids = { NULL, },
}; };
static int __init sh_pm_runtime_init(void) static int __init sh_pm_runtime_init(void)
{ {
bus_register_notifier(&platform_bus_type, &platform_bus_notifier); pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
return 0; return 0;
} }
core_initcall(sh_pm_runtime_init); core_initcall(sh_pm_runtime_init);
...@@ -3,6 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o ...@@ -3,6 +3,7 @@ obj-$(CONFIG_PM_SLEEP) += main.o wakeup.o
obj-$(CONFIG_PM_RUNTIME) += runtime.o obj-$(CONFIG_PM_RUNTIME) += runtime.o
obj-$(CONFIG_PM_TRACE_RTC) += trace.o obj-$(CONFIG_PM_TRACE_RTC) += trace.o
obj-$(CONFIG_PM_OPP) += opp.o obj-$(CONFIG_PM_OPP) += opp.o
obj-$(CONFIG_HAVE_CLK) += clock_ops.o
ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG ccflags-$(CONFIG_PM_VERBOSE) += -DDEBUG
This diff is collapsed.
...@@ -245,4 +245,46 @@ static inline void pm_runtime_dont_use_autosuspend(struct device *dev) ...@@ -245,4 +245,46 @@ static inline void pm_runtime_dont_use_autosuspend(struct device *dev)
__pm_runtime_use_autosuspend(dev, false); __pm_runtime_use_autosuspend(dev, false);
} }
struct pm_clk_notifier_block {
struct notifier_block nb;
struct dev_power_domain *pwr_domain;
char *con_ids[];
};
#ifdef CONFIG_PM_RUNTIME_CLK
extern int pm_runtime_clk_init(struct device *dev);
extern void pm_runtime_clk_destroy(struct device *dev);
extern int pm_runtime_clk_add(struct device *dev, const char *con_id);
extern void pm_runtime_clk_remove(struct device *dev, const char *con_id);
extern int pm_runtime_clk_suspend(struct device *dev);
extern int pm_runtime_clk_resume(struct device *dev);
#else
static inline int pm_runtime_clk_init(struct device *dev)
{
return -EINVAL;
}
static inline void pm_runtime_clk_destroy(struct device *dev)
{
}
static inline int pm_runtime_clk_add(struct device *dev, const char *con_id)
{
return -EINVAL;
}
static inline void pm_runtime_clk_remove(struct device *dev, const char *con_id)
{
}
#define pm_runtime_clock_suspend NULL
#define pm_runtime_clock_resume NULL
#endif
#ifdef CONFIG_HAVE_CLK
extern void pm_runtime_clk_add_notifier(struct bus_type *bus,
struct pm_clk_notifier_block *clknb);
#else
static inline void pm_runtime_clk_add_notifier(struct bus_type *bus,
struct pm_clk_notifier_block *clknb)
{
}
#endif
#endif #endif
...@@ -229,3 +229,7 @@ config PM_OPP ...@@ -229,3 +229,7 @@ config PM_OPP
representing individual voltage domains and provides SOC representing individual voltage domains and provides SOC
implementations a ready to use framework to manage OPPs. implementations a ready to use framework to manage OPPs.
For more information, read <file:Documentation/power/opp.txt> For more information, read <file:Documentation/power/opp.txt>
config PM_RUNTIME_CLK
def_bool y
depends on PM_RUNTIME && HAVE_CLK
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