diff mbox

[Update,7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

Message ID 201104182159.54316.rjw@sisk.pl (mailing list archive)
State Not Applicable
Headers show

Commit Message

Rafael Wysocki April 18, 2011, 7:59 p.m. UTC
From: Rafael J. Wysocki <rjw@sisk.pl>

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: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

The previous version of the patch had a build problem for CONFIG_HAVE_CLK
unset and a the name of pm_runtime_clock_add_notifier() misspelled (a
couple of times).

Thanks,
Rafael

---
 arch/arm/mach-shmobile/pm_runtime.c |  139 +--------------------------
 drivers/base/power/Makefile         |    1 
 drivers/base/power/clock_ops.c      |  179 ++++++++++++++++++++++++++++++++++++
 include/linux/pm_runtime.h          |   27 +++++
 kernel/power/Kconfig                |    4 
 5 files changed, 216 insertions(+), 134 deletions(-)


--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Comments

Magnus Damm April 19, 2011, 10:18 a.m. UTC | #1
Hi Rafael,

On Tue, Apr 19, 2011 at 4:59 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> 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: Rafael J. Wysocki <rjw@sisk.pl>
> ---
>
> Hi,
>
> The previous version of the patch had a build problem for CONFIG_HAVE_CLK
> unset and a the name of pm_runtime_clock_add_notifier() misspelled (a
> couple of times).

Thanks for your work on this. I like that we get closer to a shared code base.

Do you have any plans to add support for multiple clocks per struct
device? I had some plans to play around with that myself, but if we're
moving the code to a common place then this obviously becomes a bit
more complicated.

It's rather common that each hardware block in an SoC is connected to
more than a single clock. This needs to be managed by software
somehow.

So if the plan is to make to the code generic, how about allowing the
architecture to associate clocks with each struct device somehow?

Thanks,

/ magnus
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael Wysocki April 19, 2011, 9:42 p.m. UTC | #2
On Tuesday, April 19, 2011, Magnus Damm wrote:
> Hi Rafael,
> 
> On Tue, Apr 19, 2011 at 4:59 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > 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: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> >
> > Hi,
> >
> > The previous version of the patch had a build problem for CONFIG_HAVE_CLK
> > unset and a the name of pm_runtime_clock_add_notifier() misspelled (a
> > couple of times).
> 
> Thanks for your work on this. I like that we get closer to a shared code base.
> 
> Do you have any plans to add support for multiple clocks per struct
> device? I had some plans to play around with that myself, but if we're
> moving the code to a common place then this obviously becomes a bit
> more complicated.
> 
> It's rather common that each hardware block in an SoC is connected to
> more than a single clock. This needs to be managed by software
> somehow.
> 
> So if the plan is to make to the code generic, how about allowing the
> architecture to associate clocks with each struct device somehow?

Hmm.  For now, my patchset generally reorganizes the existing code without
adding new functionality.  Of course, it is possible to add new functionality
on top of it, but I'd prefer to focus on the "real" power domains support
first (which I think should be done in a generic way too).

The plan is to share as much code as it makes sense between platforms and
architectures.

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Mundt April 19, 2011, 9:59 p.m. UTC | #3
On Tue, Apr 19, 2011 at 11:42:26PM +0200, Rafael J. Wysocki wrote:
> On Tuesday, April 19, 2011, Magnus Damm wrote:
> > Do you have any plans to add support for multiple clocks per struct
> > device? I had some plans to play around with that myself, but if we're
> > moving the code to a common place then this obviously becomes a bit
> > more complicated.
> > 
> > It's rather common that each hardware block in an SoC is connected to
> > more than a single clock. This needs to be managed by software
> > somehow.
> > 
> > So if the plan is to make to the code generic, how about allowing the
> > architecture to associate clocks with each struct device somehow?
> 
> Hmm.  For now, my patchset generally reorganizes the existing code without
> adding new functionality.  Of course, it is possible to add new functionality
> on top of it, but I'd prefer to focus on the "real" power domains support
> first (which I think should be done in a generic way too).
> 
Multiple clocks is not new functionality, it's the common case for the
bulk of the platforms, and something that is already presently handled.

> The plan is to share as much code as it makes sense between platforms and
> architectures.

An admirable plan, but the framework needs to be able to provide at least
the current required level of functionality in order for it to be
adopted, too.

On Mon, Apr 18, 2011 at 09:57:28PM +0200, Rafael J. Wysocki wrote:
> @@ -24,23 +24,18 @@
>  #ifdef CONFIG_PM_RUNTIME
>  static int omap1_pm_runtime_suspend(struct device *dev)
>  {
> -	struct clk *iclk, *fclk;
> -	int ret = 0;
> +	int ret;
>  
>  	dev_dbg(dev, "%s\n", __func__);
>  
>  	ret = pm_generic_runtime_suspend(dev);
> +	if (ret)
> +		return ret;
>  
> -	fclk = clk_get(dev, "fck");
> -	if (!IS_ERR(fclk)) {
> -		clk_disable(fclk);
> -		clk_put(fclk);
> -	}
> -
> -	iclk = clk_get(dev, "ick");
> -	if (!IS_ERR(iclk)) {
> -		clk_disable(iclk);
> -		clk_put(iclk);
> +	ret = pm_runtime_clock_suspend(dev);
> +	if (ret) {
> +		pm_generic_runtime_resume(dev);
> +		return ret;
>  	}
>  
>  	return 0;

The before and after changes here are not functionally equivalent. You've
gone from an explicit multi-clock scheme to a single encapsulated one via
pm_runtime_clock_suspend().

Almost every single SH IP block is likewise abstracted in to a function
and interface clock, and OMAP and others are where we modelled this
abstraction on top of in the first place, so there are certainly users
there too.
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael Wysocki April 19, 2011, 10:10 p.m. UTC | #4
On Tuesday, April 19, 2011, Paul Mundt wrote:
> On Tue, Apr 19, 2011 at 11:42:26PM +0200, Rafael J. Wysocki wrote:
> > On Tuesday, April 19, 2011, Magnus Damm wrote:
> > > Do you have any plans to add support for multiple clocks per struct
> > > device? I had some plans to play around with that myself, but if we're
> > > moving the code to a common place then this obviously becomes a bit
> > > more complicated.
> > > 
> > > It's rather common that each hardware block in an SoC is connected to
> > > more than a single clock. This needs to be managed by software
> > > somehow.
> > > 
> > > So if the plan is to make to the code generic, how about allowing the
> > > architecture to associate clocks with each struct device somehow?
> > 
> > Hmm.  For now, my patchset generally reorganizes the existing code without
> > adding new functionality.  Of course, it is possible to add new functionality
> > on top of it, but I'd prefer to focus on the "real" power domains support
> > first (which I think should be done in a generic way too).
> > 
> Multiple clocks is not new functionality, it's the common case for the
> bulk of the platforms, and something that is already presently handled.

OK

> > The plan is to share as much code as it makes sense between platforms and
> > architectures.
> 
> An admirable plan, but the framework needs to be able to provide at least
> the current required level of functionality in order for it to be
> adopted, too.

Sure.

> On Mon, Apr 18, 2011 at 09:57:28PM +0200, Rafael J. Wysocki wrote:
> > @@ -24,23 +24,18 @@
> >  #ifdef CONFIG_PM_RUNTIME
> >  static int omap1_pm_runtime_suspend(struct device *dev)
> >  {
> > -	struct clk *iclk, *fclk;
> > -	int ret = 0;
> > +	int ret;
> >  
> >  	dev_dbg(dev, "%s\n", __func__);
> >  
> >  	ret = pm_generic_runtime_suspend(dev);
> > +	if (ret)
> > +		return ret;
> >  
> > -	fclk = clk_get(dev, "fck");
> > -	if (!IS_ERR(fclk)) {
> > -		clk_disable(fclk);
> > -		clk_put(fclk);
> > -	}
> > -
> > -	iclk = clk_get(dev, "ick");
> > -	if (!IS_ERR(iclk)) {
> > -		clk_disable(iclk);
> > -		clk_put(iclk);
> > +	ret = pm_runtime_clock_suspend(dev);
> > +	if (ret) {
> > +		pm_generic_runtime_resume(dev);
> > +		return ret;
> >  	}
> >  
> >  	return 0;
> 
> The before and after changes here are not functionally equivalent. You've
> gone from an explicit multi-clock scheme to a single encapsulated one via
> pm_runtime_clock_suspend().

You're refferring to the OMAP changes I suppose?

> Almost every single SH IP block is likewise abstracted in to a function
> and interface clock, and OMAP and others are where we modelled this
> abstraction on top of in the first place, so there are certainly users
> there too.

In fact, the shmobile runtime PM code in arch/arm/mach-shmobile/pm_runtime.c
uses only one clock right now.

Or am I missing anything?

Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Paul Mundt April 19, 2011, 10:20 p.m. UTC | #5
On Wed, Apr 20, 2011 at 12:10:50AM +0200, Rafael J. Wysocki wrote:
> On Tuesday, April 19, 2011, Paul Mundt wrote:
> > On Tue, Apr 19, 2011 at 11:42:26PM +0200, Rafael J. Wysocki wrote:
> > > On Tuesday, April 19, 2011, Magnus Damm wrote:
> > > > Do you have any plans to add support for multiple clocks per struct
> > > > device? I had some plans to play around with that myself, but if we're
> > > > moving the code to a common place then this obviously becomes a bit
> > > > more complicated.
> > > > 
> > > > It's rather common that each hardware block in an SoC is connected to
> > > > more than a single clock. This needs to be managed by software
> > > > somehow.
> > > > 
> > > > So if the plan is to make to the code generic, how about allowing the
> > > > architecture to associate clocks with each struct device somehow?
> > > 
> > > Hmm.  For now, my patchset generally reorganizes the existing code without
> > > adding new functionality.  Of course, it is possible to add new functionality
> > > on top of it, but I'd prefer to focus on the "real" power domains support
> > > first (which I think should be done in a generic way too).
> > > 
> > Multiple clocks is not new functionality, it's the common case for the
> > bulk of the platforms, and something that is already presently handled.
> 
> OK
> 
> > > The plan is to share as much code as it makes sense between platforms and
> > > architectures.
> > 
> > An admirable plan, but the framework needs to be able to provide at least
> > the current required level of functionality in order for it to be
> > adopted, too.
> 
> Sure.
> 
> > On Mon, Apr 18, 2011 at 09:57:28PM +0200, Rafael J. Wysocki wrote:
> > > @@ -24,23 +24,18 @@
> > >  #ifdef CONFIG_PM_RUNTIME
> > >  static int omap1_pm_runtime_suspend(struct device *dev)
> > >  {
> > > -	struct clk *iclk, *fclk;
> > > -	int ret = 0;
> > > +	int ret;
> > >  
> > >  	dev_dbg(dev, "%s\n", __func__);
> > >  
> > >  	ret = pm_generic_runtime_suspend(dev);
> > > +	if (ret)
> > > +		return ret;
> > >  
> > > -	fclk = clk_get(dev, "fck");
> > > -	if (!IS_ERR(fclk)) {
> > > -		clk_disable(fclk);
> > > -		clk_put(fclk);
> > > -	}
> > > -
> > > -	iclk = clk_get(dev, "ick");
> > > -	if (!IS_ERR(iclk)) {
> > > -		clk_disable(iclk);
> > > -		clk_put(iclk);
> > > +	ret = pm_runtime_clock_suspend(dev);
> > > +	if (ret) {
> > > +		pm_generic_runtime_resume(dev);
> > > +		return ret;
> > >  	}
> > >  
> > >  	return 0;
> > 
> > The before and after changes here are not functionally equivalent. You've
> > gone from an explicit multi-clock scheme to a single encapsulated one via
> > pm_runtime_clock_suspend().
> 
> You're refferring to the OMAP changes I suppose?

Yes, but we have similar use cases on SH, too.

> > Almost every single SH IP block is likewise abstracted in to a function
> > and interface clock, and OMAP and others are where we modelled this
> > abstraction on top of in the first place, so there are certainly users
> > there too.
> 
> In fact, the shmobile runtime PM code in arch/arm/mach-shmobile/pm_runtime.c
> uses only one clock right now.
> 
That's more due to general laziness than design. The code in
arch/sh/kernel/cpu/shmobile/pm_runtime.c goes through a hwblk abstraction
that functionally maps out for some CPUs via one API what is the function
clock on other CPUs. The hwblk API was never carried over to the ARM
side, and so a simplistic single clock case was implemented instead, and
the drivers with multiple clocks all performed manual clock gating on
their multiple clocks outside of the context of runtime PM.

OMAP1 clearly has a demonstratable case for multiple clocks that are
runtime PM managed, and this is exactly the sort of use case that SH
requires, too. If we can migrate off of and kill off some short-lived
ill-conceived APIs in the process, great. IOW, if you solve the OMAP1
problem then we can easily fix up ARM SH/R-Mobile and regular SH parts to
comply uniformly.
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Rafael Wysocki April 19, 2011, 10:50 p.m. UTC | #6
On Wednesday, April 20, 2011, Paul Mundt wrote:
> On Wed, Apr 20, 2011 at 12:10:50AM +0200, Rafael J. Wysocki wrote:
> > On Tuesday, April 19, 2011, Paul Mundt wrote:
> > > On Tue, Apr 19, 2011 at 11:42:26PM +0200, Rafael J. Wysocki wrote:
> > > > On Tuesday, April 19, 2011, Magnus Damm wrote:
> > > > > Do you have any plans to add support for multiple clocks per struct
> > > > > device? I had some plans to play around with that myself, but if we're
> > > > > moving the code to a common place then this obviously becomes a bit
> > > > > more complicated.
> > > > > 
> > > > > It's rather common that each hardware block in an SoC is connected to
> > > > > more than a single clock. This needs to be managed by software
> > > > > somehow.
> > > > > 
> > > > > So if the plan is to make to the code generic, how about allowing the
> > > > > architecture to associate clocks with each struct device somehow?
> > > > 
> > > > Hmm.  For now, my patchset generally reorganizes the existing code without
> > > > adding new functionality.  Of course, it is possible to add new functionality
> > > > on top of it, but I'd prefer to focus on the "real" power domains support
> > > > first (which I think should be done in a generic way too).
> > > > 
> > > Multiple clocks is not new functionality, it's the common case for the
> > > bulk of the platforms, and something that is already presently handled.
> > 
> > OK
> > 
> > > > The plan is to share as much code as it makes sense between platforms and
> > > > architectures.
> > > 
> > > An admirable plan, but the framework needs to be able to provide at least
> > > the current required level of functionality in order for it to be
> > > adopted, too.
> > 
> > Sure.
> > 
> > > On Mon, Apr 18, 2011 at 09:57:28PM +0200, Rafael J. Wysocki wrote:
> > > > @@ -24,23 +24,18 @@
> > > >  #ifdef CONFIG_PM_RUNTIME
> > > >  static int omap1_pm_runtime_suspend(struct device *dev)
> > > >  {
> > > > -	struct clk *iclk, *fclk;
> > > > -	int ret = 0;
> > > > +	int ret;
> > > >  
> > > >  	dev_dbg(dev, "%s\n", __func__);
> > > >  
> > > >  	ret = pm_generic_runtime_suspend(dev);
> > > > +	if (ret)
> > > > +		return ret;
> > > >  
> > > > -	fclk = clk_get(dev, "fck");
> > > > -	if (!IS_ERR(fclk)) {
> > > > -		clk_disable(fclk);
> > > > -		clk_put(fclk);
> > > > -	}
> > > > -
> > > > -	iclk = clk_get(dev, "ick");
> > > > -	if (!IS_ERR(iclk)) {
> > > > -		clk_disable(iclk);
> > > > -		clk_put(iclk);
> > > > +	ret = pm_runtime_clock_suspend(dev);
> > > > +	if (ret) {
> > > > +		pm_generic_runtime_resume(dev);
> > > > +		return ret;
> > > >  	}
> > > >  
> > > >  	return 0;
> > > 
> > > The before and after changes here are not functionally equivalent. You've
> > > gone from an explicit multi-clock scheme to a single encapsulated one via
> > > pm_runtime_clock_suspend().
> > 
> > You're refferring to the OMAP changes I suppose?
> 
> Yes, but we have similar use cases on SH, too.
> 
> > > Almost every single SH IP block is likewise abstracted in to a function
> > > and interface clock, and OMAP and others are where we modelled this
> > > abstraction on top of in the first place, so there are certainly users
> > > there too.
> > 
> > In fact, the shmobile runtime PM code in arch/arm/mach-shmobile/pm_runtime.c
> > uses only one clock right now.
> > 
> That's more due to general laziness than design. The code in
> arch/sh/kernel/cpu/shmobile/pm_runtime.c goes through a hwblk abstraction
> that functionally maps out for some CPUs via one API what is the function
> clock on other CPUs. The hwblk API was never carried over to the ARM
> side, and so a simplistic single clock case was implemented instead, and
> the drivers with multiple clocks all performed manual clock gating on
> their multiple clocks outside of the context of runtime PM.
> 
> OMAP1 clearly has a demonstratable case for multiple clocks that are
> runtime PM managed, and this is exactly the sort of use case that SH
> requires, too. If we can migrate off of and kill off some short-lived
> ill-conceived APIs in the process, great. IOW, if you solve the OMAP1
> problem then we can easily fix up ARM SH/R-Mobile and regular SH parts to
> comply uniformly.

OK, I'll extend the generic code to cover the OMAP case.

Thanks,
Rafael
--
To unsubscribe from this list: send the line "unsubscribe linux-sh" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -0,0 +1,179 @@ 
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Jinzai Solution Inc.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+struct pm_runtime_data {
+	struct clk *clk;
+	unsigned int clock_active:1;
+	unsigned int clock_enabled:1;
+};
+
+static struct pm_runtime_data *__to_prd(struct device *dev)
+{
+	return dev ? dev->power.subsys_data : NULL;
+}
+
+int pm_runtime_clock_add(struct device *dev)
+{
+	struct pm_runtime_data *prd;
+
+	prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+	if (!prd) {
+		dev_err(dev, "unable to allocate memory for runtime PM\n");
+		return -ENOMEM;
+	}
+
+	dev->power.subsys_data = prd;
+	return 0;
+}
+
+void pm_runtime_clock_remove(struct device *dev)
+{
+	struct pm_runtime_data *prd = __to_prd(dev);
+
+	if (!prd)
+		return;
+
+	if (prd->clk) {
+		if (prd->clock_enabled)
+			clk_disable(prd->clk);
+
+		if (prd->clock_active)
+			clk_put(prd->clk);
+	}
+
+	kfree(prd);
+}
+
+static void pm_runtime_clock_init(struct device *dev,
+				  struct pm_runtime_data *prd)
+{
+	prd->clk = clk_get(dev, NULL);
+	if (!IS_ERR(prd->clk)) {
+		prd->clock_active = true;
+		dev_info(dev, "clock managed by runtime PM\n");
+	}
+}
+
+int pm_runtime_clock_suspend(struct device *dev)
+{
+	struct pm_runtime_data *prd = __to_prd(dev);
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (prd) {
+		if (!prd->clk) {
+			dev_err(dev, "clock is not ready for runtime PM\n");
+			pm_runtime_clock_init(dev, prd);
+		}
+
+		if (prd->clock_active) {
+			clk_disable(prd->clk);
+			prd->clock_enabled = false;
+		}
+	}
+
+	return 0;
+}
+
+int pm_runtime_clock_resume(struct device *dev)
+{
+	struct pm_runtime_data *prd = __to_prd(dev);
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (prd) {
+		if (!prd->clk)
+			pm_runtime_clock_init(dev, prd);
+
+		if (prd->clock_active) {
+			clk_enable(prd->clk);
+			prd->clock_enabled = true;
+		}
+	}
+
+	return 0;
+}
+
+static int pm_runtime_clock_notify(struct notifier_block *nb,
+				   unsigned long action, void *data)
+{
+	struct pm_domain_notifier_block *pdnb;
+	struct device *dev = data;
+
+	dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+	pdnb = container_of(nb, struct pm_domain_notifier_block, nb);
+
+	switch (action) {
+	case BUS_NOTIFY_BIND_DRIVER:
+		dev->pwr_domain = pdnb->pwr_domain;
+		pm_runtime_clock_add(dev);
+		break;
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		dev->pwr_domain = NULL;
+		pm_runtime_clock_remove(dev);
+		break;
+	}
+
+	return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+static int pm_runtime_clock_notify(struct notifier_block *nb,
+				   unsigned long action, void *data)
+{
+	struct device *dev = data;
+	struct clk *clk;
+
+	dev_dbg(dev, "%s() %ld\n", __func__, 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 */
+
+void pm_runtime_clock_add_notifier(struct bus_type *bus,
+				   struct pm_domain_notifier_block *pdnb)
+{
+	if (!bus || !pdnb)
+		return;
+
+	pdnb->nb.notifier_call = pm_runtime_clock_notify;
+	bus_register_notifier(bus, &pdnb->nb);
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -229,3 +229,7 @@  config PM_OPP
 	  representing individual voltage domains and provides SOC
 	  implementations a ready to use framework to manage OPPs.
 	  For more information, read <file:Documentation/power/opp.txt>
+
+config PM_RUNTIME_CLK
+	def_bool y
+	depends on PM_RUNTIME && HAVE_CLK
Index: linux-2.6/drivers/base/power/Makefile
===================================================================
--- linux-2.6.orig/drivers/base/power/Makefile
+++ linux-2.6/drivers/base/power/Makefile
@@ -3,6 +3,7 @@  obj-$(CONFIG_PM_SLEEP)	+= main.o wakeup.
 obj-$(CONFIG_PM_RUNTIME)	+= runtime.o
 obj-$(CONFIG_PM_TRACE_RTC)	+= trace.o
 obj-$(CONFIG_PM_OPP)	+= opp.o
+obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
 ccflags-$(CONFIG_PM_VERBOSE)   += -DDEBUG
Index: linux-2.6/include/linux/pm_runtime.h
===================================================================
--- linux-2.6.orig/include/linux/pm_runtime.h
+++ linux-2.6/include/linux/pm_runtime.h
@@ -245,4 +245,31 @@  static inline void pm_runtime_dont_use_a
 	__pm_runtime_use_autosuspend(dev, false);
 }
 
+struct pm_domain_notifier_block {
+	struct notifier_block nb;
+	struct dev_power_domain *pwr_domain;
+};
+
+#ifdef CONFIG_PM_RUNTIME_CLK
+extern int pm_runtime_clock_add(struct device *dev);
+extern void pm_runtime_clock_remove(struct device *dev);
+extern int pm_runtime_clock_suspend(struct device *dev);
+extern int pm_runtime_clock_resume(struct device *dev);
+#else
+static inline int pm_runtime_clock_add(struct device *dev) { return -EINVAL; }
+static inline void pm_runtime_clock_remove(struct device *dev) {}
+#define pm_runtime_clock_suspend	NULL
+#define pm_runtime_clock_resume		NULL
+#endif
+
+#ifdef CONFIG_HAVE_CLK
+extern void pm_runtime_clock_add_notifier(struct bus_type *bus,
+					  struct pm_domain_notifier_block *pdnb);
+#else
+static inline void pm_runtime_clock_add_notifier(struct bus_type *bus,
+					  struct pm_domain_notifier_block *pdnb)
+{
+}
+#endif
+
 #endif
Index: linux-2.6/arch/arm/mach-shmobile/pm_runtime.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm_runtime.c
+++ linux-2.6/arch/arm/mach-shmobile/pm_runtime.c
@@ -21,70 +21,6 @@ 
 #include <linux/slab.h>
 
 #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)
 {
@@ -95,86 +31,21 @@  static int default_platform_runtime_idle
 static struct dev_power_domain default_power_domain = {
 	.ops = {
 		USE_PLATFORM_PM_SLEEP_OPS,
-		.runtime_suspend = default_platform_runtime_suspend,
-		.runtime_resume = default_platform_runtime_resume,
+		.runtime_suspend = pm_runtime_clock_suspend,
+		.runtime_resume = pm_runtime_clock_resume,
 		.runtime_idle = default_platform_runtime_idle,
 	},
 };
 
-static int platform_bus_notify(struct notifier_block *nb,
-			       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))
-				clk_put(prd->clk);
-		}
-		break;
-	}
-
-	return 0;
-}
-
-#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 */
 
-static struct notifier_block platform_bus_notifier = {
-	.notifier_call = platform_bus_notify
+static struct pm_domain_notifier_block platform_bus_notifier = {
+	.pwr_domain = &default_power_domain,
 };
 
 static int __init sh_pm_runtime_init(void)
 {
-	bus_register_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_runtime_clock_add_notifier(&platform_bus_type, &platform_bus_notifier);
 	return 0;
 }
 core_initcall(sh_pm_runtime_init);