2011-04-13 00:05:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

From: Rafael J. Wysocki <[email protected]>

Change the PM core's behavior related to power domains in such a way
that, if a power domain is defined for a given device, its callbacks
will be executed instead of and not in addition to the device
subsystem's PM callbacks.

The idea behind the initial implementation of power domains handling
by the PM core was that power domain callbacks would be executed in
addition to subsystem callbacks, so that it would be possible to
extend the subsystem callbacks by using power domains. It turns out,
however, that this wouldn't be really convenient in some important
situations.

For example, there are systems in which power can only be removed
from entire power domains. On those systems it is not desirable to
execute device drivers' PM callbacks until it is known that power is
going to be removed from the devices in question, which means that
they should be executed by power domain callbacks rather then by
subsystem (e.g. bus type) PM callbacks, because subsystems generally
have no information about what devices belong to which power domain.
Thus, for instance, if the bus type in question is the platform bus
type, its PM callbacks generally should not be called in addition to
power domain callbacks, because they run device drivers' callbacks
unconditionally if defined.

While in principle the default subsystem PM callbacks, or a subset of
them, may be replaced with different functions, it doesn't seem
correct to do so, because that would change the subsystem's behavior
with respect to all devices in the system, regardless of whether or
not they belong to any power domains. Thus, the only remaining
option is to make power domain callbacks take precedence over
subsystem callbacks.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/power/main.c | 64 ++++++++++++++++++++-----------------------
drivers/base/power/runtime.c | 29 ++++++-------------
2 files changed, 41 insertions(+), 52 deletions(-)

Index: linux-2.6/drivers/base/power/runtime.c
===================================================================
--- linux-2.6.orig/drivers/base/power/runtime.c
+++ linux-2.6/drivers/base/power/runtime.c
@@ -168,7 +168,6 @@ static int rpm_check_suspend_allowed(str
static int rpm_idle(struct device *dev, int rpmflags)
{
int (*callback)(struct device *);
- int (*domain_callback)(struct device *);
int retval;

retval = rpm_check_suspend_allowed(dev);
@@ -214,7 +213,9 @@ static int rpm_idle(struct device *dev,

dev->power.idle_notification = true;

- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_idle;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_idle;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_idle;
@@ -223,19 +224,10 @@ static int rpm_idle(struct device *dev,
else
callback = NULL;

- if (dev->pwr_domain)
- domain_callback = dev->pwr_domain->ops.runtime_idle;
- else
- domain_callback = NULL;
-
- if (callback || domain_callback) {
+ if (callback) {
spin_unlock_irq(&dev->power.lock);

- if (domain_callback)
- retval = domain_callback(dev);
-
- if (!retval && callback)
- callback(dev);
+ callback(dev);

spin_lock_irq(&dev->power.lock);
}
@@ -382,7 +374,9 @@ static int rpm_suspend(struct device *de

__update_runtime_status(dev, RPM_SUSPENDING);

- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_suspend;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_suspend;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_suspend;
@@ -400,8 +394,6 @@ static int rpm_suspend(struct device *de
else
pm_runtime_cancel_pending(dev);
} else {
- if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
no_callback:
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);
@@ -582,9 +574,8 @@ static int rpm_resume(struct device *dev
__update_runtime_status(dev, RPM_RESUMING);

if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
-
- if (dev->type && dev->type->pm)
+ callback = dev->pwr_domain->ops.runtime_resume;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_resume;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_resume;
Index: linux-2.6/drivers/base/power/main.c
===================================================================
--- linux-2.6.orig/drivers/base/power/main.c
+++ linux-2.6/drivers/base/power/main.c
@@ -425,10 +425,8 @@ static int device_resume_noirq(struct de

if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "EARLY power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
- if (dev->type && dev->type->pm) {
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "EARLY type ");
error = pm_noirq_op(dev, dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
@@ -516,7 +514,8 @@ static int device_resume(struct device *

if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
}

if (dev->type && dev->type->pm) {
@@ -628,12 +627,11 @@ static void device_complete(struct devic
{
device_lock(dev);

- if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
+ if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "completing power domain ");
- dev->pwr_domain->ops.complete(dev);
- }
-
- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain->ops.complete)
+ dev->pwr_domain->ops.complete(dev);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "completing type ");
if (dev->type->pm->complete)
dev->type->pm->complete(dev);
@@ -731,7 +729,12 @@ static int device_suspend_noirq(struct d
{
int error;

- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "LATE power domain ");
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ if (error)
+ return error;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "LATE type ");
error = pm_noirq_op(dev, dev->type->pm, state);
if (error)
@@ -748,11 +751,6 @@ static int device_suspend_noirq(struct d
return error;
}

- if (dev->pwr_domain) {
- pm_dev_dbg(dev, state, "LATE power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
return 0;
}

@@ -840,21 +838,27 @@ static int __device_suspend(struct devic
goto End;
}

+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "power domain ");
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
+ }
+
if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
- goto Domain;
+ goto End;
}

if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
error = pm_op(dev, dev->class->pm, state);
- goto Domain;
+ goto End;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend);
- goto Domain;
+ goto End;
}
}

@@ -868,12 +872,6 @@ static int __device_suspend(struct devic
}
}

- Domain:
- if (!error && dev->pwr_domain) {
- pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
- }
-
End:
device_unlock(dev);
complete_all(&dev->power.completion);
@@ -964,7 +962,14 @@ static int device_prepare(struct device

device_lock(dev);

- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "preparing power domain ");
+ if (dev->pwr_domain->ops.prepare)
+ error = dev->pwr_domain->ops.prepare(dev);
+ suspend_report_result(dev->pwr_domain->ops.prepare, error);
+ if (error)
+ goto End;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "preparing type ");
if (dev->type->pm->prepare)
error = dev->type->pm->prepare(dev);
@@ -983,13 +988,6 @@ static int device_prepare(struct device
if (dev->bus->pm->prepare)
error = dev->bus->pm->prepare(dev);
suspend_report_result(dev->bus->pm->prepare, error);
- if (error)
- goto End;
- }
-
- if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
- pm_dev_dbg(dev, state, "preparing power domain ");
- dev->pwr_domain->ops.prepare(dev);
}

End:


2011-04-13 14:17:46

by Alan Stern

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Wed, 13 Apr 2011, Rafael J. Wysocki wrote:

> From: Rafael J. Wysocki <[email protected]>
>
> Change the PM core's behavior related to power domains in such a way
> that, if a power domain is defined for a given device, its callbacks
> will be executed instead of and not in addition to the device
> subsystem's PM callbacks.
>
> The idea behind the initial implementation of power domains handling
> by the PM core was that power domain callbacks would be executed in
> addition to subsystem callbacks, so that it would be possible to
> extend the subsystem callbacks by using power domains. It turns out,
> however, that this wouldn't be really convenient in some important
> situations.
>
> For example, there are systems in which power can only be removed
> from entire power domains. On those systems it is not desirable to
> execute device drivers' PM callbacks until it is known that power is
> going to be removed from the devices in question, which means that
> they should be executed by power domain callbacks rather then by
> subsystem (e.g. bus type) PM callbacks, because subsystems generally
> have no information about what devices belong to which power domain.
> Thus, for instance, if the bus type in question is the platform bus
> type, its PM callbacks generally should not be called in addition to
> power domain callbacks, because they run device drivers' callbacks
> unconditionally if defined.

What about systems where it makes sense to execute the subsystem
callbacks even if power isn't going to be removed from the device?
It's quite possible that the subsystem could reduce the device's power
consumption even when the device isn't powered down completely.

Is the extra overhead of invoking the subsystem callback really all
that troublesome?

Alan Stern

2011-04-13 16:15:41

by Grant Likely

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Wed, Apr 13, 2011 at 8:17 AM, Alan Stern <[email protected]> wrote:
> On Wed, 13 Apr 2011, Rafael J. Wysocki wrote:
>
>> From: Rafael J. Wysocki <[email protected]>
>>
>> Change the PM core's behavior related to power domains in such a way
>> that, if a power domain is defined for a given device, its callbacks
>> will be executed instead of and not in addition to the device
>> subsystem's PM callbacks.
>>
>> The idea behind the initial implementation of power domains handling
>> by the PM core was that power domain callbacks would be executed in
>> addition to subsystem callbacks, so that it would be possible to
>> extend the subsystem callbacks by using power domains. ?It turns out,
>> however, that this wouldn't be really convenient in some important
>> situations.
>>
>> For example, there are systems in which power can only be removed
>> from entire power domains. ?On those systems it is not desirable to
>> execute device drivers' PM callbacks until it is known that power is
>> going to be removed from the devices in question, which means that
>> they should be executed by power domain callbacks rather then by
>> subsystem (e.g. bus type) PM callbacks, because subsystems generally
>> have no information about what devices belong to which power domain.
>> Thus, for instance, if the bus type in question is the platform bus
>> type, its PM callbacks generally should not be called in addition to
>> power domain callbacks, because they run device drivers' callbacks
>> unconditionally if defined.
>
> What about systems where it makes sense to execute the subsystem
> callbacks even if power isn't going to be removed from the device?
> It's quite possible that the subsystem could reduce the device's power
> consumption even when the device isn't powered down completely.

The understanding Rafael and I came to was that if a power domain is
attached to a device, then the power domain becomes the responsible
party. Normally this means it will turn around and immediately call
the bus_type pm ops, but it has the option to not call them if for a
particular system it knows better, or to defer calling them.

Basically, if you're using a power domain, it is assumed that the
power domain has particular knowledge about the system, and it should
have the option to override the default behaviour.

>
> Is the extra overhead of invoking the subsystem callback really all
> that troublesome?

It isn't an overhead problem. It's a control & complexity problem.
We could try to implement a heuristic or api to control when the bus
type PM ops should be overridden, but I think it is cleaner to make it
a rule that if you implement a power domain, then that power domain
becomes responsible for all PM operations.

g.

2011-04-14 18:20:48

by Magnus Damm

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

Hi Rafael,

On Wed, Apr 13, 2011 at 9:05 AM, Rafael J. Wysocki <[email protected]> wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Change the PM core's behavior related to power domains in such a way
> that, if a power domain is defined for a given device, its callbacks
> will be executed instead of and not in addition to the device
> subsystem's PM callbacks.

Thanks for your work on this! I'm very happy to see a more fine
grained interface for SoC specific code compared to the weak symbols
and other coarse grained alternatives for the platform bus.

My only thought on this is if we really want to limit ourselves to
only control power domains using these callbacks. I can imagine that
some SoCs want to do other non-power domain specific operations with
these callbacks, and if so, perhaps using the term power domain as
name of the pointer in struct device would be somewhat odd. OTOH, I
really dislike naming discussions in general and I can't really think
of any good names. So it all looks more like a set of system specific
PM override hooks.

Or is there something that is really power domain specific with these hooks?

Thanks,

/ magnus

2011-04-14 22:45:37

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Thursday, April 14, 2011, Magnus Damm wrote:
> Hi Rafael,
>
> On Wed, Apr 13, 2011 at 9:05 AM, Rafael J. Wysocki <[email protected]> wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > Change the PM core's behavior related to power domains in such a way
> > that, if a power domain is defined for a given device, its callbacks
> > will be executed instead of and not in addition to the device
> > subsystem's PM callbacks.
>
> Thanks for your work on this! I'm very happy to see a more fine
> grained interface for SoC specific code compared to the weak symbols
> and other coarse grained alternatives for the platform bus.
>
> My only thought on this is if we really want to limit ourselves to
> only control power domains using these callbacks. I can imagine that
> some SoCs want to do other non-power domain specific operations with
> these callbacks, and if so, perhaps using the term power domain as
> name of the pointer in struct device would be somewhat odd. OTOH, I
> really dislike naming discussions in general and I can't really think
> of any good names. So it all looks more like a set of system specific
> PM override hooks.
>
> Or is there something that is really power domain specific with these hooks?

Not in principle, but I think there is. Namely, if there are two groups
of devices belonging to the same bus type (e.g. platform) that each require
different PM handling, it is legitimate to call them "power domains" (where
"domain" means "a set of devices related to each other because of the way
they need to be handled"), even if they don't share power resources.

Of course, if they do share power resources, the term is just right. :-)

Thanks,
Rafael

2011-04-14 23:12:04

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Wednesday, April 13, 2011, Grant Likely wrote:
> On Wed, Apr 13, 2011 at 8:17 AM, Alan Stern <[email protected]> wrote:
> > On Wed, 13 Apr 2011, Rafael J. Wysocki wrote:
> >
> >> From: Rafael J. Wysocki <[email protected]>
> >>
> >> Change the PM core's behavior related to power domains in such a way
> >> that, if a power domain is defined for a given device, its callbacks
> >> will be executed instead of and not in addition to the device
> >> subsystem's PM callbacks.
> >>
> >> The idea behind the initial implementation of power domains handling
> >> by the PM core was that power domain callbacks would be executed in
> >> addition to subsystem callbacks, so that it would be possible to
> >> extend the subsystem callbacks by using power domains. It turns out,
> >> however, that this wouldn't be really convenient in some important
> >> situations.
> >>
> >> For example, there are systems in which power can only be removed
> >> from entire power domains. On those systems it is not desirable to
> >> execute device drivers' PM callbacks until it is known that power is
> >> going to be removed from the devices in question, which means that
> >> they should be executed by power domain callbacks rather then by
> >> subsystem (e.g. bus type) PM callbacks, because subsystems generally
> >> have no information about what devices belong to which power domain.
> >> Thus, for instance, if the bus type in question is the platform bus
> >> type, its PM callbacks generally should not be called in addition to
> >> power domain callbacks, because they run device drivers' callbacks
> >> unconditionally if defined.
> >
> > What about systems where it makes sense to execute the subsystem
> > callbacks even if power isn't going to be removed from the device?
> > It's quite possible that the subsystem could reduce the device's power
> > consumption even when the device isn't powered down completely.
>
> The understanding Rafael and I came to was that if a power domain is
> attached to a device, then the power domain becomes the responsible
> party. Normally this means it will turn around and immediately call
> the bus_type pm ops, but it has the option to not call them if for a
> particular system it knows better, or to defer calling them.
>
> Basically, if you're using a power domain, it is assumed that the
> power domain has particular knowledge about the system, and it should
> have the option to override the default behaviour.
>
> >
> > Is the extra overhead of invoking the subsystem callback really all
> > that troublesome?
>
> It isn't an overhead problem. It's a control & complexity problem.
> We could try to implement a heuristic or api to control when the bus
> type PM ops should be overridden, but I think it is cleaner to make it
> a rule that if you implement a power domain, then that power domain
> becomes responsible for all PM operations.

Well said. :-)

I'm taking that as an ACK for my patch if you don't mind.

Thanks,
Rafael

2011-04-14 23:18:54

by Rafael J. Wysocki

[permalink] [raw]
Subject: [RFC][PATCH 0/2] Remove __weak definitions of platform PM callbacks

Hi,

The following two patches are based on top of
https://patchwork.kernel.org/patch/702401/ .

[1/2] converts shmobile (both the ARM and SH flavors) to using a power domain
for overriding the platform bus type's PM callbacks, which makes it possible
to remove the __weak definitions of the runtime PM callbacks from platform.c,
which is done in [2/2].

Comments welcome.

Thanks,
Rafael

2011-04-14 23:18:49

by Rafael J. Wysocki

[permalink] [raw]
Subject: [RFC][PATCH 2/2] PM / Platform: Use generic runtime PM callbacks directly

From: Rafael J. Wysocki <[email protected]>

Once shmobile platforms have been converted to using power domains
for overriding the platform bus type's PM callbacks, it isn't
necessary to use the __weakly defined wrappers around the generinc
runtime PM callbacks in the platform bus type any more.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 31 +++----------------------------
1 file changed, 3 insertions(+), 28 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -900,31 +900,6 @@ int platform_pm_restore_noirq(struct dev

#endif /* CONFIG_HIBERNATE_CALLBACKS */

-#ifdef CONFIG_PM_RUNTIME
-
-int __weak platform_pm_runtime_suspend(struct device *dev)
-{
- return pm_generic_runtime_suspend(dev);
-};
-
-int __weak platform_pm_runtime_resume(struct device *dev)
-{
- return pm_generic_runtime_resume(dev);
-};
-
-int __weak platform_pm_runtime_idle(struct device *dev)
-{
- return pm_generic_runtime_idle(dev);
-};
-
-#else /* !CONFIG_PM_RUNTIME */
-
-#define platform_pm_runtime_suspend NULL
-#define platform_pm_runtime_resume NULL
-#define platform_pm_runtime_idle NULL
-
-#endif /* !CONFIG_PM_RUNTIME */
-
static const struct dev_pm_ops platform_dev_pm_ops = {
.prepare = platform_pm_prepare,
.complete = platform_pm_complete,
@@ -940,9 +915,9 @@ static const struct dev_pm_ops platform_
.thaw_noirq = platform_pm_thaw_noirq,
.poweroff_noirq = platform_pm_poweroff_noirq,
.restore_noirq = platform_pm_restore_noirq,
- .runtime_suspend = platform_pm_runtime_suspend,
- .runtime_resume = platform_pm_runtime_resume,
- .runtime_idle = platform_pm_runtime_idle,
+ .runtime_suspend = pm_generic_runtime_suspend,
+ .runtime_resume = pm_generic_runtime_resume,
+ .runtime_idle = pm_generic_runtime_idle,
};

struct bus_type platform_bus_type = {

2011-04-14 23:19:13

by Rafael J. Wysocki

[permalink] [raw]
Subject: [RFC][PATCH 1/2] shmobile: Use power domains for platform runtime PM

From: Rafael J. Wysocki <[email protected]>

shmobile platforms replace the runtime PM callbacks of the platform
bus type with their own routines, but this means that the callbacks
are replaced system-wide. This may not be the right approach if the
platform devices on the system are not of the same type (e.g. some
of them belong to an SoC and the others are located in separate
chips), because in those cases they may require different handling.
Thus it is better to use power domains to override the platform bus
type's PM handling, as it generally is possible to use different
power domains for devices with different PM requirements.

Define a default power domain for shmobile in both the SH and ARM
falvors and use it to override the platform bus type's PM callbacks.
Since the suspend and hibernate callbacks of the new "default" power
domains need to be the same and the platform bus type's suspend and
hibernate callbacks for the time being, export those callbacks so
that can be used outside of the platform bus type code.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 38 ++++++++++++++++----
arch/sh/kernel/cpu/shmobile/pm_runtime.c | 46 +++++++++++++++++++------
drivers/base/platform.c | 57 +++++++++----------------------
include/linux/platform_device.h | 40 +++++++++++++++++++++
4 files changed, 123 insertions(+), 58 deletions(-)

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
@@ -66,11 +66,11 @@ static void platform_pm_runtime_bug(stru
dev_err(dev, "runtime pm suspend before resume\n");
}

-int platform_pm_runtime_suspend(struct device *dev)
+static int default_platform_runtime_suspend(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);

- dev_dbg(dev, "platform_pm_runtime_suspend()\n");
+ dev_dbg(dev, "%s()\n", __func__);

platform_pm_runtime_bug(dev, prd);

@@ -82,11 +82,11 @@ int platform_pm_runtime_suspend(struct d
return 0;
}

-int platform_pm_runtime_resume(struct device *dev)
+static int default_platform_runtime_resume(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);

- dev_dbg(dev, "platform_pm_runtime_resume()\n");
+ dev_dbg(dev, "%s()\n", __func__);

platform_pm_runtime_init(dev, prd);

@@ -98,12 +98,34 @@ int platform_pm_runtime_resume(struct de
return 0;
}

-int platform_pm_runtime_idle(struct device *dev)
+static int default_platform_runtime_idle(struct device *dev)
{
/* suspend synchronously to disable clocks immediately */
return pm_runtime_suspend(dev);
}

+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ .prepare = platform_pm_prepare,
+ .complete = platform_pm_complete,
+ .suspend = platform_pm_suspend,
+ .resume = platform_pm_resume,
+ .freeze = platform_pm_freeze,
+ .thaw = platform_pm_thaw,
+ .poweroff = platform_pm_poweroff,
+ .restore = platform_pm_restore,
+ .suspend_noirq = platform_pm_suspend_noirq,
+ .resume_noirq = platform_pm_resume_noirq,
+ .freeze_noirq = platform_pm_freeze_noirq,
+ .thaw_noirq = platform_pm_thaw_noirq,
+ .poweroff_noirq = platform_pm_poweroff_noirq,
+ .restore_noirq = platform_pm_restore_noirq,
+ .runtime_suspend = default_platform_runtime_suspend,
+ .runtime_resume = default_platform_runtime_resume,
+ .runtime_idle = default_platform_runtime_idle,
+ },
+};
+
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -114,10 +136,12 @@ static int platform_bus_notify(struct no

if (action == BUS_NOTIFY_BIND_DRIVER) {
prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
- if (prd)
+ if (prd) {
devres_add(dev, prd);
- else
+ dev->pwr_domain = &default_power_domain;
+ } else {
dev_err(dev, "unable to alloc memory for runtime pm\n");
+ }
}

return 0;
Index: linux-2.6/arch/sh/kernel/cpu/shmobile/pm_runtime.c
===================================================================
--- linux-2.6.orig/arch/sh/kernel/cpu/shmobile/pm_runtime.c
+++ linux-2.6/arch/sh/kernel/cpu/shmobile/pm_runtime.c
@@ -139,7 +139,7 @@ void platform_pm_runtime_suspend_idle(vo
queue_work(pm_wq, &hwblk_work);
}

-int platform_pm_runtime_suspend(struct device *dev)
+static int default_platform_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pdev_archdata *ad = &pdev->archdata;
@@ -147,7 +147,7 @@ int platform_pm_runtime_suspend(struct d
int hwblk = ad->hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_suspend() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -183,20 +183,20 @@ int platform_pm_runtime_suspend(struct d
mutex_unlock(&ad->mutex);

out:
- dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n",
- hwblk, ret);
+ dev_dbg(dev, "%s() [%d] returns %d\n",
+ __func__, hwblk, ret);

return ret;
}

-int platform_pm_runtime_resume(struct device *dev)
+static int default_platform_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pdev_archdata *ad = &pdev->archdata;
int hwblk = ad->hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_resume() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -228,19 +228,19 @@ int platform_pm_runtime_resume(struct de
*/
mutex_unlock(&ad->mutex);
out:
- dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n",
- hwblk, ret);
+ dev_dbg(dev, "%s() [%d] returns %d\n",
+ __func__, hwblk, ret);

return ret;
}

-int platform_pm_runtime_idle(struct device *dev)
+static int default_platform_runtime_idle(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int hwblk = pdev->archdata.hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_idle() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -252,10 +252,32 @@ int platform_pm_runtime_idle(struct devi
/* suspend synchronously to disable clocks immediately */
ret = pm_runtime_suspend(dev);
out:
- dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk);
+ dev_dbg(dev, "%s() [%d] done!\n", __func__, hwblk);
return ret;
}

+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ .prepare = platform_pm_prepare,
+ .complete = platform_pm_complete,
+ .suspend = platform_pm_suspend,
+ .resume = platform_pm_resume,
+ .freeze = platform_pm_freeze,
+ .thaw = platform_pm_thaw,
+ .poweroff = platform_pm_poweroff,
+ .restore = platform_pm_restore,
+ .suspend_noirq = platform_pm_suspend_noirq,
+ .resume_noirq = platform_pm_resume_noirq,
+ .freeze_noirq = platform_pm_freeze_noirq,
+ .thaw_noirq = platform_pm_thaw_noirq,
+ .poweroff_noirq = platform_pm_poweroff_noirq,
+ .restore_noirq = platform_pm_restore_noirq,
+ .runtime_suspend = default_platform_runtime_suspend,
+ .runtime_resume = default_platform_runtime_resume,
+ .runtime_idle = default_platform_runtime_idle,
+ },
+};
+
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -276,6 +298,7 @@ static int platform_bus_notify(struct no
hwblk_disable(hwblk_info, hwblk);
/* make sure driver re-inits itself once */
__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
+ dev->pwr_domain = &default_power_domain;
break;
/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
case BUS_NOTIFY_BOUND_DRIVER:
@@ -289,6 +312,7 @@ static int platform_bus_notify(struct no
__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
break;
case BUS_NOTIFY_DEL_DEVICE:
+ dev->pwr_domain = NULL;
break;
}
return 0;
Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -667,7 +667,7 @@ static int platform_legacy_resume(struct
return ret;
}

-static int platform_pm_prepare(struct device *dev)
+int platform_pm_prepare(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -678,7 +678,7 @@ static int platform_pm_prepare(struct de
return ret;
}

-static void platform_pm_complete(struct device *dev)
+void platform_pm_complete(struct device *dev)
{
struct device_driver *drv = dev->driver;

@@ -686,16 +686,11 @@ static void platform_pm_complete(struct
drv->pm->complete(dev);
}

-#else /* !CONFIG_PM_SLEEP */
-
-#define platform_pm_prepare NULL
-#define platform_pm_complete NULL
-
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_SLEEP */

#ifdef CONFIG_SUSPEND

-int __weak platform_pm_suspend(struct device *dev)
+int platform_pm_suspend(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -713,7 +708,7 @@ int __weak platform_pm_suspend(struct de
return ret;
}

-int __weak platform_pm_suspend_noirq(struct device *dev)
+int platform_pm_suspend_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -729,7 +724,7 @@ int __weak platform_pm_suspend_noirq(str
return ret;
}

-int __weak platform_pm_resume(struct device *dev)
+int platform_pm_resume(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -747,7 +742,7 @@ int __weak platform_pm_resume(struct dev
return ret;
}

-int __weak platform_pm_resume_noirq(struct device *dev)
+int platform_pm_resume_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -763,18 +758,11 @@ int __weak platform_pm_resume_noirq(stru
return ret;
}

-#else /* !CONFIG_SUSPEND */
-
-#define platform_pm_suspend NULL
-#define platform_pm_resume NULL
-#define platform_pm_suspend_noirq NULL
-#define platform_pm_resume_noirq NULL
-
-#endif /* !CONFIG_SUSPEND */
+#endif /* CONFIG_SUSPEND */

#ifdef CONFIG_HIBERNATE_CALLBACKS

-static int platform_pm_freeze(struct device *dev)
+int platform_pm_freeze(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -792,7 +780,7 @@ static int platform_pm_freeze(struct dev
return ret;
}

-static int platform_pm_freeze_noirq(struct device *dev)
+int platform_pm_freeze_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -808,7 +796,7 @@ static int platform_pm_freeze_noirq(stru
return ret;
}

-static int platform_pm_thaw(struct device *dev)
+int platform_pm_thaw(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -826,7 +814,7 @@ static int platform_pm_thaw(struct devic
return ret;
}

-static int platform_pm_thaw_noirq(struct device *dev)
+int platform_pm_thaw_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -842,7 +830,7 @@ static int platform_pm_thaw_noirq(struct
return ret;
}

-static int platform_pm_poweroff(struct device *dev)
+int platform_pm_poweroff(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -860,7 +848,7 @@ static int platform_pm_poweroff(struct d
return ret;
}

-static int platform_pm_poweroff_noirq(struct device *dev)
+int platform_pm_poweroff_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -876,7 +864,7 @@ static int platform_pm_poweroff_noirq(st
return ret;
}

-static int platform_pm_restore(struct device *dev)
+int platform_pm_restore(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -894,7 +882,7 @@ static int platform_pm_restore(struct de
return ret;
}

-static int platform_pm_restore_noirq(struct device *dev)
+int platform_pm_restore_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -910,18 +898,7 @@ static int platform_pm_restore_noirq(str
return ret;
}

-#else /* !CONFIG_HIBERNATE_CALLBACKS */
-
-#define platform_pm_freeze NULL
-#define platform_pm_thaw NULL
-#define platform_pm_poweroff NULL
-#define platform_pm_restore NULL
-#define platform_pm_freeze_noirq NULL
-#define platform_pm_thaw_noirq NULL
-#define platform_pm_poweroff_noirq NULL
-#define platform_pm_restore_noirq NULL
-
-#endif /* !CONFIG_HIBERNATE_CALLBACKS */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */

#ifdef CONFIG_PM_RUNTIME

Index: linux-2.6/include/linux/platform_device.h
===================================================================
--- linux-2.6.orig/include/linux/platform_device.h
+++ linux-2.6/include/linux/platform_device.h
@@ -205,4 +205,44 @@ static inline char *early_platform_drive
}
#endif /* MODULE */

+#ifdef CONFIG_PM_SLEEP
+extern int platform_pm_prepare(struct device *dev);
+extern void platform_pm_complete(struct device *dev);
+#else
+#define platform_pm_prepare NULL
+#define platform_pm_complete NULL
+#endif
+
+#ifdef CONFIG_SUSPEND
+extern int platform_pm_suspend(struct device *dev);
+extern int platform_pm_suspend_noirq(struct device *dev);
+extern int platform_pm_resume(struct device *dev);
+extern int platform_pm_resume_noirq(struct device *dev);
+#else
+#define platform_pm_suspend NULL
+#define platform_pm_resume NULL
+#define platform_pm_suspend_noirq NULL
+#define platform_pm_resume_noirq NULL
+#endif
+
+#ifdef CONFIG_HIBERNATE_CALLBACKS
+extern int platform_pm_freeze(struct device *dev);
+extern int platform_pm_freeze_noirq(struct device *dev);
+extern int platform_pm_thaw(struct device *dev);
+extern int platform_pm_thaw_noirq(struct device *dev);
+extern int platform_pm_poweroff(struct device *dev);
+extern int platform_pm_poweroff_noirq(struct device *dev);
+extern int platform_pm_restore(struct device *dev);
+extern int platform_pm_restore_noirq(struct device *dev);
+#else
+#define platform_pm_freeze NULL
+#define platform_pm_thaw NULL
+#define platform_pm_poweroff NULL
+#define platform_pm_restore NULL
+#define platform_pm_freeze_noirq NULL
+#define platform_pm_thaw_noirq NULL
+#define platform_pm_poweroff_noirq NULL
+#define platform_pm_restore_noirq NULL
+#endif
+
#endif /* _PLATFORM_DEVICE_H_ */

2011-04-15 14:34:50

by Alan Stern

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Fri, 15 Apr 2011, Rafael J. Wysocki wrote:

> On Thursday, April 14, 2011, Magnus Damm wrote:

> > My only thought on this is if we really want to limit ourselves to
> > only control power domains using these callbacks. I can imagine that
> > some SoCs want to do other non-power domain specific operations with
> > these callbacks, and if so, perhaps using the term power domain as
> > name of the pointer in struct device would be somewhat odd. OTOH, I
> > really dislike naming discussions in general and I can't really think
> > of any good names. So it all looks more like a set of system specific
> > PM override hooks.
> >
> > Or is there something that is really power domain specific with these hooks?
>
> Not in principle, but I think there is. Namely, if there are two groups
> of devices belonging to the same bus type (e.g. platform) that each require
> different PM handling, it is legitimate to call them "power domains" (where
> "domain" means "a set of devices related to each other because of the way
> they need to be handled"), even if they don't share power resources.
>
> Of course, if they do share power resources, the term is just right. :-)

They could be called "PM domains" instead of "power domains". That's
legitimate because they do get used by the PM core, even if they don't
literally involve groups of devices sharing the same power supply.

Alan Stern

2011-04-15 14:39:03

by Grant Likely

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Thu, Apr 14, 2011 at 5:12 PM, Rafael J. Wysocki <[email protected]> wrote:
> On Wednesday, April 13, 2011, Grant Likely wrote:
>> On Wed, Apr 13, 2011 at 8:17 AM, Alan Stern <[email protected]> wrote:
>> > On Wed, 13 Apr 2011, Rafael J. Wysocki wrote:
>> >
>> >> From: Rafael J. Wysocki <[email protected]>
>> >>
>> >> Change the PM core's behavior related to power domains in such a way
>> >> that, if a power domain is defined for a given device, its callbacks
>> >> will be executed instead of and not in addition to the device
>> >> subsystem's PM callbacks.
>> >>
>> >> The idea behind the initial implementation of power domains handling
>> >> by the PM core was that power domain callbacks would be executed in
>> >> addition to subsystem callbacks, so that it would be possible to
>> >> extend the subsystem callbacks by using power domains. ?It turns out,
>> >> however, that this wouldn't be really convenient in some important
>> >> situations.
>> >>
>> >> For example, there are systems in which power can only be removed
>> >> from entire power domains. ?On those systems it is not desirable to
>> >> execute device drivers' PM callbacks until it is known that power is
>> >> going to be removed from the devices in question, which means that
>> >> they should be executed by power domain callbacks rather then by
>> >> subsystem (e.g. bus type) PM callbacks, because subsystems generally
>> >> have no information about what devices belong to which power domain.
>> >> Thus, for instance, if the bus type in question is the platform bus
>> >> type, its PM callbacks generally should not be called in addition to
>> >> power domain callbacks, because they run device drivers' callbacks
>> >> unconditionally if defined.
>> >
>> > What about systems where it makes sense to execute the subsystem
>> > callbacks even if power isn't going to be removed from the device?
>> > It's quite possible that the subsystem could reduce the device's power
>> > consumption even when the device isn't powered down completely.
>>
>> The understanding Rafael and I came to was that if a power domain is
>> attached to a device, then the power domain becomes the responsible
>> party. ?Normally this means it will turn around and immediately call
>> the bus_type pm ops, but it has the option to not call them if for a
>> particular system it knows better, or to defer calling them.
>>
>> Basically, if you're using a power domain, it is assumed that the
>> power domain has particular knowledge about the system, and it should
>> have the option to override the default behaviour.
>>
>> >
>> > Is the extra overhead of invoking the subsystem callback really all
>> > that troublesome?
>>
>> It isn't an overhead problem. ?It's a control & complexity problem.
>> We could try to implement a heuristic or api to control when the bus
>> type PM ops should be overridden, but I think it is cleaner to make it
>> a rule that if you implement a power domain, then that power domain
>> becomes responsible for all PM operations.
>
> Well said. :-)
>
> I'm taking that as an ACK for my patch if you don't mind.

And so you should.

g.

2011-04-15 14:39:24

by Alan Stern

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Fri, 15 Apr 2011, Rafael J. Wysocki wrote:

> On Wednesday, April 13, 2011, Grant Likely wrote:
> > On Wed, Apr 13, 2011 at 8:17 AM, Alan Stern <[email protected]> wrote:
> > > On Wed, 13 Apr 2011, Rafael J. Wysocki wrote:
> > >
> > >> From: Rafael J. Wysocki <[email protected]>
> > >>
> > >> Change the PM core's behavior related to power domains in such a way
> > >> that, if a power domain is defined for a given device, its callbacks
> > >> will be executed instead of and not in addition to the device
> > >> subsystem's PM callbacks.
> > >>
> > >> The idea behind the initial implementation of power domains handling
> > >> by the PM core was that power domain callbacks would be executed in
> > >> addition to subsystem callbacks, so that it would be possible to
> > >> extend the subsystem callbacks by using power domains. It turns out,
> > >> however, that this wouldn't be really convenient in some important
> > >> situations.
> > >>
> > >> For example, there are systems in which power can only be removed
> > >> from entire power domains. On those systems it is not desirable to
> > >> execute device drivers' PM callbacks until it is known that power is
> > >> going to be removed from the devices in question, which means that
> > >> they should be executed by power domain callbacks rather then by
> > >> subsystem (e.g. bus type) PM callbacks, because subsystems generally
> > >> have no information about what devices belong to which power domain.
> > >> Thus, for instance, if the bus type in question is the platform bus
> > >> type, its PM callbacks generally should not be called in addition to
> > >> power domain callbacks, because they run device drivers' callbacks
> > >> unconditionally if defined.
> > >
> > > What about systems where it makes sense to execute the subsystem
> > > callbacks even if power isn't going to be removed from the device?
> > > It's quite possible that the subsystem could reduce the device's power
> > > consumption even when the device isn't powered down completely.
> >
> > The understanding Rafael and I came to was that if a power domain is
> > attached to a device, then the power domain becomes the responsible
> > party. Normally this means it will turn around and immediately call
> > the bus_type pm ops, but it has the option to not call them if for a
> > particular system it knows better, or to defer calling them.
> >
> > Basically, if you're using a power domain, it is assumed that the
> > power domain has particular knowledge about the system, and it should
> > have the option to override the default behaviour.
> >
> > >
> > > Is the extra overhead of invoking the subsystem callback really all
> > > that troublesome?
> >
> > It isn't an overhead problem. It's a control & complexity problem.
> > We could try to implement a heuristic or api to control when the bus
> > type PM ops should be overridden, but I think it is cleaner to make it
> > a rule that if you implement a power domain, then that power domain
> > becomes responsible for all PM operations.
>
> Well said. :-)
>
> I'm taking that as an ACK for my patch if you don't mind.

Grant presented a convincing explanation. I have no objections.

Alan Stern

2011-04-15 23:18:18

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Friday, April 15, 2011, Alan Stern wrote:
> On Fri, 15 Apr 2011, Rafael J. Wysocki wrote:
>
> > On Thursday, April 14, 2011, Magnus Damm wrote:
>
> > > My only thought on this is if we really want to limit ourselves to
> > > only control power domains using these callbacks. I can imagine that
> > > some SoCs want to do other non-power domain specific operations with
> > > these callbacks, and if so, perhaps using the term power domain as
> > > name of the pointer in struct device would be somewhat odd. OTOH, I
> > > really dislike naming discussions in general and I can't really think
> > > of any good names. So it all looks more like a set of system specific
> > > PM override hooks.
> > >
> > > Or is there something that is really power domain specific with these hooks?
> >
> > Not in principle, but I think there is. Namely, if there are two groups
> > of devices belonging to the same bus type (e.g. platform) that each require
> > different PM handling, it is legitimate to call them "power domains" (where
> > "domain" means "a set of devices related to each other because of the way
> > they need to be handled"), even if they don't share power resources.
> >
> > Of course, if they do share power resources, the term is just right. :-)
>
> They could be called "PM domains" instead of "power domains". That's
> legitimate because they do get used by the PM core, even if they don't
> literally involve groups of devices sharing the same power supply.

Well, "power domain" can be regarded as a short form of "power management
domain", which makes the point kind of moot. ;-)

Rafael

2011-04-16 22:24:19

by Kevin Hilman

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Sat, 2011-04-16 at 01:18 +0200, Rafael J. Wysocki wrote:
> On Friday, April 15, 2011, Alan Stern wrote:
> > On Fri, 15 Apr 2011, Rafael J. Wysocki wrote:
> >
> > > On Thursday, April 14, 2011, Magnus Damm wrote:
> >
> > > > My only thought on this is if we really want to limit ourselves to
> > > > only control power domains using these callbacks. I can imagine that
> > > > some SoCs want to do other non-power domain specific operations with
> > > > these callbacks, and if so, perhaps using the term power domain as
> > > > name of the pointer in struct device would be somewhat odd. OTOH, I
> > > > really dislike naming discussions in general and I can't really think
> > > > of any good names. So it all looks more like a set of system specific
> > > > PM override hooks.
> > > >
> > > > Or is there something that is really power domain specific with these hooks?
> > >
> > > Not in principle, but I think there is. Namely, if there are two groups
> > > of devices belonging to the same bus type (e.g. platform) that each require
> > > different PM handling, it is legitimate to call them "power domains" (where
> > > "domain" means "a set of devices related to each other because of the way
> > > they need to be handled"), even if they don't share power resources.
> > >
> > > Of course, if they do share power resources, the term is just right. :-)
> >
> > They could be called "PM domains" instead of "power domains". That's
> > legitimate because they do get used by the PM core, even if they don't
> > literally involve groups of devices sharing the same power supply.
>
> Well, "power domain" can be regarded as a short form of "power management
> domain", which makes the point kind of moot. ;-)

Except that on most embedded SoCs, the term power domain has specific
meaning in hardware, so using something other than that is preferred
IMO.

What this really is is just per-device dev_pm_ops, which platform code
can use to group devices however it likes.

So rather than call it a power domain, or a PM domain, we could also
just add a struct dev_pm_ops to struct device.

Kevin

2011-04-16 22:24:33

by Kevin Hilman

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Wed, 2011-04-13 at 02:05 +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> Change the PM core's behavior related to power domains in such a way
> that, if a power domain is defined for a given device, its callbacks
> will be executed instead of and not in addition to the device
> subsystem's PM callbacks.
>
> The idea behind the initial implementation of power domains handling
> by the PM core was that power domain callbacks would be executed in
> addition to subsystem callbacks, so that it would be possible to
> extend the subsystem callbacks by using power domains. It turns out,
> however, that this wouldn't be really convenient in some important
> situations.
>
> For example, there are systems in which power can only be removed
> from entire power domains. On those systems it is not desirable to
> execute device drivers' PM callbacks until it is known that power is
> going to be removed from the devices in question, which means that
> they should be executed by power domain callbacks rather then by
> subsystem (e.g. bus type) PM callbacks, because subsystems generally
> have no information about what devices belong to which power domain.
> Thus, for instance, if the bus type in question is the platform bus
> type, its PM callbacks generally should not be called in addition to
> power domain callbacks, because they run device drivers' callbacks
> unconditionally if defined.
>
> While in principle the default subsystem PM callbacks, or a subset of
> them, may be replaced with different functions, it doesn't seem
> correct to do so, because that would change the subsystem's behavior
> with respect to all devices in the system, regardless of whether or
> not they belong to any power domains. Thus, the only remaining
> option is to make power domain callbacks take precedence over
> subsystem callbacks.
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>

Leaving the issue of to (re)name this field aside:

Acked-by: Kevin Hilman <[email protected]>

2011-04-16 23:11:54

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [RFC][PATCH] PM: Make power domain callbacks take precedence over subsystem ones

On Saturday, April 16, 2011, Kevin Hilman wrote:
> On Sat, 2011-04-16 at 01:18 +0200, Rafael J. Wysocki wrote:
> > On Friday, April 15, 2011, Alan Stern wrote:
> > > On Fri, 15 Apr 2011, Rafael J. Wysocki wrote:
> > >
> > > > On Thursday, April 14, 2011, Magnus Damm wrote:
> > >
> > > > > My only thought on this is if we really want to limit ourselves to
> > > > > only control power domains using these callbacks. I can imagine that
> > > > > some SoCs want to do other non-power domain specific operations with
> > > > > these callbacks, and if so, perhaps using the term power domain as
> > > > > name of the pointer in struct device would be somewhat odd. OTOH, I
> > > > > really dislike naming discussions in general and I can't really think
> > > > > of any good names. So it all looks more like a set of system specific
> > > > > PM override hooks.
> > > > >
> > > > > Or is there something that is really power domain specific with these hooks?
> > > >
> > > > Not in principle, but I think there is. Namely, if there are two groups
> > > > of devices belonging to the same bus type (e.g. platform) that each require
> > > > different PM handling, it is legitimate to call them "power domains" (where
> > > > "domain" means "a set of devices related to each other because of the way
> > > > they need to be handled"), even if they don't share power resources.
> > > >
> > > > Of course, if they do share power resources, the term is just right. :-)
> > >
> > > They could be called "PM domains" instead of "power domains". That's
> > > legitimate because they do get used by the PM core, even if they don't
> > > literally involve groups of devices sharing the same power supply.
> >
> > Well, "power domain" can be regarded as a short form of "power management
> > domain", which makes the point kind of moot. ;-)
>
> Except that on most embedded SoCs, the term power domain has specific
> meaning in hardware, so using something other than that is preferred
> IMO.
>
> What this really is is just per-device dev_pm_ops, which platform code
> can use to group devices however it likes.
>
> So rather than call it a power domain, or a PM domain, we could also
> just add a struct dev_pm_ops to struct device.

Well, right. But in the future this thing will be necessary to provide
additional information to _real_ power domain PM callbacks. So it will
be more than just struct dev_pm_ops.

Thanks,
Rafael

2011-04-16 23:44:53

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 0/9] PM: Rework shmobile and OMAP runtime PM using power domains

Hi,

The following series of patches is my attempt to consolidate the shmobile and
OMAP runtime PM code using power domains so that it is not necessary to
replace the platform bus type's runtime PM callbacks any more.

Some of the patches have already been posted, some are new and some are
from Kevin. :-)

[1/9] - Make power domain callbacks take precedence over subsystem ones
(this one has been discussed already).

[2/9] - Export the platform bus type's default PM callbacks so that they
can be used in power domain objects.

[3/9] - Use a default power domain for runtime PM on shmobile (both ARM
and SH flavors) instead of replacing the platform bus type's
runtime PM callbacks (using __weak).

[4/9] - Use generic runtime PM callbacks directly in the platform bus type.

[5/9] - Move OMAP2 runtime PM implementation to using a power domain object
(patch from Kevin rebased on top of [1-4/9]).

[6/9] - Add subsystem data field to struct dev_pm_info.

[7/9] - Introduce generic clock manipulation routines for runtime PM and
convert ARM shmobile to using them.

[8/9] - Move OMAP1 runtime PM to the new core infrastructure.

[9/9] - Remove platform_bus_set_pm_ops() (patch from Kevin).

Comments welcome.

Thanks,
Rafael

2011-04-16 23:45:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 2/9] PM: Export platform bus type's default PM callbacks

From: Rafael J. Wysocki <[email protected]>

Export the default PM callbacks defined for the platform bus type so
that they can be used by power domains for suspending and resuming
platform devices in the future.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 72 ++++++++++------------------------------
include/linux/platform_device.h | 60 +++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+), 54 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -667,7 +667,7 @@ static int platform_legacy_resume(struct
return ret;
}

-static int platform_pm_prepare(struct device *dev)
+int platform_pm_prepare(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -678,7 +678,7 @@ static int platform_pm_prepare(struct de
return ret;
}

-static void platform_pm_complete(struct device *dev)
+void platform_pm_complete(struct device *dev)
{
struct device_driver *drv = dev->driver;

@@ -686,16 +686,11 @@ static void platform_pm_complete(struct
drv->pm->complete(dev);
}

-#else /* !CONFIG_PM_SLEEP */
-
-#define platform_pm_prepare NULL
-#define platform_pm_complete NULL
-
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_SLEEP */

#ifdef CONFIG_SUSPEND

-int __weak platform_pm_suspend(struct device *dev)
+int platform_pm_suspend(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -713,7 +708,7 @@ int __weak platform_pm_suspend(struct de
return ret;
}

-int __weak platform_pm_suspend_noirq(struct device *dev)
+int platform_pm_suspend_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -729,7 +724,7 @@ int __weak platform_pm_suspend_noirq(str
return ret;
}

-int __weak platform_pm_resume(struct device *dev)
+int platform_pm_resume(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -747,7 +742,7 @@ int __weak platform_pm_resume(struct dev
return ret;
}

-int __weak platform_pm_resume_noirq(struct device *dev)
+int platform_pm_resume_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -763,18 +758,11 @@ int __weak platform_pm_resume_noirq(stru
return ret;
}

-#else /* !CONFIG_SUSPEND */
-
-#define platform_pm_suspend NULL
-#define platform_pm_resume NULL
-#define platform_pm_suspend_noirq NULL
-#define platform_pm_resume_noirq NULL
-
-#endif /* !CONFIG_SUSPEND */
+#endif /* CONFIG_SUSPEND */

#ifdef CONFIG_HIBERNATE_CALLBACKS

-static int platform_pm_freeze(struct device *dev)
+int platform_pm_freeze(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -792,7 +780,7 @@ static int platform_pm_freeze(struct dev
return ret;
}

-static int platform_pm_freeze_noirq(struct device *dev)
+int platform_pm_freeze_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -808,7 +796,7 @@ static int platform_pm_freeze_noirq(stru
return ret;
}

-static int platform_pm_thaw(struct device *dev)
+int platform_pm_thaw(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -826,7 +814,7 @@ static int platform_pm_thaw(struct devic
return ret;
}

-static int platform_pm_thaw_noirq(struct device *dev)
+int platform_pm_thaw_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -842,7 +830,7 @@ static int platform_pm_thaw_noirq(struct
return ret;
}

-static int platform_pm_poweroff(struct device *dev)
+int platform_pm_poweroff(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -860,7 +848,7 @@ static int platform_pm_poweroff(struct d
return ret;
}

-static int platform_pm_poweroff_noirq(struct device *dev)
+int platform_pm_poweroff_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -876,7 +864,7 @@ static int platform_pm_poweroff_noirq(st
return ret;
}

-static int platform_pm_restore(struct device *dev)
+int platform_pm_restore(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -894,7 +882,7 @@ static int platform_pm_restore(struct de
return ret;
}

-static int platform_pm_restore_noirq(struct device *dev)
+int platform_pm_restore_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -910,18 +898,7 @@ static int platform_pm_restore_noirq(str
return ret;
}

-#else /* !CONFIG_HIBERNATE_CALLBACKS */
-
-#define platform_pm_freeze NULL
-#define platform_pm_thaw NULL
-#define platform_pm_poweroff NULL
-#define platform_pm_restore NULL
-#define platform_pm_freeze_noirq NULL
-#define platform_pm_thaw_noirq NULL
-#define platform_pm_poweroff_noirq NULL
-#define platform_pm_restore_noirq NULL
-
-#endif /* !CONFIG_HIBERNATE_CALLBACKS */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */

#ifdef CONFIG_PM_RUNTIME

@@ -949,20 +926,7 @@ int __weak platform_pm_runtime_idle(stru
#endif /* !CONFIG_PM_RUNTIME */

static const struct dev_pm_ops platform_dev_pm_ops = {
- .prepare = platform_pm_prepare,
- .complete = platform_pm_complete,
- .suspend = platform_pm_suspend,
- .resume = platform_pm_resume,
- .freeze = platform_pm_freeze,
- .thaw = platform_pm_thaw,
- .poweroff = platform_pm_poweroff,
- .restore = platform_pm_restore,
- .suspend_noirq = platform_pm_suspend_noirq,
- .resume_noirq = platform_pm_resume_noirq,
- .freeze_noirq = platform_pm_freeze_noirq,
- .thaw_noirq = platform_pm_thaw_noirq,
- .poweroff_noirq = platform_pm_poweroff_noirq,
- .restore_noirq = platform_pm_restore_noirq,
+ USE_PLATFORM_PM_SLEEP_OPS,
.runtime_suspend = platform_pm_runtime_suspend,
.runtime_resume = platform_pm_runtime_resume,
.runtime_idle = platform_pm_runtime_idle,
Index: linux-2.6/include/linux/platform_device.h
===================================================================
--- linux-2.6.orig/include/linux/platform_device.h
+++ linux-2.6/include/linux/platform_device.h
@@ -205,4 +205,64 @@ static inline char *early_platform_drive
}
#endif /* MODULE */

+#ifdef CONFIG_PM_SLEEP
+extern int platform_pm_prepare(struct device *dev);
+extern void platform_pm_complete(struct device *dev);
+#else
+#define platform_pm_prepare NULL
+#define platform_pm_complete NULL
+#endif
+
+#ifdef CONFIG_SUSPEND
+extern int platform_pm_suspend(struct device *dev);
+extern int platform_pm_suspend_noirq(struct device *dev);
+extern int platform_pm_resume(struct device *dev);
+extern int platform_pm_resume_noirq(struct device *dev);
+#else
+#define platform_pm_suspend NULL
+#define platform_pm_resume NULL
+#define platform_pm_suspend_noirq NULL
+#define platform_pm_resume_noirq NULL
+#endif
+
+#ifdef CONFIG_HIBERNATE_CALLBACKS
+extern int platform_pm_freeze(struct device *dev);
+extern int platform_pm_freeze_noirq(struct device *dev);
+extern int platform_pm_thaw(struct device *dev);
+extern int platform_pm_thaw_noirq(struct device *dev);
+extern int platform_pm_poweroff(struct device *dev);
+extern int platform_pm_poweroff_noirq(struct device *dev);
+extern int platform_pm_restore(struct device *dev);
+extern int platform_pm_restore_noirq(struct device *dev);
+#else
+#define platform_pm_freeze NULL
+#define platform_pm_thaw NULL
+#define platform_pm_poweroff NULL
+#define platform_pm_restore NULL
+#define platform_pm_freeze_noirq NULL
+#define platform_pm_thaw_noirq NULL
+#define platform_pm_poweroff_noirq NULL
+#define platform_pm_restore_noirq NULL
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+#define USE_PLATFORM_PM_SLEEP_OPS \
+ .prepare = platform_pm_prepare, \
+ .complete = platform_pm_complete, \
+ .suspend = platform_pm_suspend, \
+ .resume = platform_pm_resume, \
+ .freeze = platform_pm_freeze, \
+ .thaw = platform_pm_thaw, \
+ .poweroff = platform_pm_poweroff, \
+ .restore = platform_pm_restore, \
+ .suspend_noirq = platform_pm_suspend_noirq, \
+ .resume_noirq = platform_pm_resume_noirq, \
+ .freeze_noirq = platform_pm_freeze_noirq, \
+ .thaw_noirq = platform_pm_thaw_noirq, \
+ .poweroff_noirq = platform_pm_poweroff_noirq, \
+ .restore_noirq = platform_pm_restore_noirq
+#else
+#define USE_PLATFORM_PM_SLEEP_OPS
+#endif
+
#endif /* _PLATFORM_DEVICE_H_ */

2011-04-16 23:45:38

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 3/9] shmobile: Use power domains for platform runtime PM

From: Rafael J. Wysocki <[email protected]>

shmobile platforms replace the runtime PM callbacks of the platform
bus type with their own routines, but this means that the callbacks
are replaced system-wide. This may not be the right approach if the
platform devices on the system are not of the same type (e.g. some
of them belong to an SoC and the others are located in separate
chips), because in those cases they may require different handling.
Thus it is better to use power domains to override the platform bus
type's PM handling, as it generally is possible to use different
power domains for devices with different PM requirements.

Define a default power domain for shmobile in both the SH and ARM
falvors and use it to override the platform bus type's PM callbacks.
Since the suspend and hibernate callbacks of the new "default" power
domains need to be the same and the platform bus type's suspend and
hibernate callbacks for the time being, export those callbacks so
that can be used outside of the platform bus type code.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 25 ++++++++++++++++-------
arch/sh/kernel/cpu/shmobile/pm_runtime.c | 33 ++++++++++++++++++++-----------
2 files changed, 40 insertions(+), 18 deletions(-)

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
@@ -66,11 +66,11 @@ static void platform_pm_runtime_bug(stru
dev_err(dev, "runtime pm suspend before resume\n");
}

-int platform_pm_runtime_suspend(struct device *dev)
+static int default_platform_runtime_suspend(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);

- dev_dbg(dev, "platform_pm_runtime_suspend()\n");
+ dev_dbg(dev, "%s()\n", __func__);

platform_pm_runtime_bug(dev, prd);

@@ -82,11 +82,11 @@ int platform_pm_runtime_suspend(struct d
return 0;
}

-int platform_pm_runtime_resume(struct device *dev)
+static int default_platform_runtime_resume(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);

- dev_dbg(dev, "platform_pm_runtime_resume()\n");
+ dev_dbg(dev, "%s()\n", __func__);

platform_pm_runtime_init(dev, prd);

@@ -98,12 +98,21 @@ int platform_pm_runtime_resume(struct de
return 0;
}

-int platform_pm_runtime_idle(struct device *dev)
+static int default_platform_runtime_idle(struct device *dev)
{
/* suspend synchronously to disable clocks immediately */
return pm_runtime_suspend(dev);
}

+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_idle = default_platform_runtime_idle,
+ },
+};
+
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -114,10 +123,12 @@ static int platform_bus_notify(struct no

if (action == BUS_NOTIFY_BIND_DRIVER) {
prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
- if (prd)
+ if (prd) {
devres_add(dev, prd);
- else
+ dev->pwr_domain = &default_power_domain;
+ } else {
dev_err(dev, "unable to alloc memory for runtime pm\n");
+ }
}

return 0;
Index: linux-2.6/arch/sh/kernel/cpu/shmobile/pm_runtime.c
===================================================================
--- linux-2.6.orig/arch/sh/kernel/cpu/shmobile/pm_runtime.c
+++ linux-2.6/arch/sh/kernel/cpu/shmobile/pm_runtime.c
@@ -139,7 +139,7 @@ void platform_pm_runtime_suspend_idle(vo
queue_work(pm_wq, &hwblk_work);
}

-int platform_pm_runtime_suspend(struct device *dev)
+static int default_platform_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pdev_archdata *ad = &pdev->archdata;
@@ -147,7 +147,7 @@ int platform_pm_runtime_suspend(struct d
int hwblk = ad->hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_suspend() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -183,20 +183,20 @@ int platform_pm_runtime_suspend(struct d
mutex_unlock(&ad->mutex);

out:
- dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n",
- hwblk, ret);
+ dev_dbg(dev, "%s() [%d] returns %d\n",
+ __func__, hwblk, ret);

return ret;
}

-int platform_pm_runtime_resume(struct device *dev)
+static int default_platform_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pdev_archdata *ad = &pdev->archdata;
int hwblk = ad->hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_resume() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -228,19 +228,19 @@ int platform_pm_runtime_resume(struct de
*/
mutex_unlock(&ad->mutex);
out:
- dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n",
- hwblk, ret);
+ dev_dbg(dev, "%s() [%d] returns %d\n",
+ __func__, hwblk, ret);

return ret;
}

-int platform_pm_runtime_idle(struct device *dev)
+static int default_platform_runtime_idle(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int hwblk = pdev->archdata.hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_idle() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -252,10 +252,19 @@ int platform_pm_runtime_idle(struct devi
/* suspend synchronously to disable clocks immediately */
ret = pm_runtime_suspend(dev);
out:
- dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk);
+ dev_dbg(dev, "%s() [%d] done!\n", __func__, hwblk);
return ret;
}

+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_idle = default_platform_runtime_idle,
+ },
+};
+
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -276,6 +285,7 @@ static int platform_bus_notify(struct no
hwblk_disable(hwblk_info, hwblk);
/* make sure driver re-inits itself once */
__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
+ dev->pwr_domain = &default_power_domain;
break;
/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
case BUS_NOTIFY_BOUND_DRIVER:
@@ -289,6 +299,7 @@ static int platform_bus_notify(struct no
__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
break;
case BUS_NOTIFY_DEL_DEVICE:
+ dev->pwr_domain = NULL;
break;
}
return 0;

2011-04-16 23:45:30

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 4/9] PM / Platform: Use generic runtime PM callbacks directly

From: Rafael J. Wysocki <[email protected]>

Once shmobile platforms have been converted to using power domains
for overriding the platform bus type's PM callbacks, it isn't
necessary to use the __weakly defined wrappers around the generinc
runtime PM callbacks in the platform bus type any more.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 31 +++----------------------------
1 file changed, 3 insertions(+), 28 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -900,36 +900,11 @@ int platform_pm_restore_noirq(struct dev

#endif /* CONFIG_HIBERNATE_CALLBACKS */

-#ifdef CONFIG_PM_RUNTIME
-
-int __weak platform_pm_runtime_suspend(struct device *dev)
-{
- return pm_generic_runtime_suspend(dev);
-};
-
-int __weak platform_pm_runtime_resume(struct device *dev)
-{
- return pm_generic_runtime_resume(dev);
-};
-
-int __weak platform_pm_runtime_idle(struct device *dev)
-{
- return pm_generic_runtime_idle(dev);
-};
-
-#else /* !CONFIG_PM_RUNTIME */
-
-#define platform_pm_runtime_suspend NULL
-#define platform_pm_runtime_resume NULL
-#define platform_pm_runtime_idle NULL
-
-#endif /* !CONFIG_PM_RUNTIME */
-
static const struct dev_pm_ops platform_dev_pm_ops = {
USE_PLATFORM_PM_SLEEP_OPS,
- .runtime_suspend = platform_pm_runtime_suspend,
- .runtime_resume = platform_pm_runtime_resume,
- .runtime_idle = platform_pm_runtime_idle,
+ .runtime_suspend = pm_generic_runtime_suspend,
+ .runtime_resume = pm_generic_runtime_resume,
+ .runtime_idle = pm_generic_runtime_idle,
};

struct bus_type platform_bus_type = {

2011-04-16 23:45:44

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 1/9] PM: Make power domain callbacks take precedence over subsystem ones

From: Rafael J. Wysocki <[email protected]>

Change the PM core's behavior related to power domains in such a way
that, if a power domain is defined for a given device, its callbacks
will be executed instead of and not in addition to the device
subsystem's PM callbacks.

The idea behind the initial implementation of power domains handling
by the PM core was that power domain callbacks would be executed in
addition to subsystem callbacks, so that it would be possible to
extend the subsystem callbacks by using power domains. It turns out,
however, that this wouldn't be really convenient in some important
situations.

For example, there are systems in which power can only be removed
from entire power domains. On those systems it is not desirable to
execute device drivers' PM callbacks until it is known that power is
going to be removed from the devices in question, which means that
they should be executed by power domain callbacks rather then by
subsystem (e.g. bus type) PM callbacks, because subsystems generally
have no information about what devices belong to which power domain.
Thus, for instance, if the bus type in question is the platform bus
type, its PM callbacks generally should not be called in addition to
power domain callbacks, because they run device drivers' callbacks
unconditionally if defined.

While in principle the default subsystem PM callbacks, or a subset of
them, may be replaced with different functions, it doesn't seem
correct to do so, because that would change the subsystem's behavior
with respect to all devices in the system, regardless of whether or
not they belong to any power domains. Thus, the only remaining
option is to make power domain callbacks take precedence over
subsystem callbacks.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Acked-by: Grant Likely <[email protected]>
Acked-by: Kevin Hilman <[email protected]>
---
drivers/base/power/main.c | 64 ++++++++++++++++++++-----------------------
drivers/base/power/runtime.c | 29 ++++++-------------
2 files changed, 41 insertions(+), 52 deletions(-)

Index: linux-2.6/drivers/base/power/runtime.c
===================================================================
--- linux-2.6.orig/drivers/base/power/runtime.c
+++ linux-2.6/drivers/base/power/runtime.c
@@ -168,7 +168,6 @@ static int rpm_check_suspend_allowed(str
static int rpm_idle(struct device *dev, int rpmflags)
{
int (*callback)(struct device *);
- int (*domain_callback)(struct device *);
int retval;

retval = rpm_check_suspend_allowed(dev);
@@ -214,7 +213,9 @@ static int rpm_idle(struct device *dev,

dev->power.idle_notification = true;

- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_idle;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_idle;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_idle;
@@ -223,19 +224,10 @@ static int rpm_idle(struct device *dev,
else
callback = NULL;

- if (dev->pwr_domain)
- domain_callback = dev->pwr_domain->ops.runtime_idle;
- else
- domain_callback = NULL;
-
- if (callback || domain_callback) {
+ if (callback) {
spin_unlock_irq(&dev->power.lock);

- if (domain_callback)
- retval = domain_callback(dev);
-
- if (!retval && callback)
- callback(dev);
+ callback(dev);

spin_lock_irq(&dev->power.lock);
}
@@ -382,7 +374,9 @@ static int rpm_suspend(struct device *de

__update_runtime_status(dev, RPM_SUSPENDING);

- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_suspend;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_suspend;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_suspend;
@@ -400,8 +394,6 @@ static int rpm_suspend(struct device *de
else
pm_runtime_cancel_pending(dev);
} else {
- if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
no_callback:
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);
@@ -582,9 +574,8 @@ static int rpm_resume(struct device *dev
__update_runtime_status(dev, RPM_RESUMING);

if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
-
- if (dev->type && dev->type->pm)
+ callback = dev->pwr_domain->ops.runtime_resume;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_resume;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_resume;
Index: linux-2.6/drivers/base/power/main.c
===================================================================
--- linux-2.6.orig/drivers/base/power/main.c
+++ linux-2.6/drivers/base/power/main.c
@@ -425,10 +425,8 @@ static int device_resume_noirq(struct de

if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "EARLY power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
- if (dev->type && dev->type->pm) {
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "EARLY type ");
error = pm_noirq_op(dev, dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
@@ -516,7 +514,8 @@ static int device_resume(struct device *

if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
}

if (dev->type && dev->type->pm) {
@@ -628,12 +627,11 @@ static void device_complete(struct devic
{
device_lock(dev);

- if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
+ if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "completing power domain ");
- dev->pwr_domain->ops.complete(dev);
- }
-
- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain->ops.complete)
+ dev->pwr_domain->ops.complete(dev);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "completing type ");
if (dev->type->pm->complete)
dev->type->pm->complete(dev);
@@ -731,7 +729,12 @@ static int device_suspend_noirq(struct d
{
int error;

- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "LATE power domain ");
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ if (error)
+ return error;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "LATE type ");
error = pm_noirq_op(dev, dev->type->pm, state);
if (error)
@@ -748,11 +751,6 @@ static int device_suspend_noirq(struct d
return error;
}

- if (dev->pwr_domain) {
- pm_dev_dbg(dev, state, "LATE power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
return 0;
}

@@ -840,21 +838,27 @@ static int __device_suspend(struct devic
goto End;
}

+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "power domain ");
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
+ }
+
if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
- goto Domain;
+ goto End;
}

if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
error = pm_op(dev, dev->class->pm, state);
- goto Domain;
+ goto End;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend);
- goto Domain;
+ goto End;
}
}

@@ -868,12 +872,6 @@ static int __device_suspend(struct devic
}
}

- Domain:
- if (!error && dev->pwr_domain) {
- pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
- }
-
End:
device_unlock(dev);
complete_all(&dev->power.completion);
@@ -964,7 +962,14 @@ static int device_prepare(struct device

device_lock(dev);

- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "preparing power domain ");
+ if (dev->pwr_domain->ops.prepare)
+ error = dev->pwr_domain->ops.prepare(dev);
+ suspend_report_result(dev->pwr_domain->ops.prepare, error);
+ if (error)
+ goto End;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "preparing type ");
if (dev->type->pm->prepare)
error = dev->type->pm->prepare(dev);
@@ -983,13 +988,6 @@ static int device_prepare(struct device
if (dev->bus->pm->prepare)
error = dev->bus->pm->prepare(dev);
suspend_report_result(dev->bus->pm->prepare, error);
- if (error)
- goto End;
- }
-
- if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
- pm_dev_dbg(dev, state, "preparing power domain ");
- dev->pwr_domain->ops.prepare(dev);
}

End:

2011-04-16 23:45:56

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 5/9] OMAP2+ / PM: Move runtime PM implementation to use power domains

From: Kevin Hilman <[email protected]>

In commit 7538e3db6e015e890825fbd9f8659952896ddd5b (PM: add support
for device power domains) a better way for handling platform-specific
power hooks was introduced.

Rather than using the platform_bus dev_pm_ops overrides
(platform_bus_set_pm_ops()), this patch moves the OMAP runtime PM
implementation over to using device power domains.

Since OMAP is the only user of platform_bus_set_pm_ops(), that
interface can be removed (and will be in a forthcoming patch.)

[rjw: Rebased on top of a previous change modifying the handling of
power domains by the PM core so that power domain callbacks take
precendence over subsystem-level PM callbacks.]

Signed-off-by: Kevin Hilman <[email protected]>
Acked-by: Grant Likely <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-omap2/Makefile | 6 +-
arch/arm/mach-omap2/pm_bus.c | 85 ---------------------------------------
arch/arm/plat-omap/omap_device.c | 23 ++++++++++
3 files changed, 26 insertions(+), 88 deletions(-)
delete mode 100644 arch/arm/mach-omap2/pm_bus.c

Index: linux-2.6/arch/arm/mach-omap2/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap2/Makefile
+++ linux-2.6/arch/arm/mach-omap2/Makefile
@@ -59,10 +59,10 @@ endif
# Power Management
ifeq ($(CONFIG_PM),y)
obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o
-obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o pm_bus.o
+obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o
obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \
- cpuidle34xx.o pm_bus.o
-obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o pm_bus.o
+ cpuidle34xx.o
+obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o
obj-$(CONFIG_PM_DEBUG) += pm-debug.o
obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
Index: linux-2.6/arch/arm/mach-omap2/pm_bus.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap2/pm_bus.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Runtime PM support code for OMAP
- *
- * Author: Kevin Hilman, Deep Root Systems, LLC
- *
- * Copyright (C) 2010 Texas Instruments, Inc.
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/pm_runtime.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-
-#include <plat/omap_device.h>
-#include <plat/omap-pm.h>
-
-#ifdef CONFIG_PM_RUNTIME
-static int omap_pm_runtime_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- int r, ret = 0;
-
- dev_dbg(dev, "%s\n", __func__);
-
- ret = pm_generic_runtime_suspend(dev);
-
- if (!ret && dev->parent == &omap_device_parent) {
- r = omap_device_idle(pdev);
- WARN_ON(r);
- }
-
- return ret;
-};
-
-static int omap_pm_runtime_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- int r;
-
- dev_dbg(dev, "%s\n", __func__);
-
- if (dev->parent == &omap_device_parent) {
- r = omap_device_enable(pdev);
- WARN_ON(r);
- }
-
- return pm_generic_runtime_resume(dev);
-};
-#else
-#define omap_pm_runtime_suspend NULL
-#define omap_pm_runtime_resume NULL
-#endif /* CONFIG_PM_RUNTIME */
-
-static int __init omap_pm_runtime_init(void)
-{
- const struct dev_pm_ops *pm;
- struct dev_pm_ops *omap_pm;
-
- pm = platform_bus_get_pm_ops();
- if (!pm) {
- pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
- __func__);
- return -ENODEV;
- }
-
- omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
- if (!omap_pm) {
- pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
- __func__);
- return -ENOMEM;
- }
-
- omap_pm->runtime_suspend = omap_pm_runtime_suspend;
- omap_pm->runtime_resume = omap_pm_runtime_resume;
-
- platform_bus_set_pm_ops(omap_pm);
-
- return 0;
-}
-core_initcall(omap_pm_runtime_init);
Index: linux-2.6/arch/arm/plat-omap/omap_device.c
===================================================================
--- linux-2.6.orig/arch/arm/plat-omap/omap_device.c
+++ linux-2.6/arch/arm/plat-omap/omap_device.c
@@ -536,6 +536,28 @@ int omap_early_device_register(struct om
return 0;
}

+static int _od_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return omap_device_idle(pdev);
+}
+
+static int _od_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return omap_device_enable(pdev);
+}
+
+static struct dev_power_domain omap_device_power_domain = {
+ .ops = {
+ USE_PLATFORM_PM_SLEEP_OPS,
+ .runtime_suspend = _od_runtime_suspend,
+ .runtime_resume = _od_runtime_resume,
+ }
+};
+
/**
* omap_device_register - register an omap_device with one omap_hwmod
* @od: struct omap_device * to register
@@ -549,6 +571,7 @@ int omap_device_register(struct omap_dev
pr_debug("omap_device: %s: registering\n", od->pdev.name);

od->pdev.dev.parent = &omap_device_parent;
+ od->pdev.dev.pwr_domain = &omap_device_power_domain;
return platform_device_register(&od->pdev);
}

2011-04-16 23:46:10

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 9/9] PM: Revert "driver core: platform_bus: allow runtime override of dev_pm_ops"

From: Kevin Hilman <[email protected]>

The platform_bus_set_pm_ops() operation is deprecated in favor of the
new device power domain infrastructre implemented in commit
7538e3db6e015e890825fbd9f8659952896ddd5b (PM: add support for device
power domains)

Signed-off-by: Kevin Hilman <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 35 -----------------------------------
include/linux/platform_device.h | 3 ---
2 files changed, 38 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -916,41 +916,6 @@ struct bus_type platform_bus_type = {
};
EXPORT_SYMBOL_GPL(platform_bus_type);

-/**
- * platform_bus_get_pm_ops() - return pointer to busses dev_pm_ops
- *
- * This function can be used by platform code to get the current
- * set of dev_pm_ops functions used by the platform_bus_type.
- */
-const struct dev_pm_ops * __init platform_bus_get_pm_ops(void)
-{
- return platform_bus_type.pm;
-}
-
-/**
- * platform_bus_set_pm_ops() - update dev_pm_ops for the platform_bus_type
- *
- * @pm: pointer to new dev_pm_ops struct to be used for platform_bus_type
- *
- * Platform code can override the dev_pm_ops methods of
- * platform_bus_type by using this function. It is expected that
- * platform code will first do a platform_bus_get_pm_ops(), then
- * kmemdup it, then customize selected methods and pass a pointer to
- * the new struct dev_pm_ops to this function.
- *
- * Since platform-specific code is customizing methods for *all*
- * devices (not just platform-specific devices) it is expected that
- * any custom overrides of these functions will keep existing behavior
- * and simply extend it. For example, any customization of the
- * runtime PM methods should continue to call the pm_generic_*
- * functions as the default ones do in addition to the
- * platform-specific behavior.
- */
-void __init platform_bus_set_pm_ops(const struct dev_pm_ops *pm)
-{
- platform_bus_type.pm = pm;
-}
-
int __init platform_bus_init(void)
{
int error;
Index: linux-2.6/include/linux/platform_device.h
===================================================================
--- linux-2.6.orig/include/linux/platform_device.h
+++ linux-2.6/include/linux/platform_device.h
@@ -150,9 +150,6 @@ extern struct platform_device *platform_
struct resource *res, unsigned int n_res,
const void *data, size_t size);

-extern const struct dev_pm_ops * platform_bus_get_pm_ops(void);
-extern void platform_bus_set_pm_ops(const struct dev_pm_ops *pm);
-
/* early platform driver interface */
struct early_platform_driver {
const char *class_str;

2011-04-16 23:45:50

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 6/9] PM / Runtime: Add subsystem data field to struct dev_pm_info

From: Rafael J. Wysocki <[email protected]>

Some subsystems need to attach PM-related data to struct device and
they need to use devres for this purpose. For their convenience
and to make code more straightforward, add a new field called
subsys_data to struct dev_pm_info and let subsystems use it for
attaching PM-related information to devices.

Convert the ARM shmobile platform to using the new field.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 34 +++++++++++++++++-----------------
include/linux/pm.h | 1 +
2 files changed, 18 insertions(+), 17 deletions(-)

Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -460,6 +460,7 @@ struct dev_pm_info {
unsigned long active_jiffies;
unsigned long suspended_jiffies;
unsigned long accounting_timestamp;
+ void *subsys_data; /* Owned by the subsystem. */
#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
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/sh_clk.h>
#include <linux/bitmap.h>
+#include <linux/slab.h>

#ifdef CONFIG_PM_RUNTIME
#define BIT_ONCE 0
@@ -29,22 +30,9 @@ struct pm_runtime_data {
struct clk *clk;
};

-static void __devres_release(struct device *dev, void *res)
-{
- struct pm_runtime_data *prd = res;
-
- dev_dbg(dev, "__devres_release()\n");
-
- if (test_bit(BIT_CLK_ENABLED, &prd->flags))
- clk_disable(prd->clk);
-
- if (test_bit(BIT_ACTIVE, &prd->flags))
- clk_put(prd->clk);
-}
-
static struct pm_runtime_data *__to_prd(struct device *dev)
{
- return devres_find(dev, __devres_release, NULL, NULL);
+ return dev ? dev->power.subsys_data : NULL;
}

static void platform_pm_runtime_init(struct device *dev,
@@ -121,14 +109,26 @@ static int platform_bus_notify(struct no

dev_dbg(dev, "platform_bus_notify() %ld !\n", action);

- if (action == BUS_NOTIFY_BIND_DRIVER) {
- prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
+ switch (action) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
if (prd) {
- devres_add(dev, 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;

2011-04-16 23:46:05

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 8/9] OMAP1 / PM: Use generic clock manipulation routines for runtime PM

From: Rafael J. Wysocki <[email protected]>

Convert OMAP1 to using the new generic clock manipulation routines
and a device power domain for runtime PM instead of overriding the
platform bus type's runtime PM callbacks. This allows us to simplify
OMAP1-specific code and to share some code with other platforms
(shmobile in particular).

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-omap1/pm_bus.c | 63 +++++++++++++------------------------------
1 file changed, 20 insertions(+), 43 deletions(-)

Index: linux-2.6/arch/arm/mach-omap1/pm_bus.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap1/pm_bus.c
+++ linux-2.6/arch/arm/mach-omap1/pm_bus.c
@@ -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;
@@ -48,23 +43,22 @@ static int omap1_pm_runtime_suspend(stru

static int omap1_pm_runtime_resume(struct device *dev)
{
- struct clk *iclk, *fclk;
-
dev_dbg(dev, "%s\n", __func__);

- iclk = clk_get(dev, "ick");
- if (!IS_ERR(iclk)) {
- clk_enable(iclk);
- clk_put(iclk);
- }
+ pm_runtime_clock_resume(dev);
+ return pm_generic_runtime_resume(dev);
+};

- fclk = clk_get(dev, "fck");
- if (!IS_ERR(fclk)) {
- clk_enable(fclk);
- clk_put(fclk);
- }
+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ USE_PLATFORM_PM_SLEEP_OPS,
+ .runtime_suspend = omap1_pm_runtime_suspend,
+ .runtime_resume = omap1_pm_runtime_resume,
+ },
+};

- return pm_generic_runtime_resume(dev);
+static struct pm_domain_notifier_block platform_bus_notifier = {
+ .pwr_domain = &default_power_domain,
};

static int __init omap1_pm_runtime_init(void)
@@ -75,24 +69,7 @@ static int __init omap1_pm_runtime_init(
if (!cpu_class_is_omap1())
return -ENODEV;

- pm = platform_bus_get_pm_ops();
- if (!pm) {
- pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
- __func__);
- return -ENODEV;
- }
-
- omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
- if (!omap_pm) {
- pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
- __func__);
- return -ENOMEM;
- }
-
- omap_pm->runtime_suspend = omap1_pm_runtime_suspend;
- omap_pm->runtime_resume = omap1_pm_runtime_resume;
-
- platform_bus_set_pm_ops(omap_pm);
+ pm_runtim_clock_add_notifier(&platform_bus_type, &platform_bus_notifier);

return 0;
}

2011-04-16 23:46:17

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

From: Rafael J. Wysocki <[email protected]>

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 <[email protected]>
---
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(-)

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 <[email protected]>, 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_runtim_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 pm_runtime_clock_add(struct device *dev) { return -EINVAL; }
+static inline 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_runtim_clock_add_notifier(struct bus_type *bus,
+ struct pm_domain_notifier_block *pdnb);
+#else
+static inline void pm_runtim_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_runtim_clock_add_notifier(&platform_bus_type, &platform_bus_notifier);
return 0;
}
core_initcall(sh_pm_runtime_init);

2011-04-18 08:19:41

by Paul Mundt

[permalink] [raw]
Subject: Re: [PATCH 8/9] OMAP1 / PM: Use generic clock manipulation routines for runtime PM

On Sun, Apr 17, 2011 at 01:43:28AM +0200, Rafael J. Wysocki wrote:
> @@ -75,24 +69,7 @@ static int __init omap1_pm_runtime_init(
> if (!cpu_class_is_omap1())
> return -ENODEV;
>
> - pm = platform_bus_get_pm_ops();
> - if (!pm) {
> - pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
> - __func__);
> - return -ENODEV;
> - }
> -
> - omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
> - if (!omap_pm) {
> - pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
> - __func__);
> - return -ENOMEM;
> - }
> -
> - omap_pm->runtime_suspend = omap1_pm_runtime_suspend;
> - omap_pm->runtime_resume = omap1_pm_runtime_resume;
> -
> - platform_bus_set_pm_ops(omap_pm);
> + pm_runtim_clock_add_notifier(&platform_bus_type, &platform_bus_notifier);
>
Presumably you meant pm_runtime_clock_add_notifier() here. You seem to
have the same typo in a few other places too.

2011-04-18 19:56:58

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 8/9] OMAP1 / PM: Use generic clock manipulation routines for runtime PM

On Monday, April 18, 2011, Paul Mundt wrote:
> On Sun, Apr 17, 2011 at 01:43:28AM +0200, Rafael J. Wysocki wrote:
> > @@ -75,24 +69,7 @@ static int __init omap1_pm_runtime_init(
> > if (!cpu_class_is_omap1())
> > return -ENODEV;
> >
> > - pm = platform_bus_get_pm_ops();
> > - if (!pm) {
> > - pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
> > - __func__);
> > - return -ENODEV;
> > - }
> > -
> > - omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
> > - if (!omap_pm) {
> > - pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
> > - __func__);
> > - return -ENOMEM;
> > - }
> > -
> > - omap_pm->runtime_suspend = omap1_pm_runtime_suspend;
> > - omap_pm->runtime_resume = omap1_pm_runtime_resume;
> > -
> > - platform_bus_set_pm_ops(omap_pm);
> > + pm_runtim_clock_add_notifier(&platform_bus_type, &platform_bus_notifier);
> >
> Presumably you meant pm_runtime_clock_add_notifier() here. You seem to
> have the same typo in a few other places too.

Yes, in fact I have fixed those typos (and a build problem in [7/9] for
CONFIG_HAVE_CLK unset) already.

Corrected patch is appended, but it depends on the fixed [7/9] (I'll send
it in a little while).

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: OMAP1 / PM: Use generic clock manipulation routines for runtime PM

Convert OMAP1 to using the new generic clock manipulation routines
and a device power domain for runtime PM instead of overriding the
platform bus type's runtime PM callbacks. This allows us to simplify
OMAP1-specific code and to share some code with other platforms
(shmobile in particular).

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-omap1/pm_bus.c | 63 +++++++++++++------------------------------
1 file changed, 20 insertions(+), 43 deletions(-)

Index: linux-2.6/arch/arm/mach-omap1/pm_bus.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap1/pm_bus.c
+++ linux-2.6/arch/arm/mach-omap1/pm_bus.c
@@ -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;
@@ -48,23 +43,22 @@ static int omap1_pm_runtime_suspend(stru

static int omap1_pm_runtime_resume(struct device *dev)
{
- struct clk *iclk, *fclk;
-
dev_dbg(dev, "%s\n", __func__);

- iclk = clk_get(dev, "ick");
- if (!IS_ERR(iclk)) {
- clk_enable(iclk);
- clk_put(iclk);
- }
+ pm_runtime_clock_resume(dev);
+ return pm_generic_runtime_resume(dev);
+};

- fclk = clk_get(dev, "fck");
- if (!IS_ERR(fclk)) {
- clk_enable(fclk);
- clk_put(fclk);
- }
+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ USE_PLATFORM_PM_SLEEP_OPS,
+ .runtime_suspend = omap1_pm_runtime_suspend,
+ .runtime_resume = omap1_pm_runtime_resume,
+ },
+};

- return pm_generic_runtime_resume(dev);
+static struct pm_domain_notifier_block platform_bus_notifier = {
+ .pwr_domain = &default_power_domain,
};

static int __init omap1_pm_runtime_init(void)
@@ -75,24 +69,7 @@ static int __init omap1_pm_runtime_init(
if (!cpu_class_is_omap1())
return -ENODEV;

- pm = platform_bus_get_pm_ops();
- if (!pm) {
- pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
- __func__);
- return -ENODEV;
- }
-
- omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
- if (!omap_pm) {
- pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
- __func__);
- return -ENOMEM;
- }
-
- omap_pm->runtime_suspend = omap1_pm_runtime_suspend;
- omap_pm->runtime_resume = omap1_pm_runtime_resume;
-
- platform_bus_set_pm_ops(omap_pm);
+ pm_runtime_clock_add_notifier(&platform_bus_type, &platform_bus_notifier);

return 0;
}

2011-04-18 19:59:22

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

From: Rafael J. Wysocki <[email protected]>

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 <[email protected]>
---

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(-)

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 <[email protected]>, 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);

2011-04-19 10:18:13

by Magnus Damm

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

Hi Rafael,

On Tue, Apr 19, 2011 at 4:59 AM, Rafael J. Wysocki <[email protected]> wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> 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 <[email protected]>
> ---
>
> 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

2011-04-19 10:58:51

by Mark Brown

[permalink] [raw]
Subject: Re: [linux-pm] [PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

On Sun, Apr 17, 2011 at 01:42:10AM +0200, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> 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.

I've had similar thoughts with respect to the regulator API - I'd been
thinking of dealing with this by adding a notifier which subsystems
could build upon, in much the same way as already exists for the system
ssuspend and resume events.

2011-04-19 21:35:42

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [linux-pm] [PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

On Tuesday, April 19, 2011, Mark Brown wrote:
> On Sun, Apr 17, 2011 at 01:42:10AM +0200, Rafael J. Wysocki wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > 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.
>
> I've had similar thoughts with respect to the regulator API - I'd been
> thinking of dealing with this by adding a notifier which subsystems
> could build upon, in much the same way as already exists for the system
> ssuspend and resume events.

Well, I generally avoid using notifiers, because they lead to big functions
that carry out many different tasks.

That said, I think consolidation and reduction of duplicate code is all good,
this way or another. :-)

Thanks,
Rafael

2011-04-19 21:42:00

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

On Tuesday, April 19, 2011, Magnus Damm wrote:
> Hi Rafael,
>
> On Tue, Apr 19, 2011 at 4:59 AM, Rafael J. Wysocki <[email protected]> wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> >
> > 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

2011-04-19 22:00:25

by Paul Mundt

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

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.

2011-04-19 22:10:46

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

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

2011-04-19 22:21:06

by Paul Mundt

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

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.

2011-04-19 22:50:03

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

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

2011-04-20 11:57:54

by Mark Brown

[permalink] [raw]
Subject: Re: [linux-pm] [PATCH 7/9] PM / Runtime: Add generic clock manipulation rountines for runtime PM

On Tue, Apr 19, 2011 at 11:35:34PM +0200, Rafael J. Wysocki wrote:
> On Tuesday, April 19, 2011, Mark Brown wrote:

> > I've had similar thoughts with respect to the regulator API - I'd been
> > thinking of dealing with this by adding a notifier which subsystems
> > could build upon, in much the same way as already exists for the system
> > ssuspend and resume events.

> Well, I generally avoid using notifiers, because they lead to big functions
> that carry out many different tasks.

> That said, I think consolidation and reduction of duplicate code is all good,
> this way or another. :-)

Well, if you're comfortable with adding explicit regulator API hooks in
the code directly then I can just do that - I'd been thinking notifiers
since they mean you don't have to worry about what's going on with it
but if you're happy with making it explicit that's fine by me also.

2011-04-24 21:44:51

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 3/9] shmobile: Use power domains for platform runtime PM

From: Rafael J. Wysocki <[email protected]>

shmobile platforms replace the runtime PM callbacks of the platform
bus type with their own routines, but this means that the callbacks
are replaced system-wide. This may not be the right approach if the
platform devices on the system are not of the same type (e.g. some
of them belong to an SoC and the others are located in separate
chips), because in those cases they may require different handling.
Thus it is better to use power domains to override the platform bus
type's PM handling, as it generally is possible to use different
power domains for devices with different PM requirements.

Define a default power domain for shmobile in both the SH and ARM
falvors and use it to override the platform bus type's PM callbacks.
Since the suspend and hibernate callbacks of the new "default" power
domains need to be the same and the platform bus type's suspend and
hibernate callbacks for the time being, export those callbacks so
that can be used outside of the platform bus type code.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 25 ++++++++++++++++-------
arch/sh/kernel/cpu/shmobile/pm_runtime.c | 33 ++++++++++++++++++++-----------
2 files changed, 40 insertions(+), 18 deletions(-)

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
@@ -66,11 +66,11 @@ static void platform_pm_runtime_bug(stru
dev_err(dev, "runtime pm suspend before resume\n");
}

-int platform_pm_runtime_suspend(struct device *dev)
+static int default_platform_runtime_suspend(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);

- dev_dbg(dev, "platform_pm_runtime_suspend()\n");
+ dev_dbg(dev, "%s()\n", __func__);

platform_pm_runtime_bug(dev, prd);

@@ -82,11 +82,11 @@ int platform_pm_runtime_suspend(struct d
return 0;
}

-int platform_pm_runtime_resume(struct device *dev)
+static int default_platform_runtime_resume(struct device *dev)
{
struct pm_runtime_data *prd = __to_prd(dev);

- dev_dbg(dev, "platform_pm_runtime_resume()\n");
+ dev_dbg(dev, "%s()\n", __func__);

platform_pm_runtime_init(dev, prd);

@@ -98,12 +98,21 @@ int platform_pm_runtime_resume(struct de
return 0;
}

-int platform_pm_runtime_idle(struct device *dev)
+static int default_platform_runtime_idle(struct device *dev)
{
/* suspend synchronously to disable clocks immediately */
return pm_runtime_suspend(dev);
}

+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ .runtime_suspend = default_platform_runtime_suspend,
+ .runtime_resume = default_platform_runtime_resume,
+ .runtime_idle = default_platform_runtime_idle,
+ USE_PLATFORM_PM_SLEEP_OPS
+ },
+};
+
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -114,10 +123,12 @@ static int platform_bus_notify(struct no

if (action == BUS_NOTIFY_BIND_DRIVER) {
prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
- if (prd)
+ if (prd) {
devres_add(dev, prd);
- else
+ dev->pwr_domain = &default_power_domain;
+ } else {
dev_err(dev, "unable to alloc memory for runtime pm\n");
+ }
}

return 0;
Index: linux-2.6/arch/sh/kernel/cpu/shmobile/pm_runtime.c
===================================================================
--- linux-2.6.orig/arch/sh/kernel/cpu/shmobile/pm_runtime.c
+++ linux-2.6/arch/sh/kernel/cpu/shmobile/pm_runtime.c
@@ -139,7 +139,7 @@ void platform_pm_runtime_suspend_idle(vo
queue_work(pm_wq, &hwblk_work);
}

-int platform_pm_runtime_suspend(struct device *dev)
+static int default_platform_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pdev_archdata *ad = &pdev->archdata;
@@ -147,7 +147,7 @@ int platform_pm_runtime_suspend(struct d
int hwblk = ad->hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_suspend() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -183,20 +183,20 @@ int platform_pm_runtime_suspend(struct d
mutex_unlock(&ad->mutex);

out:
- dev_dbg(dev, "platform_pm_runtime_suspend() [%d] returns %d\n",
- hwblk, ret);
+ dev_dbg(dev, "%s() [%d] returns %d\n",
+ __func__, hwblk, ret);

return ret;
}

-int platform_pm_runtime_resume(struct device *dev)
+static int default_platform_runtime_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct pdev_archdata *ad = &pdev->archdata;
int hwblk = ad->hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_resume() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -228,19 +228,19 @@ int platform_pm_runtime_resume(struct de
*/
mutex_unlock(&ad->mutex);
out:
- dev_dbg(dev, "platform_pm_runtime_resume() [%d] returns %d\n",
- hwblk, ret);
+ dev_dbg(dev, "%s() [%d] returns %d\n",
+ __func__, hwblk, ret);

return ret;
}

-int platform_pm_runtime_idle(struct device *dev)
+static int default_platform_runtime_idle(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
int hwblk = pdev->archdata.hwblk_id;
int ret = 0;

- dev_dbg(dev, "platform_pm_runtime_idle() [%d]\n", hwblk);
+ dev_dbg(dev, "%s() [%d]\n", __func__, hwblk);

/* ignore off-chip platform devices */
if (!hwblk)
@@ -252,10 +252,19 @@ int platform_pm_runtime_idle(struct devi
/* suspend synchronously to disable clocks immediately */
ret = pm_runtime_suspend(dev);
out:
- dev_dbg(dev, "platform_pm_runtime_idle() [%d] done!\n", hwblk);
+ dev_dbg(dev, "%s() [%d] done!\n", __func__, hwblk);
return ret;
}

+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ .runtime_suspend = default_platform_runtime_suspend,
+ .runtime_resume = default_platform_runtime_resume,
+ .runtime_idle = default_platform_runtime_idle,
+ USE_PLATFORM_PM_SLEEP_OPS
+ },
+};
+
static int platform_bus_notify(struct notifier_block *nb,
unsigned long action, void *data)
{
@@ -276,6 +285,7 @@ static int platform_bus_notify(struct no
hwblk_disable(hwblk_info, hwblk);
/* make sure driver re-inits itself once */
__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
+ dev->pwr_domain = &default_power_domain;
break;
/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
case BUS_NOTIFY_BOUND_DRIVER:
@@ -289,6 +299,7 @@ static int platform_bus_notify(struct no
__set_bit(PDEV_ARCHDATA_FLAG_INIT, &pdev->archdata.flags);
break;
case BUS_NOTIFY_DEL_DEVICE:
+ dev->pwr_domain = NULL;
break;
}
return 0;

2011-04-24 21:45:21

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 5/9] OMAP2+ / PM: move runtime PM implementation to use device power domains

From: Kevin Hilman <[email protected]>

In commit 7538e3db6e015e890825fbd9f8659952896ddd5b (PM: add support
for device power domains) a better way for handling platform-specific
power hooks was introduced.

Rather than using the platform_bus dev_pm_ops overrides
(platform_bus_set_pm_ops()), this patch moves the OMAP runtime PM
implementation over to using device power domains.

Since OMAP is the only user of platform_bus_set_pm_ops(), that
interface can be removed (and will be in a forthcoming patch.)

[rjw: Rebased on top of a previous change modifying the handling of
power domains by the PM core so that power domain callbacks take
precendence over subsystem-level PM callbacks.]

Signed-off-by: Kevin Hilman <[email protected]>
Acked-by: Grant Likely <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-omap2/Makefile | 6 +-
arch/arm/mach-omap2/pm_bus.c | 85 ---------------------------------------
arch/arm/plat-omap/omap_device.c | 23 ++++++++++
3 files changed, 26 insertions(+), 88 deletions(-)
delete mode 100644 arch/arm/mach-omap2/pm_bus.c

Index: linux-2.6/arch/arm/mach-omap2/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap2/Makefile
+++ linux-2.6/arch/arm/mach-omap2/Makefile
@@ -59,10 +59,10 @@ endif
# Power Management
ifeq ($(CONFIG_PM),y)
obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o
-obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o pm_bus.o
+obj-$(CONFIG_ARCH_OMAP2) += sleep24xx.o
obj-$(CONFIG_ARCH_OMAP3) += pm34xx.o sleep34xx.o \
- cpuidle34xx.o pm_bus.o
-obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o pm_bus.o
+ cpuidle34xx.o
+obj-$(CONFIG_ARCH_OMAP4) += pm44xx.o
obj-$(CONFIG_PM_DEBUG) += pm-debug.o
obj-$(CONFIG_OMAP_SMARTREFLEX) += sr_device.o smartreflex.o
obj-$(CONFIG_OMAP_SMARTREFLEX_CLASS3) += smartreflex-class3.o
Index: linux-2.6/arch/arm/mach-omap2/pm_bus.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap2/pm_bus.c
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Runtime PM support code for OMAP
- *
- * Author: Kevin Hilman, Deep Root Systems, LLC
- *
- * Copyright (C) 2010 Texas Instruments, Inc.
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2. This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/io.h>
-#include <linux/pm_runtime.h>
-#include <linux/platform_device.h>
-#include <linux/mutex.h>
-
-#include <plat/omap_device.h>
-#include <plat/omap-pm.h>
-
-#ifdef CONFIG_PM_RUNTIME
-static int omap_pm_runtime_suspend(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- int r, ret = 0;
-
- dev_dbg(dev, "%s\n", __func__);
-
- ret = pm_generic_runtime_suspend(dev);
-
- if (!ret && dev->parent == &omap_device_parent) {
- r = omap_device_idle(pdev);
- WARN_ON(r);
- }
-
- return ret;
-};
-
-static int omap_pm_runtime_resume(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
- int r;
-
- dev_dbg(dev, "%s\n", __func__);
-
- if (dev->parent == &omap_device_parent) {
- r = omap_device_enable(pdev);
- WARN_ON(r);
- }
-
- return pm_generic_runtime_resume(dev);
-};
-#else
-#define omap_pm_runtime_suspend NULL
-#define omap_pm_runtime_resume NULL
-#endif /* CONFIG_PM_RUNTIME */
-
-static int __init omap_pm_runtime_init(void)
-{
- const struct dev_pm_ops *pm;
- struct dev_pm_ops *omap_pm;
-
- pm = platform_bus_get_pm_ops();
- if (!pm) {
- pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
- __func__);
- return -ENODEV;
- }
-
- omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
- if (!omap_pm) {
- pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
- __func__);
- return -ENOMEM;
- }
-
- omap_pm->runtime_suspend = omap_pm_runtime_suspend;
- omap_pm->runtime_resume = omap_pm_runtime_resume;
-
- platform_bus_set_pm_ops(omap_pm);
-
- return 0;
-}
-core_initcall(omap_pm_runtime_init);
Index: linux-2.6/arch/arm/plat-omap/omap_device.c
===================================================================
--- linux-2.6.orig/arch/arm/plat-omap/omap_device.c
+++ linux-2.6/arch/arm/plat-omap/omap_device.c
@@ -536,6 +536,28 @@ int omap_early_device_register(struct om
return 0;
}

+static int _od_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return omap_device_idle(pdev);
+}
+
+static int _od_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ return omap_device_enable(pdev);
+}
+
+static struct dev_power_domain omap_device_power_domain = {
+ .ops = {
+ .runtime_suspend = _od_runtime_suspend,
+ .runtime_resume = _od_runtime_resume,
+ USE_PLATFORM_PM_SLEEP_OPS
+ }
+};
+
/**
* omap_device_register - register an omap_device with one omap_hwmod
* @od: struct omap_device * to register
@@ -549,6 +571,7 @@ int omap_device_register(struct omap_dev
pr_debug("omap_device: %s: registering\n", od->pdev.name);

od->pdev.dev.parent = &omap_device_parent;
+ od->pdev.dev.pwr_domain = &omap_device_power_domain;
return platform_device_register(&od->pdev);
}

2011-04-24 21:45:33

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v2)

From: Rafael J. Wysocki <[email protected]>

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 <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 140 -----------
drivers/base/power/Makefile | 1
drivers/base/power/clock_ops.c | 422 ++++++++++++++++++++++++++++++++++++
include/linux/pm_runtime.h | 42 +++
kernel/power/Kconfig | 4
5 files changed, 478 insertions(+), 131 deletions(-)

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -0,0 +1,422 @@
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <[email protected]>, 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_clk_data {
+ struct list_head clock_list;
+ struct mutex lock;
+};
+
+struct pm_clock_entry {
+ struct list_head node;
+ char *con_id;
+ struct clk *clk;
+ unsigned int clock_active:1;
+ unsigned int clock_enabled:1;
+};
+
+static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
+/**
+ * pm_runtime_clk_add - Start using a device clock for runtime PM.
+ * @dev: Device whose clock is going to be used for runtime PM.
+ * @con_id: Connection ID of the clock.
+ *
+ * Add the clock represented by @con_id to the list of clocks used for
+ * the runtime PM of @dev.
+ */
+int pm_runtime_clk_add(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return -EINVAL;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ dev_err(dev, "Not enough memory for clock entry.\n");
+ return -ENOMEM;
+ }
+
+ if (con_id) {
+ ce->con_id = kstrdup(con_id, GFP_KERNEL);
+ if (!ce->con_id) {
+ dev_err(dev,
+ "Not enough memory for clock conection ID.\n");
+ kfree(ce);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&prd->lock);
+ list_add_tail(&ce->node, &prd->clock_list);
+ mutex_unlock(&prd->lock);
+ return 0;
+}
+
+/**
+ * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
+ * @ce: Runtime PM clock entry to destroy.
+ *
+ * This routine must be called under the mutex protecting the runtime PM list
+ * of clocks corresponding the the @ce's device.
+ */
+static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+{
+ if (!ce)
+ return;
+
+ list_del(&ce->node);
+
+ if (ce->clk) {
+ if (ce->clock_enabled)
+ clk_disable(ce->clk);
+
+ if (ce->clock_active)
+ clk_put(ce->clk);
+ }
+
+ if (ce->con_id)
+ kfree(ce->con_id);
+
+ kfree(ce);
+}
+
+/**
+ * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
+ * @dev: Device whose clock should not be used for runtime PM any more.
+ * @con_id: Connection ID of the clock.
+ *
+ * Remove the clock represented by @con_id from the list of clocks used for
+ * the runtime PM of @dev.
+ */
+void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node)
+ if (!con_id && !ce->con_id) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ } else if (!con_id || !ce->con_id) {
+ continue;
+ } else if (!strcmp(con_id, ce->con_id)) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ }
+
+ mutex_unlock(&prd->lock);
+}
+
+/**
+ * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
+ * @dev: Device to initialize the list of runtime PM clocks for.
+ *
+ * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * make the @dev's power.subsys_data field point to it.
+ */
+int pm_runtime_clk_init(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd;
+
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+ if (!prd) {
+ dev_err(dev, "Not enough memory fo runtime PM data.\n");
+ return -ENOMEM;
+ }
+
+ dev->power.subsys_data = prd;
+ mutex_init(&prd->lock);
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
+ * @dev: Device to destroy the list of runtime PM clocks for.
+ *
+ * Clear the @dev's power.subsys_data field, remove the list of clock entries
+ * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * that object.
+ */
+void pm_runtime_clk_destroy(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce, *c;
+
+ if (!prd)
+ return;
+
+ dev->power.subsys_data = NULL;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
+ __pm_runtime_clk_remove(ce);
+
+ mutex_unlock(&prd->lock);
+
+ kfree(prd);
+}
+
+/**
+ * pm_runtime_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @con_id: Connection ID of the clock.
+ */
+static void pm_runtime_clk_acquire(struct device *dev,
+ struct pm_clock_entry *ce)
+{
+ ce->clk = clk_get(dev, ce->con_id);
+ if (!IS_ERR(ce->clk)) {
+ ce->clock_active = true;
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ }
+}
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_runtime_clk_suspend(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+ if (!ce->clk) {
+ dev_err(dev, "Clock is not ready for runtime PM\n");
+ pm_runtime_clk_acquire(dev, ce);
+ }
+
+ if (ce->clock_active) {
+ clk_disable(ce->clk);
+ ce->clock_enabled = false;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_runtime_clk_resume(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (!ce->clk)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (ce->clock_active) {
+ clk_enable(ce->clk);
+ ce->clock_enabled = true;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the pwr_domain member of that object is copied to the device's
+ * pwr_domain field and its con_ids member is used to populate the device's list
+ * of runtime PM clocks, depending on @action.
+ *
+ * If the device's pwr_domain field is already populated with a value different
+ * from the one stored in the struct pm_clk_notifier_block object, the function
+ * does nothing.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+ int error;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->pwr_domain)
+ break;
+
+ error = pm_runtime_clk_init(dev);
+ if (error)
+ break;
+
+ dev->pwr_domain = clknb->pwr_domain;
+ if (clknb->con_ids) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ pm_runtime_clk_add(dev, con_id);
+ } else {
+ pm_runtime_clk_add(dev, NULL);
+ }
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->pwr_domain != clknb->pwr_domain)
+ break;
+
+ dev->pwr_domain = NULL;
+ pm_runtime_clk_destroy(dev);
+ break;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+/**
+ * enable_clock - Enable a device clock.
+ * @dev: Device whose clock is to be enabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void enable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced on.\n");
+ }
+}
+
+/**
+ * disable_clock - Disable a device clock.
+ * @dev: Device whose clock is to be disabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void disable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced off.\n");
+ }
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the con_ids member of that object is used to enable or disable
+ * the device's clocks, depending on @action.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (clknb->con_ids) {
+ for (con_id = clknb->con_ids; *con_id; con_id++)
+ enable_clock(dev, con_id);
+ } else {
+ enable_clock(dev, NULL);
+ }
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (clknb->con_ids) {
+ for (con_id = clknb->con_ids; *con_id; con_id++)
+ disable_clock(dev, con_id);
+ } else {
+ disable_clock(dev, NULL);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+/**
+ * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * @bus: Bus type to add the notifier to.
+ * @clknb: Notifier to be added to the given bus type.
+ *
+ * The nb member of @clknb is not expected to be initialized and its
+ * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * the remaining members of @clknb should be populated prior to calling this
+ * routine.
+ */
+void pm_runtime_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+ if (!bus || !clknb)
+ return;
+
+ clknb->nb.notifier_call = pm_runtime_clk_notify;
+ bus_register_notifier(bus, &clknb->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,46 @@ static inline void pm_runtime_dont_use_a
__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
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)
{
@@ -94,87 +30,29 @@ static int default_platform_runtime_idle

static struct dev_power_domain default_power_domain = {
.ops = {
- .runtime_suspend = default_platform_runtime_suspend,
- .runtime_resume = default_platform_runtime_resume,
+ .runtime_suspend = pm_runtime_clk_suspend,
+ .runtime_resume = pm_runtime_clk_resume,
.runtime_idle = default_platform_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
},
};

-static int platform_bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct pm_runtime_data *prd;
+#define DEFAULT_PWR_DOMAIN_PTR (&default_power_domain)

- dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
+#else

- 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;
-}
+#define DEFAULT_PWR_DOMAIN_PTR NULL

#endif /* CONFIG_PM_RUNTIME */

-static struct notifier_block platform_bus_notifier = {
- .notifier_call = platform_bus_notify
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+ .con_ids = { NULL, },
};

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;
}
core_initcall(sh_pm_runtime_init);

2011-04-24 21:45:53

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 2/9] PM: Export platform bus type's default PM callbacks

From: Rafael J. Wysocki <[email protected]>

Export the default PM callbacks defined for the platform bus type so
that they can be used by power domains for suspending and resuming
platform devices in the future.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 72 ++++++++++------------------------------
include/linux/platform_device.h | 60 +++++++++++++++++++++++++++++++++
2 files changed, 78 insertions(+), 54 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -667,7 +667,7 @@ static int platform_legacy_resume(struct
return ret;
}

-static int platform_pm_prepare(struct device *dev)
+int platform_pm_prepare(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -678,7 +678,7 @@ static int platform_pm_prepare(struct de
return ret;
}

-static void platform_pm_complete(struct device *dev)
+void platform_pm_complete(struct device *dev)
{
struct device_driver *drv = dev->driver;

@@ -686,16 +686,11 @@ static void platform_pm_complete(struct
drv->pm->complete(dev);
}

-#else /* !CONFIG_PM_SLEEP */
-
-#define platform_pm_prepare NULL
-#define platform_pm_complete NULL
-
-#endif /* !CONFIG_PM_SLEEP */
+#endif /* CONFIG_PM_SLEEP */

#ifdef CONFIG_SUSPEND

-int __weak platform_pm_suspend(struct device *dev)
+int platform_pm_suspend(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -713,7 +708,7 @@ int __weak platform_pm_suspend(struct de
return ret;
}

-int __weak platform_pm_suspend_noirq(struct device *dev)
+int platform_pm_suspend_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -729,7 +724,7 @@ int __weak platform_pm_suspend_noirq(str
return ret;
}

-int __weak platform_pm_resume(struct device *dev)
+int platform_pm_resume(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -747,7 +742,7 @@ int __weak platform_pm_resume(struct dev
return ret;
}

-int __weak platform_pm_resume_noirq(struct device *dev)
+int platform_pm_resume_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -763,18 +758,11 @@ int __weak platform_pm_resume_noirq(stru
return ret;
}

-#else /* !CONFIG_SUSPEND */
-
-#define platform_pm_suspend NULL
-#define platform_pm_resume NULL
-#define platform_pm_suspend_noirq NULL
-#define platform_pm_resume_noirq NULL
-
-#endif /* !CONFIG_SUSPEND */
+#endif /* CONFIG_SUSPEND */

#ifdef CONFIG_HIBERNATE_CALLBACKS

-static int platform_pm_freeze(struct device *dev)
+int platform_pm_freeze(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -792,7 +780,7 @@ static int platform_pm_freeze(struct dev
return ret;
}

-static int platform_pm_freeze_noirq(struct device *dev)
+int platform_pm_freeze_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -808,7 +796,7 @@ static int platform_pm_freeze_noirq(stru
return ret;
}

-static int platform_pm_thaw(struct device *dev)
+int platform_pm_thaw(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -826,7 +814,7 @@ static int platform_pm_thaw(struct devic
return ret;
}

-static int platform_pm_thaw_noirq(struct device *dev)
+int platform_pm_thaw_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -842,7 +830,7 @@ static int platform_pm_thaw_noirq(struct
return ret;
}

-static int platform_pm_poweroff(struct device *dev)
+int platform_pm_poweroff(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -860,7 +848,7 @@ static int platform_pm_poweroff(struct d
return ret;
}

-static int platform_pm_poweroff_noirq(struct device *dev)
+int platform_pm_poweroff_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -876,7 +864,7 @@ static int platform_pm_poweroff_noirq(st
return ret;
}

-static int platform_pm_restore(struct device *dev)
+int platform_pm_restore(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -894,7 +882,7 @@ static int platform_pm_restore(struct de
return ret;
}

-static int platform_pm_restore_noirq(struct device *dev)
+int platform_pm_restore_noirq(struct device *dev)
{
struct device_driver *drv = dev->driver;
int ret = 0;
@@ -910,18 +898,7 @@ static int platform_pm_restore_noirq(str
return ret;
}

-#else /* !CONFIG_HIBERNATE_CALLBACKS */
-
-#define platform_pm_freeze NULL
-#define platform_pm_thaw NULL
-#define platform_pm_poweroff NULL
-#define platform_pm_restore NULL
-#define platform_pm_freeze_noirq NULL
-#define platform_pm_thaw_noirq NULL
-#define platform_pm_poweroff_noirq NULL
-#define platform_pm_restore_noirq NULL
-
-#endif /* !CONFIG_HIBERNATE_CALLBACKS */
+#endif /* CONFIG_HIBERNATE_CALLBACKS */

#ifdef CONFIG_PM_RUNTIME

@@ -949,23 +926,10 @@ int __weak platform_pm_runtime_idle(stru
#endif /* !CONFIG_PM_RUNTIME */

static const struct dev_pm_ops platform_dev_pm_ops = {
- .prepare = platform_pm_prepare,
- .complete = platform_pm_complete,
- .suspend = platform_pm_suspend,
- .resume = platform_pm_resume,
- .freeze = platform_pm_freeze,
- .thaw = platform_pm_thaw,
- .poweroff = platform_pm_poweroff,
- .restore = platform_pm_restore,
- .suspend_noirq = platform_pm_suspend_noirq,
- .resume_noirq = platform_pm_resume_noirq,
- .freeze_noirq = platform_pm_freeze_noirq,
- .thaw_noirq = platform_pm_thaw_noirq,
- .poweroff_noirq = platform_pm_poweroff_noirq,
- .restore_noirq = platform_pm_restore_noirq,
.runtime_suspend = platform_pm_runtime_suspend,
.runtime_resume = platform_pm_runtime_resume,
.runtime_idle = platform_pm_runtime_idle,
+ USE_PLATFORM_PM_SLEEP_OPS
};

struct bus_type platform_bus_type = {
Index: linux-2.6/include/linux/platform_device.h
===================================================================
--- linux-2.6.orig/include/linux/platform_device.h
+++ linux-2.6/include/linux/platform_device.h
@@ -205,4 +205,64 @@ static inline char *early_platform_drive
}
#endif /* MODULE */

+#ifdef CONFIG_PM_SLEEP
+extern int platform_pm_prepare(struct device *dev);
+extern void platform_pm_complete(struct device *dev);
+#else
+#define platform_pm_prepare NULL
+#define platform_pm_complete NULL
+#endif
+
+#ifdef CONFIG_SUSPEND
+extern int platform_pm_suspend(struct device *dev);
+extern int platform_pm_suspend_noirq(struct device *dev);
+extern int platform_pm_resume(struct device *dev);
+extern int platform_pm_resume_noirq(struct device *dev);
+#else
+#define platform_pm_suspend NULL
+#define platform_pm_resume NULL
+#define platform_pm_suspend_noirq NULL
+#define platform_pm_resume_noirq NULL
+#endif
+
+#ifdef CONFIG_HIBERNATE_CALLBACKS
+extern int platform_pm_freeze(struct device *dev);
+extern int platform_pm_freeze_noirq(struct device *dev);
+extern int platform_pm_thaw(struct device *dev);
+extern int platform_pm_thaw_noirq(struct device *dev);
+extern int platform_pm_poweroff(struct device *dev);
+extern int platform_pm_poweroff_noirq(struct device *dev);
+extern int platform_pm_restore(struct device *dev);
+extern int platform_pm_restore_noirq(struct device *dev);
+#else
+#define platform_pm_freeze NULL
+#define platform_pm_thaw NULL
+#define platform_pm_poweroff NULL
+#define platform_pm_restore NULL
+#define platform_pm_freeze_noirq NULL
+#define platform_pm_thaw_noirq NULL
+#define platform_pm_poweroff_noirq NULL
+#define platform_pm_restore_noirq NULL
+#endif
+
+#ifdef CONFIG_PM_SLEEP
+#define USE_PLATFORM_PM_SLEEP_OPS \
+ .prepare = platform_pm_prepare, \
+ .complete = platform_pm_complete, \
+ .suspend = platform_pm_suspend, \
+ .resume = platform_pm_resume, \
+ .freeze = platform_pm_freeze, \
+ .thaw = platform_pm_thaw, \
+ .poweroff = platform_pm_poweroff, \
+ .restore = platform_pm_restore, \
+ .suspend_noirq = platform_pm_suspend_noirq, \
+ .resume_noirq = platform_pm_resume_noirq, \
+ .freeze_noirq = platform_pm_freeze_noirq, \
+ .thaw_noirq = platform_pm_thaw_noirq, \
+ .poweroff_noirq = platform_pm_poweroff_noirq, \
+ .restore_noirq = platform_pm_restore_noirq,
+#else
+#define USE_PLATFORM_PM_SLEEP_OPS
+#endif
+
#endif /* _PLATFORM_DEVICE_H_ */

2011-04-24 21:45:48

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 0/9] PM: Rework shmobile and OMAP runtime PM using power domains (v2)

Hi,

Taking the feedback from the last version of the patchset into account
I reworked patches [7/9] and [8/9] so that multiple clocks can be used for
runtime PM. I also fixed a couple of build issues (the patches have been
built for x86, ARM/shmobile and ARM/OMAP1).

On Sunday, April 17, 2011, Rafael J. Wysocki wrote:
> Hi,
>
> The following series of patches is my attempt to consolidate the shmobile and
> OMAP runtime PM code using power domains so that it is not necessary to
> replace the platform bus type's runtime PM callbacks any more.
>
> Some of the patches have already been posted, some are new and some are
> from Kevin. :-)

The high-level description of what the patches do hasn't changed, so:

[1/9] - Make power domain callbacks take precedence over subsystem ones
(this one has been discussed already).

[2/9] - Export the platform bus type's default PM callbacks so that they
can be used in power domain objects.

[3/9] - Use a default power domain for runtime PM on shmobile (both ARM
and SH flavors) instead of replacing the platform bus type's
runtime PM callbacks (using __weak).

[4/9] - Use generic runtime PM callbacks directly in the platform bus type.

[5/9] - Move OMAP2 runtime PM implementation to using a power domain object
(patch from Kevin rebased on top of [1-4/9]).

[6/9] - Add subsystem data field to struct dev_pm_info.

[7/9] - Introduce generic clock manipulation routines for runtime PM and
convert ARM shmobile to using them.

[8/9] - Move OMAP1 runtime PM to the new core infrastructure.

[9/9] - Remove platform_bus_set_pm_ops() (patch from Kevin).

If there are no objections (or reports of breakage), I'd like to push these
patches through the suspend-2.6 tree in the 2.6.40 merge window.

Thanks,
Rafael

2011-04-24 21:45:45

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 6/9] PM / Runtime: Add subsystem data field to struct dev_pm_info

From: Rafael J. Wysocki <[email protected]>

Some subsystems need to attach PM-related data to struct device and
they need to use devres for this purpose. For their convenience
and to make code more straightforward, add a new field called
subsys_data to struct dev_pm_info and let subsystems use it for
attaching PM-related information to devices.

Convert the ARM shmobile platform to using the new field.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 34 +++++++++++++++++-----------------
include/linux/pm.h | 1 +
2 files changed, 18 insertions(+), 17 deletions(-)

Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -460,6 +460,7 @@ struct dev_pm_info {
unsigned long active_jiffies;
unsigned long suspended_jiffies;
unsigned long accounting_timestamp;
+ void *subsys_data; /* Owned by the subsystem. */
#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
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/sh_clk.h>
#include <linux/bitmap.h>
+#include <linux/slab.h>

#ifdef CONFIG_PM_RUNTIME
#define BIT_ONCE 0
@@ -29,22 +30,9 @@ struct pm_runtime_data {
struct clk *clk;
};

-static void __devres_release(struct device *dev, void *res)
-{
- struct pm_runtime_data *prd = res;
-
- dev_dbg(dev, "__devres_release()\n");
-
- if (test_bit(BIT_CLK_ENABLED, &prd->flags))
- clk_disable(prd->clk);
-
- if (test_bit(BIT_ACTIVE, &prd->flags))
- clk_put(prd->clk);
-}
-
static struct pm_runtime_data *__to_prd(struct device *dev)
{
- return devres_find(dev, __devres_release, NULL, NULL);
+ return dev ? dev->power.subsys_data : NULL;
}

static void platform_pm_runtime_init(struct device *dev,
@@ -121,14 +109,26 @@ static int platform_bus_notify(struct no

dev_dbg(dev, "platform_bus_notify() %ld !\n", action);

- if (action == BUS_NOTIFY_BIND_DRIVER) {
- prd = devres_alloc(__devres_release, sizeof(*prd), GFP_KERNEL);
+ switch (action) {
+ case BUS_NOTIFY_BIND_DRIVER:
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
if (prd) {
- devres_add(dev, 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;

2011-04-24 21:45:43

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 1/9] PM: Make power domain callbacks take precedence over subsystem ones

From: Rafael J. Wysocki <[email protected]>

Change the PM core's behavior related to power domains in such a way
that, if a power domain is defined for a given device, its callbacks
will be executed instead of and not in addition to the device
subsystem's PM callbacks.

The idea behind the initial implementation of power domains handling
by the PM core was that power domain callbacks would be executed in
addition to subsystem callbacks, so that it would be possible to
extend the subsystem callbacks by using power domains. It turns out,
however, that this wouldn't be really convenient in some important
situations.

For example, there are systems in which power can only be removed
from entire power domains. On those systems it is not desirable to
execute device drivers' PM callbacks until it is known that power is
going to be removed from the devices in question, which means that
they should be executed by power domain callbacks rather then by
subsystem (e.g. bus type) PM callbacks, because subsystems generally
have no information about what devices belong to which power domain.
Thus, for instance, if the bus type in question is the platform bus
type, its PM callbacks generally should not be called in addition to
power domain callbacks, because they run device drivers' callbacks
unconditionally if defined.

While in principle the default subsystem PM callbacks, or a subset of
them, may be replaced with different functions, it doesn't seem
correct to do so, because that would change the subsystem's behavior
with respect to all devices in the system, regardless of whether or
not they belong to any power domains. Thus, the only remaining
option is to make power domain callbacks take precedence over
subsystem callbacks.

Signed-off-by: Rafael J. Wysocki <[email protected]>
Acked-by: Grant Likely <[email protected]>
Acked-by: Kevin Hilman <[email protected]>
---
drivers/base/power/main.c | 64 ++++++++++++++++++++-----------------------
drivers/base/power/runtime.c | 29 ++++++-------------
2 files changed, 41 insertions(+), 52 deletions(-)

Index: linux-2.6/drivers/base/power/runtime.c
===================================================================
--- linux-2.6.orig/drivers/base/power/runtime.c
+++ linux-2.6/drivers/base/power/runtime.c
@@ -168,7 +168,6 @@ static int rpm_check_suspend_allowed(str
static int rpm_idle(struct device *dev, int rpmflags)
{
int (*callback)(struct device *);
- int (*domain_callback)(struct device *);
int retval;

retval = rpm_check_suspend_allowed(dev);
@@ -214,7 +213,9 @@ static int rpm_idle(struct device *dev,

dev->power.idle_notification = true;

- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_idle;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_idle;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_idle;
@@ -223,19 +224,10 @@ static int rpm_idle(struct device *dev,
else
callback = NULL;

- if (dev->pwr_domain)
- domain_callback = dev->pwr_domain->ops.runtime_idle;
- else
- domain_callback = NULL;
-
- if (callback || domain_callback) {
+ if (callback) {
spin_unlock_irq(&dev->power.lock);

- if (domain_callback)
- retval = domain_callback(dev);
-
- if (!retval && callback)
- callback(dev);
+ callback(dev);

spin_lock_irq(&dev->power.lock);
}
@@ -382,7 +374,9 @@ static int rpm_suspend(struct device *de

__update_runtime_status(dev, RPM_SUSPENDING);

- if (dev->type && dev->type->pm)
+ if (dev->pwr_domain)
+ callback = dev->pwr_domain->ops.runtime_suspend;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_suspend;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_suspend;
@@ -400,8 +394,6 @@ static int rpm_suspend(struct device *de
else
pm_runtime_cancel_pending(dev);
} else {
- if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_suspend, dev);
no_callback:
__update_runtime_status(dev, RPM_SUSPENDED);
pm_runtime_deactivate_timer(dev);
@@ -582,9 +574,8 @@ static int rpm_resume(struct device *dev
__update_runtime_status(dev, RPM_RESUMING);

if (dev->pwr_domain)
- rpm_callback(dev->pwr_domain->ops.runtime_resume, dev);
-
- if (dev->type && dev->type->pm)
+ callback = dev->pwr_domain->ops.runtime_resume;
+ else if (dev->type && dev->type->pm)
callback = dev->type->pm->runtime_resume;
else if (dev->class && dev->class->pm)
callback = dev->class->pm->runtime_resume;
Index: linux-2.6/drivers/base/power/main.c
===================================================================
--- linux-2.6.orig/drivers/base/power/main.c
+++ linux-2.6/drivers/base/power/main.c
@@ -426,10 +426,8 @@ static int device_resume_noirq(struct de

if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "EARLY power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
- if (dev->type && dev->type->pm) {
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "EARLY type ");
error = pm_noirq_op(dev, dev->type->pm, state);
} else if (dev->class && dev->class->pm) {
@@ -517,7 +515,8 @@ static int device_resume(struct device *

if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
}

if (dev->type && dev->type->pm) {
@@ -629,12 +628,11 @@ static void device_complete(struct devic
{
device_lock(dev);

- if (dev->pwr_domain && dev->pwr_domain->ops.complete) {
+ if (dev->pwr_domain) {
pm_dev_dbg(dev, state, "completing power domain ");
- dev->pwr_domain->ops.complete(dev);
- }
-
- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain->ops.complete)
+ dev->pwr_domain->ops.complete(dev);
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "completing type ");
if (dev->type->pm->complete)
dev->type->pm->complete(dev);
@@ -732,7 +730,12 @@ static int device_suspend_noirq(struct d
{
int error;

- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "LATE power domain ");
+ error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+ if (error)
+ return error;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "LATE type ");
error = pm_noirq_op(dev, dev->type->pm, state);
if (error)
@@ -749,11 +752,6 @@ static int device_suspend_noirq(struct d
return error;
}

- if (dev->pwr_domain) {
- pm_dev_dbg(dev, state, "LATE power domain ");
- pm_noirq_op(dev, &dev->pwr_domain->ops, state);
- }
-
return 0;
}

@@ -841,21 +839,27 @@ static int __device_suspend(struct devic
goto End;
}

+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "power domain ");
+ error = pm_op(dev, &dev->pwr_domain->ops, state);
+ goto End;
+ }
+
if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "type ");
error = pm_op(dev, dev->type->pm, state);
- goto Domain;
+ goto End;
}

if (dev->class) {
if (dev->class->pm) {
pm_dev_dbg(dev, state, "class ");
error = pm_op(dev, dev->class->pm, state);
- goto Domain;
+ goto End;
} else if (dev->class->suspend) {
pm_dev_dbg(dev, state, "legacy class ");
error = legacy_suspend(dev, state, dev->class->suspend);
- goto Domain;
+ goto End;
}
}

@@ -869,12 +873,6 @@ static int __device_suspend(struct devic
}
}

- Domain:
- if (!error && dev->pwr_domain) {
- pm_dev_dbg(dev, state, "power domain ");
- pm_op(dev, &dev->pwr_domain->ops, state);
- }
-
End:
device_unlock(dev);
complete_all(&dev->power.completion);
@@ -965,7 +963,14 @@ static int device_prepare(struct device

device_lock(dev);

- if (dev->type && dev->type->pm) {
+ if (dev->pwr_domain) {
+ pm_dev_dbg(dev, state, "preparing power domain ");
+ if (dev->pwr_domain->ops.prepare)
+ error = dev->pwr_domain->ops.prepare(dev);
+ suspend_report_result(dev->pwr_domain->ops.prepare, error);
+ if (error)
+ goto End;
+ } else if (dev->type && dev->type->pm) {
pm_dev_dbg(dev, state, "preparing type ");
if (dev->type->pm->prepare)
error = dev->type->pm->prepare(dev);
@@ -984,13 +989,6 @@ static int device_prepare(struct device
if (dev->bus->pm->prepare)
error = dev->bus->pm->prepare(dev);
suspend_report_result(dev->bus->pm->prepare, error);
- if (error)
- goto End;
- }
-
- if (dev->pwr_domain && dev->pwr_domain->ops.prepare) {
- pm_dev_dbg(dev, state, "preparing power domain ");
- dev->pwr_domain->ops.prepare(dev);
}

End:

2011-04-24 21:45:40

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 8/9] OMAP1 / PM: Use generic clock manipulation routines for runtime PM

From: Rafael J. Wysocki <[email protected]>

Convert OMAP1 to using the new generic clock manipulation routines
and a device power domain for runtime PM instead of overriding the
platform bus type's runtime PM callbacks. This allows us to simplify
OMAP1-specific code and to share some code with other platforms
(shmobile in particular).

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
arch/arm/mach-omap1/pm_bus.c | 69 +++++++++++++------------------------------
1 file changed, 22 insertions(+), 47 deletions(-)

Index: linux-2.6/arch/arm/mach-omap1/pm_bus.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-omap1/pm_bus.c
+++ linux-2.6/arch/arm/mach-omap1/pm_bus.c
@@ -24,75 +24,50 @@
#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_clk_suspend(dev);
+ if (ret) {
+ pm_generic_runtime_resume(dev);
+ return ret;
}

return 0;
-};
+}

static int omap1_pm_runtime_resume(struct device *dev)
{
- struct clk *iclk, *fclk;
-
dev_dbg(dev, "%s\n", __func__);

- iclk = clk_get(dev, "ick");
- if (!IS_ERR(iclk)) {
- clk_enable(iclk);
- clk_put(iclk);
- }
+ pm_runtime_clk_resume(dev);
+ return pm_generic_runtime_resume(dev);
+}

- fclk = clk_get(dev, "fck");
- if (!IS_ERR(fclk)) {
- clk_enable(fclk);
- clk_put(fclk);
- }
+static struct dev_power_domain default_power_domain = {
+ .ops = {
+ .runtime_suspend = omap1_pm_runtime_suspend,
+ .runtime_resume = omap1_pm_runtime_resume,
+ USE_PLATFORM_PM_SLEEP_OPS
+ },
+};

- return pm_generic_runtime_resume(dev);
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pwr_domain = &default_power_domain,
+ .con_ids = { "ick", "fck", NULL, },
};

static int __init omap1_pm_runtime_init(void)
{
- const struct dev_pm_ops *pm;
- struct dev_pm_ops *omap_pm;
-
if (!cpu_class_is_omap1())
return -ENODEV;

- pm = platform_bus_get_pm_ops();
- if (!pm) {
- pr_err("%s: unable to get dev_pm_ops from platform_bus\n",
- __func__);
- return -ENODEV;
- }
-
- omap_pm = kmemdup(pm, sizeof(struct dev_pm_ops), GFP_KERNEL);
- if (!omap_pm) {
- pr_err("%s: unable to alloc memory for new dev_pm_ops\n",
- __func__);
- return -ENOMEM;
- }
-
- omap_pm->runtime_suspend = omap1_pm_runtime_suspend;
- omap_pm->runtime_resume = omap1_pm_runtime_resume;
-
- platform_bus_set_pm_ops(omap_pm);
+ pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);

return 0;
}

2011-04-24 21:45:30

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 9/9] PM: Revert "driver core: platform_bus: allow runtime override of dev_pm_ops"

From: Kevin Hilman <[email protected]>

The platform_bus_set_pm_ops() operation is deprecated in favor of the
new device power domain infrastructre implemented in commit
7538e3db6e015e890825fbd9f8659952896ddd5b (PM: add support for device
power domains)

Signed-off-by: Kevin Hilman <[email protected]>
Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 35 -----------------------------------
include/linux/platform_device.h | 3 ---
2 files changed, 38 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -916,41 +916,6 @@ struct bus_type platform_bus_type = {
};
EXPORT_SYMBOL_GPL(platform_bus_type);

-/**
- * platform_bus_get_pm_ops() - return pointer to busses dev_pm_ops
- *
- * This function can be used by platform code to get the current
- * set of dev_pm_ops functions used by the platform_bus_type.
- */
-const struct dev_pm_ops * __init platform_bus_get_pm_ops(void)
-{
- return platform_bus_type.pm;
-}
-
-/**
- * platform_bus_set_pm_ops() - update dev_pm_ops for the platform_bus_type
- *
- * @pm: pointer to new dev_pm_ops struct to be used for platform_bus_type
- *
- * Platform code can override the dev_pm_ops methods of
- * platform_bus_type by using this function. It is expected that
- * platform code will first do a platform_bus_get_pm_ops(), then
- * kmemdup it, then customize selected methods and pass a pointer to
- * the new struct dev_pm_ops to this function.
- *
- * Since platform-specific code is customizing methods for *all*
- * devices (not just platform-specific devices) it is expected that
- * any custom overrides of these functions will keep existing behavior
- * and simply extend it. For example, any customization of the
- * runtime PM methods should continue to call the pm_generic_*
- * functions as the default ones do in addition to the
- * platform-specific behavior.
- */
-void __init platform_bus_set_pm_ops(const struct dev_pm_ops *pm)
-{
- platform_bus_type.pm = pm;
-}
-
int __init platform_bus_init(void)
{
int error;
Index: linux-2.6/include/linux/platform_device.h
===================================================================
--- linux-2.6.orig/include/linux/platform_device.h
+++ linux-2.6/include/linux/platform_device.h
@@ -150,9 +150,6 @@ extern struct platform_device *platform_
struct resource *res, unsigned int n_res,
const void *data, size_t size);

-extern const struct dev_pm_ops * platform_bus_get_pm_ops(void);
-extern void platform_bus_set_pm_ops(const struct dev_pm_ops *pm);
-
/* early platform driver interface */
struct early_platform_driver {
const char *class_str;

2011-04-24 21:45:26

by Rafael J. Wysocki

[permalink] [raw]
Subject: [PATCH 4/9] PM / Platform: Use generic runtime PM callbacks directly

From: Rafael J. Wysocki <[email protected]>

Once shmobile platforms have been converted to using power domains
for overriding the platform bus type's PM callbacks, it isn't
necessary to use the __weakly defined wrappers around the generinc
runtime PM callbacks in the platform bus type any more.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/platform.c | 31 +++----------------------------
1 file changed, 3 insertions(+), 28 deletions(-)

Index: linux-2.6/drivers/base/platform.c
===================================================================
--- linux-2.6.orig/drivers/base/platform.c
+++ linux-2.6/drivers/base/platform.c
@@ -900,35 +900,10 @@ int platform_pm_restore_noirq(struct dev

#endif /* CONFIG_HIBERNATE_CALLBACKS */

-#ifdef CONFIG_PM_RUNTIME
-
-int __weak platform_pm_runtime_suspend(struct device *dev)
-{
- return pm_generic_runtime_suspend(dev);
-};
-
-int __weak platform_pm_runtime_resume(struct device *dev)
-{
- return pm_generic_runtime_resume(dev);
-};
-
-int __weak platform_pm_runtime_idle(struct device *dev)
-{
- return pm_generic_runtime_idle(dev);
-};
-
-#else /* !CONFIG_PM_RUNTIME */
-
-#define platform_pm_runtime_suspend NULL
-#define platform_pm_runtime_resume NULL
-#define platform_pm_runtime_idle NULL
-
-#endif /* !CONFIG_PM_RUNTIME */
-
static const struct dev_pm_ops platform_dev_pm_ops = {
- .runtime_suspend = platform_pm_runtime_suspend,
- .runtime_resume = platform_pm_runtime_resume,
- .runtime_idle = platform_pm_runtime_idle,
+ .runtime_suspend = pm_generic_runtime_suspend,
+ .runtime_resume = pm_generic_runtime_resume,
+ .runtime_idle = pm_generic_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
};

2011-04-25 00:02:39

by Greg KH

[permalink] [raw]
Subject: Re: [PATCH 0/9] PM: Rework shmobile and OMAP runtime PM using power domains (v2)

On Sun, Apr 24, 2011 at 11:30:13PM +0200, Rafael J. Wysocki wrote:
> Hi,
>
> Taking the feedback from the last version of the patchset into account
> I reworked patches [7/9] and [8/9] so that multiple clocks can be used for
> runtime PM. I also fixed a couple of build issues (the patches have been
> built for x86, ARM/shmobile and ARM/OMAP1).
>
> On Sunday, April 17, 2011, Rafael J. Wysocki wrote:
> > Hi,
> >
> > The following series of patches is my attempt to consolidate the shmobile and
> > OMAP runtime PM code using power domains so that it is not necessary to
> > replace the platform bus type's runtime PM callbacks any more.
> >
> > Some of the patches have already been posted, some are new and some are
> > from Kevin. :-)
>
> The high-level description of what the patches do hasn't changed, so:
>
> [1/9] - Make power domain callbacks take precedence over subsystem ones
> (this one has been discussed already).
>
> [2/9] - Export the platform bus type's default PM callbacks so that they
> can be used in power domain objects.
>
> [3/9] - Use a default power domain for runtime PM on shmobile (both ARM
> and SH flavors) instead of replacing the platform bus type's
> runtime PM callbacks (using __weak).
>
> [4/9] - Use generic runtime PM callbacks directly in the platform bus type.
>
> [5/9] - Move OMAP2 runtime PM implementation to using a power domain object
> (patch from Kevin rebased on top of [1-4/9]).
>
> [6/9] - Add subsystem data field to struct dev_pm_info.
>
> [7/9] - Introduce generic clock manipulation routines for runtime PM and
> convert ARM shmobile to using them.
>
> [8/9] - Move OMAP1 runtime PM to the new core infrastructure.
>
> [9/9] - Remove platform_bus_set_pm_ops() (patch from Kevin).
>
> If there are no objections (or reports of breakage), I'd like to push these
> patches through the suspend-2.6 tree in the 2.6.40 merge window.

No objections from me at all, looks great, thanks for doing this.

greg k-h

2011-04-27 21:48:07

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

From: Rafael J. Wysocki <[email protected]>

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 <[email protected]>
---

Hi,

This (hopefully final) version of the patch has a couple of bugs fixed in
clock_ops.c.

Thanks,
Rafael

---
arch/arm/mach-shmobile/pm_runtime.c | 140 -----------
drivers/base/power/Makefile | 1
drivers/base/power/clock_ops.c | 423 ++++++++++++++++++++++++++++++++++++
include/linux/pm_runtime.h | 42 +++
kernel/power/Kconfig | 4
5 files changed, 479 insertions(+), 131 deletions(-)

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -0,0 +1,423 @@
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <[email protected]>, Renesas Solutions Corp.
+ *
+ * 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_clk_data {
+ struct list_head clock_list;
+ struct mutex lock;
+};
+
+struct pm_clock_entry {
+ struct list_head node;
+ char *con_id;
+ struct clk *clk;
+ unsigned int clock_active:1;
+ unsigned int clock_enabled:1;
+};
+
+static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
+/**
+ * pm_runtime_clk_add - Start using a device clock for runtime PM.
+ * @dev: Device whose clock is going to be used for runtime PM.
+ * @con_id: Connection ID of the clock.
+ *
+ * Add the clock represented by @con_id to the list of clocks used for
+ * the runtime PM of @dev.
+ */
+int pm_runtime_clk_add(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return -EINVAL;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ dev_err(dev, "Not enough memory for clock entry.\n");
+ return -ENOMEM;
+ }
+
+ if (con_id) {
+ ce->con_id = kstrdup(con_id, GFP_KERNEL);
+ if (!ce->con_id) {
+ dev_err(dev,
+ "Not enough memory for clock conection ID.\n");
+ kfree(ce);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&prd->lock);
+ list_add_tail(&ce->node, &prd->clock_list);
+ mutex_unlock(&prd->lock);
+ return 0;
+}
+
+/**
+ * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
+ * @ce: Runtime PM clock entry to destroy.
+ *
+ * This routine must be called under the mutex protecting the runtime PM list
+ * of clocks corresponding the the @ce's device.
+ */
+static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+{
+ if (!ce)
+ return;
+
+ list_del(&ce->node);
+
+ if (ce->clk) {
+ if (ce->clock_enabled)
+ clk_disable(ce->clk);
+
+ if (ce->clock_active)
+ clk_put(ce->clk);
+ }
+
+ if (ce->con_id)
+ kfree(ce->con_id);
+
+ kfree(ce);
+}
+
+/**
+ * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
+ * @dev: Device whose clock should not be used for runtime PM any more.
+ * @con_id: Connection ID of the clock.
+ *
+ * Remove the clock represented by @con_id from the list of clocks used for
+ * the runtime PM of @dev.
+ */
+void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node)
+ if (!con_id && !ce->con_id) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ } else if (!con_id || !ce->con_id) {
+ continue;
+ } else if (!strcmp(con_id, ce->con_id)) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ }
+
+ mutex_unlock(&prd->lock);
+}
+
+/**
+ * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
+ * @dev: Device to initialize the list of runtime PM clocks for.
+ *
+ * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * make the @dev's power.subsys_data field point to it.
+ */
+int pm_runtime_clk_init(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd;
+
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+ if (!prd) {
+ dev_err(dev, "Not enough memory fo runtime PM data.\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&prd->clock_list);
+ mutex_init(&prd->lock);
+ dev->power.subsys_data = prd;
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
+ * @dev: Device to destroy the list of runtime PM clocks for.
+ *
+ * Clear the @dev's power.subsys_data field, remove the list of clock entries
+ * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * that object.
+ */
+void pm_runtime_clk_destroy(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce, *c;
+
+ if (!prd)
+ return;
+
+ dev->power.subsys_data = NULL;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
+ __pm_runtime_clk_remove(ce);
+
+ mutex_unlock(&prd->lock);
+
+ kfree(prd);
+}
+
+/**
+ * pm_runtime_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @con_id: Connection ID of the clock.
+ */
+static void pm_runtime_clk_acquire(struct device *dev,
+ struct pm_clock_entry *ce)
+{
+ ce->clk = clk_get(dev, ce->con_id);
+ if (!IS_ERR(ce->clk)) {
+ ce->clock_active = true;
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ }
+}
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_runtime_clk_suspend(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+ if (!ce->clk) {
+ dev_err(dev, "Clock is not ready for runtime PM\n");
+ pm_runtime_clk_acquire(dev, ce);
+ }
+
+ if (ce->clock_active) {
+ clk_disable(ce->clk);
+ ce->clock_enabled = false;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_runtime_clk_resume(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (!ce->clk)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (ce->clock_active) {
+ clk_enable(ce->clk);
+ ce->clock_enabled = true;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the pwr_domain member of that object is copied to the device's
+ * pwr_domain field and its con_ids member is used to populate the device's list
+ * of runtime PM clocks, depending on @action.
+ *
+ * If the device's pwr_domain field is already populated with a value different
+ * from the one stored in the struct pm_clk_notifier_block object, the function
+ * does nothing.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+ int error;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->pwr_domain)
+ break;
+
+ error = pm_runtime_clk_init(dev);
+ if (error)
+ break;
+
+ dev->pwr_domain = clknb->pwr_domain;
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ pm_runtime_clk_add(dev, con_id);
+ } else {
+ pm_runtime_clk_add(dev, NULL);
+ }
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->pwr_domain != clknb->pwr_domain)
+ break;
+
+ dev->pwr_domain = NULL;
+ pm_runtime_clk_destroy(dev);
+ break;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+/**
+ * enable_clock - Enable a device clock.
+ * @dev: Device whose clock is to be enabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void enable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced on.\n");
+ }
+}
+
+/**
+ * disable_clock - Disable a device clock.
+ * @dev: Device whose clock is to be disabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void disable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced off.\n");
+ }
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the con_ids member of that object is used to enable or disable
+ * the device's clocks, depending on @action.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ enable_clock(dev, con_id);
+ } else {
+ enable_clock(dev, NULL);
+ }
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ disable_clock(dev, con_id);
+ } else {
+ disable_clock(dev, NULL);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+/**
+ * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * @bus: Bus type to add the notifier to.
+ * @clknb: Notifier to be added to the given bus type.
+ *
+ * The nb member of @clknb is not expected to be initialized and its
+ * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * the remaining members of @clknb should be populated prior to calling this
+ * routine.
+ */
+void pm_runtime_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+ if (!bus || !clknb)
+ return;
+
+ clknb->nb.notifier_call = pm_runtime_clk_notify;
+ bus_register_notifier(bus, &clknb->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,46 @@ static inline void pm_runtime_dont_use_a
__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
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)
{
@@ -94,87 +30,29 @@ static int default_platform_runtime_idle

static struct dev_power_domain default_power_domain = {
.ops = {
- .runtime_suspend = default_platform_runtime_suspend,
- .runtime_resume = default_platform_runtime_resume,
+ .runtime_suspend = pm_runtime_clk_suspend,
+ .runtime_resume = pm_runtime_clk_resume,
.runtime_idle = default_platform_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
},
};

-static int platform_bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct pm_runtime_data *prd;
+#define DEFAULT_PWR_DOMAIN_PTR (&default_power_domain)

- dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
+#else

- 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;
-}
+#define DEFAULT_PWR_DOMAIN_PTR NULL

#endif /* CONFIG_PM_RUNTIME */

-static struct notifier_block platform_bus_notifier = {
- .notifier_call = platform_bus_notify
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+ .con_ids = { NULL, },
};

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;
}
core_initcall(sh_pm_runtime_init);

2011-04-27 23:04:15

by Colin Cross

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

On Wed, Apr 27, 2011 at 2:48 PM, Rafael J. Wysocki <[email protected]> wrote:
> From: Rafael J. Wysocki <[email protected]>
>
> 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 <[email protected]>
> ---
>
> Hi,
>
> This (hopefully final) version of the patch has a couple of bugs fixed in
> clock_ops.c.
>
> Thanks,
> Rafael
>
> ---
> ?arch/arm/mach-shmobile/pm_runtime.c | ?140 -----------
> ?drivers/base/power/Makefile ? ? ? ? | ? ?1
> ?drivers/base/power/clock_ops.c ? ? ?| ?423 ++++++++++++++++++++++++++++++++++++
> ?include/linux/pm_runtime.h ? ? ? ? ?| ? 42 +++
> ?kernel/power/Kconfig ? ? ? ? ? ? ? ?| ? ?4
> ?5 files changed, 479 insertions(+), 131 deletions(-)
>
<snip>
> +void pm_runtime_clk_remove(struct device *dev, const char *con_id)
> +{
> + struct pm_runtime_clk_data *prd = __to_prd(dev);
> + struct pm_clock_entry *ce;
> +
> + if (!prd)
> + return;
> +
> + mutex_lock(&prd->lock);
> +
> + list_for_each_entry(ce, &prd->clock_list, node)
Braces
> + if (!con_id && !ce->con_id) {
> + __pm_runtime_clk_remove(ce);
> + break;
> + } else if (!con_id || !ce->con_id) {
> + continue;
> + } else if (!strcmp(con_id, ce->con_id)) {
> + __pm_runtime_clk_remove(ce);
> + break;
> + }
> +
> + mutex_unlock(&prd->lock);
> +}
>
> +/**
> + * pm_runtime_clk_acquire - Acquire a device clock.
> + * @dev: Device whose clock is to be acquired.
> + * @con_id: Connection ID of the clock.
> + */
> +static void pm_runtime_clk_acquire(struct device *dev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct pm_clock_entry *ce)
> +{
> + ? ? ? ce->clk = clk_get(dev, ce->con_id);
> + ? ? ? if (!IS_ERR(ce->clk)) {
> + ? ? ? ? ? ? ? ce->clock_active = true;
> + ? ? ? ? ? ? ? dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
> + ? ? ? }
> +}
> +
> +/**
> + * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
> + * @dev: Device to disable the clocks for.
> + */
> +int pm_runtime_clk_suspend(struct device *dev)
> +{
> + ? ? ? struct pm_runtime_clk_data *prd = __to_prd(dev);
> + ? ? ? struct pm_clock_entry *ce;
> +
> + ? ? ? dev_dbg(dev, "%s()\n", __func__);
> +
> + ? ? ? if (!prd)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? mutex_lock(&prd->lock);
> +
> + ? ? ? list_for_each_entry_reverse(ce, &prd->clock_list, node) {
> + ? ? ? ? ? ? ? if (!ce->clk) {
> + ? ? ? ? ? ? ? ? ? ? ? dev_err(dev, "Clock is not ready for runtime PM\n");
> + ? ? ? ? ? ? ? ? ? ? ? pm_runtime_clk_acquire(dev, ce);
Why delay the call to clk_get until the first suspend? Also, this
will always print an error during the first call to suspend.

> + ? ? ? ? ? ? ? }
> +
> + ? ? ? ? ? ? ? if (ce->clock_active) {
I don't think clock_active is necessary, and the name is misleading.
Why not use if (ce->clk)?

> + ? ? ? ? ? ? ? ? ? ? ? clk_disable(ce->clk);
> + ? ? ? ? ? ? ? ? ? ? ? ce->clock_enabled = false;
Clock enables are already refcounted, do you really need a flag as
well? In what situations would pm_runtime_clk_remove get called,
which currently needs to know when the clock is enabled?

> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? mutex_unlock(&prd->lock);
> +
> + ? ? ? return 0;
> +}
> +
> +/**
> + * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
> + * @dev: Device to enable the clocks for.
> + */
> +int pm_runtime_clk_resume(struct device *dev)
> +{
> + ? ? ? struct pm_runtime_clk_data *prd = __to_prd(dev);
> + ? ? ? struct pm_clock_entry *ce;
> +
> + ? ? ? dev_dbg(dev, "%s()\n", __func__);
> +
> + ? ? ? if (!prd)
> + ? ? ? ? ? ? ? return 0;
> +
> + ? ? ? mutex_lock(&prd->lock);
> +
> + ? ? ? list_for_each_entry(ce, &prd->clock_list, node) {
> + ? ? ? ? ? ? ? if (!ce->clk)
> + ? ? ? ? ? ? ? ? ? ? ? pm_runtime_clk_acquire(dev, ce);
If the clock was not present during suspend, should you be enabling it
during resume?

> +
> + ? ? ? ? ? ? ? if (ce->clock_active) {
> + ? ? ? ? ? ? ? ? ? ? ? clk_enable(ce->clk);
> + ? ? ? ? ? ? ? ? ? ? ? ce->clock_enabled = true;
> + ? ? ? ? ? ? ? }
> + ? ? ? }
> +
> + ? ? ? mutex_unlock(&prd->lock);
> +
> + ? ? ? return 0;
> +}

2011-04-28 00:58:24

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

On Thursday, April 28, 2011, Colin Cross wrote:
> On Wed, Apr 27, 2011 at 2:48 PM, Rafael J. Wysocki <[email protected]> wrote:
> > From: Rafael J. Wysocki <[email protected]>
> >
> > 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 <[email protected]>
> > ---
> >
> > Hi,
> >
> > This (hopefully final) version of the patch has a couple of bugs fixed in
> > clock_ops.c.
> >
> > Thanks,
> > Rafael
> >
> > ---
> > arch/arm/mach-shmobile/pm_runtime.c | 140 -----------
> > drivers/base/power/Makefile | 1
> > drivers/base/power/clock_ops.c | 423 ++++++++++++++++++++++++++++++++++++
> > include/linux/pm_runtime.h | 42 +++
> > kernel/power/Kconfig | 4
> > 5 files changed, 479 insertions(+), 131 deletions(-)
> >
> <snip>
> > +void pm_runtime_clk_remove(struct device *dev, const char *con_id)
> > +{
> > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > + struct pm_clock_entry *ce;
> > +
> > + if (!prd)
> > + return;
> > +
> > + mutex_lock(&prd->lock);
> > +
> > + list_for_each_entry(ce, &prd->clock_list, node)
> Braces

No, this is correct as is.

> > + if (!con_id && !ce->con_id) {
> > + __pm_runtime_clk_remove(ce);
> > + break;
> > + } else if (!con_id || !ce->con_id) {
> > + continue;
> > + } else if (!strcmp(con_id, ce->con_id)) {
> > + __pm_runtime_clk_remove(ce);
> > + break;
> > + }
> > +
> > + mutex_unlock(&prd->lock);
> > +}
> >
> > +/**
> > + * pm_runtime_clk_acquire - Acquire a device clock.
> > + * @dev: Device whose clock is to be acquired.
> > + * @con_id: Connection ID of the clock.
> > + */
> > +static void pm_runtime_clk_acquire(struct device *dev,
> > + struct pm_clock_entry *ce)
> > +{
> > + ce->clk = clk_get(dev, ce->con_id);
> > + if (!IS_ERR(ce->clk)) {
> > + ce->clock_active = true;
> > + dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
> > + }
> > +}
> > +
> > +/**
> > + * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
> > + * @dev: Device to disable the clocks for.
> > + */
> > +int pm_runtime_clk_suspend(struct device *dev)
> > +{
> > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > + struct pm_clock_entry *ce;
> > +
> > + dev_dbg(dev, "%s()\n", __func__);
> > +
> > + if (!prd)
> > + return 0;
> > +
> > + mutex_lock(&prd->lock);
> > +
> > + list_for_each_entry_reverse(ce, &prd->clock_list, node) {
> > + if (!ce->clk) {
> > + dev_err(dev, "Clock is not ready for runtime PM\n");
> > + pm_runtime_clk_acquire(dev, ce);
> Why delay the call to clk_get until the first suspend?

Because the clock framework need not be ready at the _add time.

> Also, this will always print an error during the first call to suspend.

That actually depends on the initial state of the device and the
assumption is that will be RPM_SUSPENDED, so _resume will be called
first.

I can remove the message, but it's there for backwards compatibility with
the code this is intended to replace.

> > + }
> > +
> > + if (ce->clock_active) {
> I don't think clock_active is necessary, and the name is misleading.

It's not strictly necessary and "active" means "being used for runtime PM".

> Why not use if (ce->clk)?

Because _that_ would be confusing?

> > + clk_disable(ce->clk);
> > + ce->clock_enabled = false;
> Clock enables are already refcounted, do you really need a flag as
> well? In what situations would pm_runtime_clk_remove get called,
> which currently needs to know when the clock is enabled?

When the device object is removed, for whatever reason.

> > + }
> > + }
> > +
> > + mutex_unlock(&prd->lock);
> > +
> > + return 0;
> > +}
> > +
> > +/**
> > + * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
> > + * @dev: Device to enable the clocks for.
> > + */
> > +int pm_runtime_clk_resume(struct device *dev)
> > +{
> > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > + struct pm_clock_entry *ce;
> > +
> > + dev_dbg(dev, "%s()\n", __func__);
> > +
> > + if (!prd)
> > + return 0;
> > +
> > + mutex_lock(&prd->lock);
> > +
> > + list_for_each_entry(ce, &prd->clock_list, node) {
> > + if (!ce->clk)
> > + pm_runtime_clk_acquire(dev, ce);
> If the clock was not present during suspend, should you be enabling it
> during resume?

Because resume is called first as the initial state of the device
is assumed to be RPM_SUSPENDED.

> > +
> > + if (ce->clock_active) {
> > + clk_enable(ce->clk);
> > + ce->clock_enabled = true;
> > + }
> > + }
> > +
> > + mutex_unlock(&prd->lock);
> > +
> > + return 0;
> > +}

HTH,
Rafael

2011-04-28 01:06:04

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

On Thursday, April 28, 2011, Rafael J. Wysocki wrote:
> On Thursday, April 28, 2011, Colin Cross wrote:
> > On Wed, Apr 27, 2011 at 2:48 PM, Rafael J. Wysocki <[email protected]> wrote:
> > > From: Rafael J. Wysocki <[email protected]>
> > >
> > > 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 <[email protected]>
> > > ---
> > >
> > > Hi,
> > >
> > > This (hopefully final) version of the patch has a couple of bugs fixed in
> > > clock_ops.c.
> > >
> > > Thanks,
> > > Rafael
> > >
> > > ---
> > > arch/arm/mach-shmobile/pm_runtime.c | 140 -----------
> > > drivers/base/power/Makefile | 1
> > > drivers/base/power/clock_ops.c | 423 ++++++++++++++++++++++++++++++++++++
> > > include/linux/pm_runtime.h | 42 +++
> > > kernel/power/Kconfig | 4
> > > 5 files changed, 479 insertions(+), 131 deletions(-)
> > >
> > <snip>
> > > +void pm_runtime_clk_remove(struct device *dev, const char *con_id)
> > > +{
> > > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > > + struct pm_clock_entry *ce;
> > > +
> > > + if (!prd)
> > > + return;
> > > +
> > > + mutex_lock(&prd->lock);
> > > +
> > > + list_for_each_entry(ce, &prd->clock_list, node)
> > Braces
>
> No, this is correct as is.
>
> > > + if (!con_id && !ce->con_id) {
> > > + __pm_runtime_clk_remove(ce);
> > > + break;
> > > + } else if (!con_id || !ce->con_id) {
> > > + continue;
> > > + } else if (!strcmp(con_id, ce->con_id)) {
> > > + __pm_runtime_clk_remove(ce);
> > > + break;
> > > + }
> > > +
> > > + mutex_unlock(&prd->lock);
> > > +}
> > >
> > > +/**
> > > + * pm_runtime_clk_acquire - Acquire a device clock.
> > > + * @dev: Device whose clock is to be acquired.
> > > + * @con_id: Connection ID of the clock.
> > > + */
> > > +static void pm_runtime_clk_acquire(struct device *dev,
> > > + struct pm_clock_entry *ce)
> > > +{
> > > + ce->clk = clk_get(dev, ce->con_id);
> > > + if (!IS_ERR(ce->clk)) {
> > > + ce->clock_active = true;
> > > + dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
> > > + }
> > > +}
> > > +
> > > +/**
> > > + * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
> > > + * @dev: Device to disable the clocks for.
> > > + */
> > > +int pm_runtime_clk_suspend(struct device *dev)
> > > +{
> > > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > > + struct pm_clock_entry *ce;
> > > +
> > > + dev_dbg(dev, "%s()\n", __func__);
> > > +
> > > + if (!prd)
> > > + return 0;
> > > +
> > > + mutex_lock(&prd->lock);
> > > +
> > > + list_for_each_entry_reverse(ce, &prd->clock_list, node) {
> > > + if (!ce->clk) {
> > > + dev_err(dev, "Clock is not ready for runtime PM\n");
> > > + pm_runtime_clk_acquire(dev, ce);
> > Why delay the call to clk_get until the first suspend?
>
> Because the clock framework need not be ready at the _add time.
>
> > Also, this will always print an error during the first call to suspend.
>
> That actually depends on the initial state of the device and the
> assumption is that will be RPM_SUSPENDED, so _resume will be called
> first.
>
> I can remove the message, but it's there for backwards compatibility with
> the code this is intended to replace.
>
> > > + }
> > > +
> > > + if (ce->clock_active) {
> > I don't think clock_active is necessary, and the name is misleading.
>
> It's not strictly necessary and "active" means "being used for runtime PM".
>
> > Why not use if (ce->clk)?
>
> Because _that_ would be confusing?

Moreover, if the first clk_get() fails, this avoids repeating it
during every suspend/resume (because it most probably is going to fail too).

Thanks,
Rafael

2011-04-28 01:32:53

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

On Thursday, April 28, 2011, Rafael J. Wysocki wrote:
> On Thursday, April 28, 2011, Rafael J. Wysocki wrote:
> > On Thursday, April 28, 2011, Colin Cross wrote:
> > > On Wed, Apr 27, 2011 at 2:48 PM, Rafael J. Wysocki <[email protected]> wrote:
> > > > From: Rafael J. Wysocki <[email protected]>
...
> > > > + }
> > > > +
> > > > + if (ce->clock_active) {
> > > I don't think clock_active is necessary, and the name is misleading.
> >
> > It's not strictly necessary and "active" means "being used for runtime PM".
> >
> > > Why not use if (ce->clk)?
> >
> > Because _that_ would be confusing?
>
> Moreover, if the first clk_get() fails, this avoids repeating it
> during every suspend/resume (because it most probably is going to fail too).

Still, I could use !IS_ERR() to get the same thing.

Below is updated patch with that change.

Thanks,
Rafael

---
From: Rafael J. Wysocki <[email protected]>
Subject: PM / Runtime: Generic clock manipulation rountines for runtime PM (v4)

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 <[email protected]>
---
arch/arm/mach-shmobile/pm_runtime.c | 140 ------------
drivers/base/power/Makefile | 1
drivers/base/power/clock_ops.c | 420 ++++++++++++++++++++++++++++++++++++
include/linux/pm_runtime.h | 42 +++
kernel/power/Kconfig | 4
5 files changed, 476 insertions(+), 131 deletions(-)

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -0,0 +1,420 @@
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp.
+ *
+ * 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_clk_data {
+ struct list_head clock_list;
+ struct mutex lock;
+};
+
+struct pm_clock_entry {
+ struct list_head node;
+ char *con_id;
+ struct clk *clk;
+ unsigned int clock_enabled:1;
+};
+
+static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
+/**
+ * pm_runtime_clk_add - Start using a device clock for runtime PM.
+ * @dev: Device whose clock is going to be used for runtime PM.
+ * @con_id: Connection ID of the clock.
+ *
+ * Add the clock represented by @con_id to the list of clocks used for
+ * the runtime PM of @dev.
+ */
+int pm_runtime_clk_add(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return -EINVAL;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ dev_err(dev, "Not enough memory for clock entry.\n");
+ return -ENOMEM;
+ }
+
+ if (con_id) {
+ ce->con_id = kstrdup(con_id, GFP_KERNEL);
+ if (!ce->con_id) {
+ dev_err(dev,
+ "Not enough memory for clock conection ID.\n");
+ kfree(ce);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&prd->lock);
+ list_add_tail(&ce->node, &prd->clock_list);
+ mutex_unlock(&prd->lock);
+ return 0;
+}
+
+/**
+ * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
+ * @ce: Runtime PM clock entry to destroy.
+ *
+ * This routine must be called under the mutex protecting the runtime PM list
+ * of clocks corresponding the the @ce's device.
+ */
+static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+{
+ if (!ce)
+ return;
+
+ list_del(&ce->node);
+
+ if (ce->clk) {
+ if (ce->clock_enabled)
+ clk_disable(ce->clk);
+
+ if (!IS_ERR(ce->clk))
+ clk_put(ce->clk);
+ }
+
+ if (ce->con_id)
+ kfree(ce->con_id);
+
+ kfree(ce);
+}
+
+/**
+ * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
+ * @dev: Device whose clock should not be used for runtime PM any more.
+ * @con_id: Connection ID of the clock.
+ *
+ * Remove the clock represented by @con_id from the list of clocks used for
+ * the runtime PM of @dev.
+ */
+void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node)
+ if (!con_id && !ce->con_id) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ } else if (!con_id || !ce->con_id) {
+ continue;
+ } else if (!strcmp(con_id, ce->con_id)) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ }
+
+ mutex_unlock(&prd->lock);
+}
+
+/**
+ * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
+ * @dev: Device to initialize the list of runtime PM clocks for.
+ *
+ * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * make the @dev's power.subsys_data field point to it.
+ */
+int pm_runtime_clk_init(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd;
+
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+ if (!prd) {
+ dev_err(dev, "Not enough memory fo runtime PM data.\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&prd->clock_list);
+ mutex_init(&prd->lock);
+ dev->power.subsys_data = prd;
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
+ * @dev: Device to destroy the list of runtime PM clocks for.
+ *
+ * Clear the @dev's power.subsys_data field, remove the list of clock entries
+ * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * that object.
+ */
+void pm_runtime_clk_destroy(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce, *c;
+
+ if (!prd)
+ return;
+
+ dev->power.subsys_data = NULL;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
+ __pm_runtime_clk_remove(ce);
+
+ mutex_unlock(&prd->lock);
+
+ kfree(prd);
+}
+
+/**
+ * pm_runtime_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @con_id: Connection ID of the clock.
+ */
+static void pm_runtime_clk_acquire(struct device *dev,
+ struct pm_clock_entry *ce)
+{
+ ce->clk = clk_get(dev, ce->con_id);
+ if (!IS_ERR(ce->clk))
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+}
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_runtime_clk_suspend(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+ if (!ce->clk) {
+ dev_err(dev, "Clock is not ready for runtime PM\n");
+ pm_runtime_clk_acquire(dev, ce);
+ }
+
+ if (!IS_ERR(ce->clk)) {
+ clk_disable(ce->clk);
+ ce->clock_enabled = false;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_runtime_clk_resume(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (!ce->clk)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (!IS_ERR(ce->clk)) {
+ clk_enable(ce->clk);
+ ce->clock_enabled = true;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the pwr_domain member of that object is copied to the device's
+ * pwr_domain field and its con_ids member is used to populate the device's list
+ * of runtime PM clocks, depending on @action.
+ *
+ * If the device's pwr_domain field is already populated with a value different
+ * from the one stored in the struct pm_clk_notifier_block object, the function
+ * does nothing.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+ int error;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->pwr_domain)
+ break;
+
+ error = pm_runtime_clk_init(dev);
+ if (error)
+ break;
+
+ dev->pwr_domain = clknb->pwr_domain;
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ pm_runtime_clk_add(dev, con_id);
+ } else {
+ pm_runtime_clk_add(dev, NULL);
+ }
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->pwr_domain != clknb->pwr_domain)
+ break;
+
+ dev->pwr_domain = NULL;
+ pm_runtime_clk_destroy(dev);
+ break;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+/**
+ * enable_clock - Enable a device clock.
+ * @dev: Device whose clock is to be enabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void enable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced on.\n");
+ }
+}
+
+/**
+ * disable_clock - Disable a device clock.
+ * @dev: Device whose clock is to be disabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void disable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced off.\n");
+ }
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the con_ids member of that object is used to enable or disable
+ * the device's clocks, depending on @action.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ enable_clock(dev, con_id);
+ } else {
+ enable_clock(dev, NULL);
+ }
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ disable_clock(dev, con_id);
+ } else {
+ disable_clock(dev, NULL);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+/**
+ * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * @bus: Bus type to add the notifier to.
+ * @clknb: Notifier to be added to the given bus type.
+ *
+ * The nb member of @clknb is not expected to be initialized and its
+ * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * the remaining members of @clknb should be populated prior to calling this
+ * routine.
+ */
+void pm_runtime_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+ if (!bus || !clknb)
+ return;
+
+ clknb->nb.notifier_call = pm_runtime_clk_notify;
+ bus_register_notifier(bus, &clknb->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,46 @@ static inline void pm_runtime_dont_use_a
__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
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)
{
@@ -94,87 +30,29 @@ static int default_platform_runtime_idle

static struct dev_power_domain default_power_domain = {
.ops = {
- .runtime_suspend = default_platform_runtime_suspend,
- .runtime_resume = default_platform_runtime_resume,
+ .runtime_suspend = pm_runtime_clk_suspend,
+ .runtime_resume = pm_runtime_clk_resume,
.runtime_idle = default_platform_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
},
};

-static int platform_bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct pm_runtime_data *prd;
+#define DEFAULT_PWR_DOMAIN_PTR (&default_power_domain)

- dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
+#else

- 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;
-}
+#define DEFAULT_PWR_DOMAIN_PTR NULL

#endif /* CONFIG_PM_RUNTIME */

-static struct notifier_block platform_bus_notifier = {
- .notifier_call = platform_bus_notify
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+ .con_ids = { NULL, },
};

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;
}
core_initcall(sh_pm_runtime_init);

2011-04-28 19:36:22

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update x2][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v5)

From: Rafael J. Wysocki <[email protected]>

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 <[email protected]>
---

Hi,

I thought I'd do two tweaks more.

First, I decided to make pm_runtime_clk_suspend() and
pm_runtime_clk_resume() totally symmetrical, so I removed the message
that Colin didn't like.

Second, I noticed that I could simplify __pm_runtime_clk_remove()
slightly, by using IS_ERR_OR_NULL() in it.

Thanks,
Rafael

---
arch/arm/mach-shmobile/pm_runtime.c | 140 ------------
drivers/base/power/Makefile | 1
drivers/base/power/clock_ops.c | 417 ++++++++++++++++++++++++++++++++++++
include/linux/pm_runtime.h | 42 +++
kernel/power/Kconfig | 4
5 files changed, 473 insertions(+), 131 deletions(-)

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -0,0 +1,417 @@
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp.
+ *
+ * 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_clk_data {
+ struct list_head clock_list;
+ struct mutex lock;
+};
+
+struct pm_clock_entry {
+ struct list_head node;
+ char *con_id;
+ struct clk *clk;
+ unsigned int clock_enabled:1;
+};
+
+static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
+/**
+ * pm_runtime_clk_add - Start using a device clock for runtime PM.
+ * @dev: Device whose clock is going to be used for runtime PM.
+ * @con_id: Connection ID of the clock.
+ *
+ * Add the clock represented by @con_id to the list of clocks used for
+ * the runtime PM of @dev.
+ */
+int pm_runtime_clk_add(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return -EINVAL;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ dev_err(dev, "Not enough memory for clock entry.\n");
+ return -ENOMEM;
+ }
+
+ if (con_id) {
+ ce->con_id = kstrdup(con_id, GFP_KERNEL);
+ if (!ce->con_id) {
+ dev_err(dev,
+ "Not enough memory for clock conection ID.\n");
+ kfree(ce);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&prd->lock);
+ list_add_tail(&ce->node, &prd->clock_list);
+ mutex_unlock(&prd->lock);
+ return 0;
+}
+
+/**
+ * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
+ * @ce: Runtime PM clock entry to destroy.
+ *
+ * This routine must be called under the mutex protecting the runtime PM list
+ * of clocks corresponding the the @ce's device.
+ */
+static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+{
+ if (!ce)
+ return;
+
+ list_del(&ce->node);
+
+ if (!IS_ERR_OR_NULL(ce->clk)) {
+ if (ce->clock_enabled)
+ clk_disable(ce->clk);
+
+ clk_put(ce->clk);
+ }
+
+ if (ce->con_id)
+ kfree(ce->con_id);
+
+ kfree(ce);
+}
+
+/**
+ * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
+ * @dev: Device whose clock should not be used for runtime PM any more.
+ * @con_id: Connection ID of the clock.
+ *
+ * Remove the clock represented by @con_id from the list of clocks used for
+ * the runtime PM of @dev.
+ */
+void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node)
+ if (!con_id && !ce->con_id) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ } else if (!con_id || !ce->con_id) {
+ continue;
+ } else if (!strcmp(con_id, ce->con_id)) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ }
+
+ mutex_unlock(&prd->lock);
+}
+
+/**
+ * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
+ * @dev: Device to initialize the list of runtime PM clocks for.
+ *
+ * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * make the @dev's power.subsys_data field point to it.
+ */
+int pm_runtime_clk_init(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd;
+
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+ if (!prd) {
+ dev_err(dev, "Not enough memory fo runtime PM data.\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&prd->clock_list);
+ mutex_init(&prd->lock);
+ dev->power.subsys_data = prd;
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
+ * @dev: Device to destroy the list of runtime PM clocks for.
+ *
+ * Clear the @dev's power.subsys_data field, remove the list of clock entries
+ * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * that object.
+ */
+void pm_runtime_clk_destroy(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce, *c;
+
+ if (!prd)
+ return;
+
+ dev->power.subsys_data = NULL;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
+ __pm_runtime_clk_remove(ce);
+
+ mutex_unlock(&prd->lock);
+
+ kfree(prd);
+}
+
+/**
+ * pm_runtime_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @con_id: Connection ID of the clock.
+ */
+static void pm_runtime_clk_acquire(struct device *dev,
+ struct pm_clock_entry *ce)
+{
+ ce->clk = clk_get(dev, ce->con_id);
+ if (!IS_ERR(ce->clk))
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+}
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_runtime_clk_suspend(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+ if (!ce->clk)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (!IS_ERR(ce->clk)) {
+ clk_disable(ce->clk);
+ ce->clock_enabled = false;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_runtime_clk_resume(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (!ce->clk)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (!IS_ERR(ce->clk)) {
+ clk_enable(ce->clk);
+ ce->clock_enabled = true;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the pwr_domain member of that object is copied to the device's
+ * pwr_domain field and its con_ids member is used to populate the device's list
+ * of runtime PM clocks, depending on @action.
+ *
+ * If the device's pwr_domain field is already populated with a value different
+ * from the one stored in the struct pm_clk_notifier_block object, the function
+ * does nothing.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+ int error;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->pwr_domain)
+ break;
+
+ error = pm_runtime_clk_init(dev);
+ if (error)
+ break;
+
+ dev->pwr_domain = clknb->pwr_domain;
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ pm_runtime_clk_add(dev, con_id);
+ } else {
+ pm_runtime_clk_add(dev, NULL);
+ }
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->pwr_domain != clknb->pwr_domain)
+ break;
+
+ dev->pwr_domain = NULL;
+ pm_runtime_clk_destroy(dev);
+ break;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+/**
+ * enable_clock - Enable a device clock.
+ * @dev: Device whose clock is to be enabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void enable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced on.\n");
+ }
+}
+
+/**
+ * disable_clock - Disable a device clock.
+ * @dev: Device whose clock is to be disabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void disable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced off.\n");
+ }
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the con_ids member of that object is used to enable or disable
+ * the device's clocks, depending on @action.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ enable_clock(dev, con_id);
+ } else {
+ enable_clock(dev, NULL);
+ }
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ disable_clock(dev, con_id);
+ } else {
+ disable_clock(dev, NULL);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+/**
+ * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * @bus: Bus type to add the notifier to.
+ * @clknb: Notifier to be added to the given bus type.
+ *
+ * The nb member of @clknb is not expected to be initialized and its
+ * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * the remaining members of @clknb should be populated prior to calling this
+ * routine.
+ */
+void pm_runtime_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+ if (!bus || !clknb)
+ return;
+
+ clknb->nb.notifier_call = pm_runtime_clk_notify;
+ bus_register_notifier(bus, &clknb->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,46 @@ static inline void pm_runtime_dont_use_a
__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
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)
{
@@ -94,87 +30,29 @@ static int default_platform_runtime_idle

static struct dev_power_domain default_power_domain = {
.ops = {
- .runtime_suspend = default_platform_runtime_suspend,
- .runtime_resume = default_platform_runtime_resume,
+ .runtime_suspend = pm_runtime_clk_suspend,
+ .runtime_resume = pm_runtime_clk_resume,
.runtime_idle = default_platform_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
},
};

-static int platform_bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct pm_runtime_data *prd;
+#define DEFAULT_PWR_DOMAIN_PTR (&default_power_domain)

- dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
+#else

- 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;
-}
+#define DEFAULT_PWR_DOMAIN_PTR NULL

#endif /* CONFIG_PM_RUNTIME */

-static struct notifier_block platform_bus_notifier = {
- .notifier_call = platform_bus_notify
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+ .con_ids = { NULL, },
};

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;
}
core_initcall(sh_pm_runtime_init);

2011-04-29 19:35:29

by Stephen Boyd

[permalink] [raw]
Subject: Re: [Update x2][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v5)

On 04/28/2011 12:36 PM, Rafael J. Wysocki wrote:
> +/**
> + * pm_runtime_clk_add - Start using a device clock for runtime PM.
> + * @dev: Device whose clock is going to be used for runtime PM.
> + * @con_id: Connection ID of the clock.
> + *
> + * Add the clock represented by @con_id to the list of clocks used for
> + * the runtime PM of @dev.
> + */
> +int pm_runtime_clk_add(struct device *dev, const char *con_id)
> +{
> + struct pm_runtime_clk_data *prd = __to_prd(dev);
> + struct pm_clock_entry *ce;
> +
> + if (!prd)
> + return -EINVAL;
> +
> + ce = kzalloc(sizeof(*ce), GFP_KERNEL);
> + if (!ce) {
> + dev_err(dev, "Not enough memory for clock entry.\n");
> + return -ENOMEM;
> + }
> +
> + if (con_id) {
> + ce->con_id = kstrdup(con_id, GFP_KERNEL);
> + if (!ce->con_id) {
> + dev_err(dev,
> + "Not enough memory for clock conection ID.\n");

Missing an 'n' in connection.

> + kfree(ce);
> + return -ENOMEM;
> + }
> + }
> +
> + mutex_lock(&prd->lock);
> + list_add_tail(&ce->node, &prd->clock_list);
> + mutex_unlock(&prd->lock);
> + return 0;
> +}
> +
> +/**
> + * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
> + * @ce: Runtime PM clock entry to destroy.
> + *
> + * This routine must be called under the mutex protecting the runtime PM list
> + * of clocks corresponding the the @ce's device.
> + */
> +static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
> +{
> + if (!ce)
> + return;
> +
> + list_del(&ce->node);
> +
> + if (!IS_ERR_OR_NULL(ce->clk)) {

NULL is a valid clock so you'll need to figure out some way to handle
that throughout this code.

> + if (ce->clock_enabled)
> + clk_disable(ce->clk);
> +
> + clk_put(ce->clk);
> + }
> +
> + if (ce->con_id)
> + kfree(ce->con_id);
> +
> + kfree(ce);
> +}
> +

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2011-04-29 20:29:27

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update x2][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v5)

On Friday, April 29, 2011, Stephen Boyd wrote:
> On 04/28/2011 12:36 PM, Rafael J. Wysocki wrote:
> > +/**
> > + * pm_runtime_clk_add - Start using a device clock for runtime PM.
> > + * @dev: Device whose clock is going to be used for runtime PM.
> > + * @con_id: Connection ID of the clock.
> > + *
> > + * Add the clock represented by @con_id to the list of clocks used for
> > + * the runtime PM of @dev.
> > + */
> > +int pm_runtime_clk_add(struct device *dev, const char *con_id)
> > +{
> > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > + struct pm_clock_entry *ce;
> > +
> > + if (!prd)
> > + return -EINVAL;
> > +
> > + ce = kzalloc(sizeof(*ce), GFP_KERNEL);
> > + if (!ce) {
> > + dev_err(dev, "Not enough memory for clock entry.\n");
> > + return -ENOMEM;
> > + }
> > +
> > + if (con_id) {
> > + ce->con_id = kstrdup(con_id, GFP_KERNEL);
> > + if (!ce->con_id) {
> > + dev_err(dev,
> > + "Not enough memory for clock conection ID.\n");
>
> Missing an 'n' in connection.

Right, thanks!

> > + kfree(ce);
> > + return -ENOMEM;
> > + }
> > + }
> > +
> > + mutex_lock(&prd->lock);
> > + list_add_tail(&ce->node, &prd->clock_list);
> > + mutex_unlock(&prd->lock);
> > + return 0;
> > +}
> > +
> > +/**
> > + * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
> > + * @ce: Runtime PM clock entry to destroy.
> > + *
> > + * This routine must be called under the mutex protecting the runtime PM list
> > + * of clocks corresponding the the @ce's device.
> > + */
> > +static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
> > +{
> > + if (!ce)
> > + return;
> > +
> > + list_del(&ce->node);
> > +
> > + if (!IS_ERR_OR_NULL(ce->clk)) {
>
> NULL is a valid clock so you'll need to figure out some way to handle
> that throughout this code.

Well, I didn't know that, thanks! I'll need to add the "active" flag
back, then, after all. In addition to that, I'll need to distinguish between
the "clk == NULL because it's not initialized" and "clk == NULL becuase it's
a valid clock" cases.

I guess I'll use combinations of two flags.

Thanks,
Rafael

2011-04-29 20:50:44

by Grant Likely

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

On Thu, Apr 28, 2011 at 02:58:34AM +0200, Rafael J. Wysocki wrote:
> On Thursday, April 28, 2011, Colin Cross wrote:
> > On Wed, Apr 27, 2011 at 2:48 PM, Rafael J. Wysocki <[email protected]> wrote:
> > > +void pm_runtime_clk_remove(struct device *dev, const char *con_id)
> > > +{
> > > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > > + struct pm_clock_entry *ce;
> > > +
> > > + if (!prd)
> > > + return;
> > > +
> > > + mutex_lock(&prd->lock);
> > > +
> > > + list_for_each_entry(ce, &prd->clock_list, node)
> > Braces
>
> No, this is correct as is.

The code is correct, but Colin's comment is valid. Braces do make it
easier for a reader to properly interpret the scope of large multiline
blocks, even if it does resolve to a single statement.

g.

>
> > > + if (!con_id && !ce->con_id) {
> > > + __pm_runtime_clk_remove(ce);
> > > + break;
> > > + } else if (!con_id || !ce->con_id) {
> > > + continue;
> > > + } else if (!strcmp(con_id, ce->con_id)) {
> > > + __pm_runtime_clk_remove(ce);
> > > + break;
> > > + }
> > > +
> > > + mutex_unlock(&prd->lock);
> > > +}

2011-04-29 21:07:30

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v3)

On Friday, April 29, 2011, Grant Likely wrote:
> On Thu, Apr 28, 2011 at 02:58:34AM +0200, Rafael J. Wysocki wrote:
> > On Thursday, April 28, 2011, Colin Cross wrote:
> > > On Wed, Apr 27, 2011 at 2:48 PM, Rafael J. Wysocki <[email protected]> wrote:
> > > > +void pm_runtime_clk_remove(struct device *dev, const char *con_id)
> > > > +{
> > > > + struct pm_runtime_clk_data *prd = __to_prd(dev);
> > > > + struct pm_clock_entry *ce;
> > > > +
> > > > + if (!prd)
> > > > + return;
> > > > +
> > > > + mutex_lock(&prd->lock);
> > > > +
> > > > + list_for_each_entry(ce, &prd->clock_list, node)
> > > Braces
> >
> > No, this is correct as is.
>
> The code is correct, but Colin's comment is valid. Braces do make it
> easier for a reader to properly interpret the scope of large multiline
> blocks, even if it does resolve to a single statement.

OK, I'll add the braces.

Thanks,
Rafael

2011-04-29 22:04:00

by Rafael J. Wysocki

[permalink] [raw]
Subject: [Update x3][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v6)

From: Rafael J. Wysocki <[email protected]>

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 <[email protected]>
---

Hi,

This version of the patch takes the fact that NULL may be a valid clock into
account and fixes some spelling and such.

Thanks,
Rafael

---
arch/arm/mach-shmobile/pm_runtime.c | 140 -----------
drivers/base/power/Makefile | 1
drivers/base/power/clock_ops.c | 430 ++++++++++++++++++++++++++++++++++++
include/linux/pm_runtime.h | 42 +++
kernel/power/Kconfig | 4
5 files changed, 486 insertions(+), 131 deletions(-)

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -0,0 +1,430 @@
+/*
+ * drivers/base/power/clock_ops.c - Generic clock manipulation PM callbacks
+ *
+ * Copyright (c) 2011 Rafael J. Wysocki <[email protected]>, Renesas Electronics Corp.
+ *
+ * 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_clk_data {
+ struct list_head clock_list;
+ struct mutex lock;
+};
+
+enum pce_status {
+ PCE_STATUS_NONE = 0,
+ PCE_STATUS_ACQUIRED,
+ PCE_STATUS_ENABLED,
+ PCE_STATUS_ERROR,
+};
+
+struct pm_clock_entry {
+ struct list_head node;
+ char *con_id;
+ struct clk *clk;
+ enum pce_status status;
+};
+
+static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+{
+ return dev ? dev->power.subsys_data : NULL;
+}
+
+/**
+ * pm_runtime_clk_add - Start using a device clock for runtime PM.
+ * @dev: Device whose clock is going to be used for runtime PM.
+ * @con_id: Connection ID of the clock.
+ *
+ * Add the clock represented by @con_id to the list of clocks used for
+ * the runtime PM of @dev.
+ */
+int pm_runtime_clk_add(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return -EINVAL;
+
+ ce = kzalloc(sizeof(*ce), GFP_KERNEL);
+ if (!ce) {
+ dev_err(dev, "Not enough memory for clock entry.\n");
+ return -ENOMEM;
+ }
+
+ if (con_id) {
+ ce->con_id = kstrdup(con_id, GFP_KERNEL);
+ if (!ce->con_id) {
+ dev_err(dev,
+ "Not enough memory for clock connection ID.\n");
+ kfree(ce);
+ return -ENOMEM;
+ }
+ }
+
+ mutex_lock(&prd->lock);
+ list_add_tail(&ce->node, &prd->clock_list);
+ mutex_unlock(&prd->lock);
+ return 0;
+}
+
+/**
+ * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
+ * @ce: Runtime PM clock entry to destroy.
+ *
+ * This routine must be called under the mutex protecting the runtime PM list
+ * of clocks corresponding the the @ce's device.
+ */
+static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+{
+ if (!ce)
+ return;
+
+ list_del(&ce->node);
+
+ if (ce->status < PCE_STATUS_ERROR) {
+ if (ce->status == PCE_STATUS_ENABLED)
+ clk_disable(ce->clk);
+
+ if (ce->status >= PCE_STATUS_ACQUIRED)
+ clk_put(ce->clk);
+ }
+
+ if (ce->con_id)
+ kfree(ce->con_id);
+
+ kfree(ce);
+}
+
+/**
+ * pm_runtime_clk_remove - Stop using a device clock for runtime PM.
+ * @dev: Device whose clock should not be used for runtime PM any more.
+ * @con_id: Connection ID of the clock.
+ *
+ * Remove the clock represented by @con_id from the list of clocks used for
+ * the runtime PM of @dev.
+ */
+void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ if (!prd)
+ return;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (!con_id && !ce->con_id) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ } else if (!con_id || !ce->con_id) {
+ continue;
+ } else if (!strcmp(con_id, ce->con_id)) {
+ __pm_runtime_clk_remove(ce);
+ break;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+}
+
+/**
+ * pm_runtime_clk_init - Initialize a device's list of runtime PM clocks.
+ * @dev: Device to initialize the list of runtime PM clocks for.
+ *
+ * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * make the @dev's power.subsys_data field point to it.
+ */
+int pm_runtime_clk_init(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd;
+
+ prd = kzalloc(sizeof(*prd), GFP_KERNEL);
+ if (!prd) {
+ dev_err(dev, "Not enough memory fo runtime PM data.\n");
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&prd->clock_list);
+ mutex_init(&prd->lock);
+ dev->power.subsys_data = prd;
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_destroy - Destroy a device's list of runtime PM clocks.
+ * @dev: Device to destroy the list of runtime PM clocks for.
+ *
+ * Clear the @dev's power.subsys_data field, remove the list of clock entries
+ * from the struct pm_runtime_clk_data object pointed to by it before and free
+ * that object.
+ */
+void pm_runtime_clk_destroy(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce, *c;
+
+ if (!prd)
+ return;
+
+ dev->power.subsys_data = NULL;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
+ __pm_runtime_clk_remove(ce);
+
+ mutex_unlock(&prd->lock);
+
+ kfree(prd);
+}
+
+/**
+ * pm_runtime_clk_acquire - Acquire a device clock.
+ * @dev: Device whose clock is to be acquired.
+ * @con_id: Connection ID of the clock.
+ */
+static void pm_runtime_clk_acquire(struct device *dev,
+ struct pm_clock_entry *ce)
+{
+ ce->clk = clk_get(dev, ce->con_id);
+ if (IS_ERR(ce->clk)) {
+ ce->status = PCE_STATUS_ERROR;
+ } else {
+ ce->status = PCE_STATUS_ACQUIRED;
+ dev_dbg(dev, "Clock %s managed by runtime PM.\n", ce->con_id);
+ }
+}
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * @dev: Device to disable the clocks for.
+ */
+int pm_runtime_clk_suspend(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+ if (ce->status == PCE_STATUS_NONE)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (ce->status < PCE_STATUS_ERROR) {
+ clk_disable(ce->clk);
+ ce->status = PCE_STATUS_ACQUIRED;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * @dev: Device to enable the clocks for.
+ */
+int pm_runtime_clk_resume(struct device *dev)
+{
+ struct pm_runtime_clk_data *prd = __to_prd(dev);
+ struct pm_clock_entry *ce;
+
+ dev_dbg(dev, "%s()\n", __func__);
+
+ if (!prd)
+ return 0;
+
+ mutex_lock(&prd->lock);
+
+ list_for_each_entry(ce, &prd->clock_list, node) {
+ if (ce->status == PCE_STATUS_NONE)
+ pm_runtime_clk_acquire(dev, ce);
+
+ if (ce->status < PCE_STATUS_ERROR) {
+ clk_enable(ce->clk);
+ ce->status = PCE_STATUS_ENABLED;
+ }
+ }
+
+ mutex_unlock(&prd->lock);
+
+ return 0;
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the pwr_domain member of that object is copied to the device's
+ * pwr_domain field and its con_ids member is used to populate the device's list
+ * of runtime PM clocks, depending on @action.
+ *
+ * If the device's pwr_domain field is already populated with a value different
+ * from the one stored in the struct pm_clk_notifier_block object, the function
+ * does nothing.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+ char *con_id;
+ int error;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (dev->pwr_domain)
+ break;
+
+ error = pm_runtime_clk_init(dev);
+ if (error)
+ break;
+
+ dev->pwr_domain = clknb->pwr_domain;
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ pm_runtime_clk_add(dev, con_id);
+ } else {
+ pm_runtime_clk_add(dev, NULL);
+ }
+
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (dev->pwr_domain != clknb->pwr_domain)
+ break;
+
+ dev->pwr_domain = NULL;
+ pm_runtime_clk_destroy(dev);
+ break;
+ }
+
+ return 0;
+}
+
+#else /* !CONFIG_PM_RUNTIME */
+
+/**
+ * enable_clock - Enable a device clock.
+ * @dev: Device whose clock is to be enabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void enable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_enable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced on.\n");
+ }
+}
+
+/**
+ * disable_clock - Disable a device clock.
+ * @dev: Device whose clock is to be disabled.
+ * @con_id: Connection ID of the clock.
+ */
+static void disable_clock(struct device *dev, const char *con_id)
+{
+ struct clk *clk;
+
+ clk = clk_get(dev, con_id);
+ if (!IS_ERR(clk)) {
+ clk_disable(clk);
+ clk_put(clk);
+ dev_info(dev, "Runtime PM disabled, clock forced off.\n");
+ }
+}
+
+/**
+ * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * @nb: Notifier block object this function is a member of.
+ * @action: Operation being carried out by the caller.
+ * @data: Device the routine is being run for.
+ *
+ * For this function to work, @nb must be a member of an object of type
+ * struct pm_clk_notifier_block containing all of the requisite data.
+ * Specifically, the con_ids member of that object is used to enable or disable
+ * the device's clocks, depending on @action.
+ */
+static int pm_runtime_clk_notify(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct pm_clk_notifier_block *clknb;
+ struct device *dev = data;
+
+ dev_dbg(dev, "%s() %ld\n", __func__, action);
+
+ clknb = container_of(nb, struct pm_clk_notifier_block, nb);
+
+ switch (action) {
+ case BUS_NOTIFY_ADD_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ enable_clock(dev, con_id);
+ } else {
+ enable_clock(dev, NULL);
+ }
+ break;
+ case BUS_NOTIFY_DEL_DEVICE:
+ if (clknb->con_ids[0]) {
+ for (con_id = clknb->con_ids[0]; *con_id; con_id++)
+ disable_clock(dev, con_id);
+ } else {
+ disable_clock(dev, NULL);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+#endif /* !CONFIG_PM_RUNTIME */
+
+/**
+ * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * @bus: Bus type to add the notifier to.
+ * @clknb: Notifier to be added to the given bus type.
+ *
+ * The nb member of @clknb is not expected to be initialized and its
+ * notifier_call member will be replaced with pm_runtime_clk_notify(). However,
+ * the remaining members of @clknb should be populated prior to calling this
+ * routine.
+ */
+void pm_runtime_clk_add_notifier(struct bus_type *bus,
+ struct pm_clk_notifier_block *clknb)
+{
+ if (!bus || !clknb)
+ return;
+
+ clknb->nb.notifier_call = pm_runtime_clk_notify;
+ bus_register_notifier(bus, &clknb->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,46 @@ static inline void pm_runtime_dont_use_a
__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
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)
{
@@ -94,87 +30,29 @@ static int default_platform_runtime_idle

static struct dev_power_domain default_power_domain = {
.ops = {
- .runtime_suspend = default_platform_runtime_suspend,
- .runtime_resume = default_platform_runtime_resume,
+ .runtime_suspend = pm_runtime_clk_suspend,
+ .runtime_resume = pm_runtime_clk_resume,
.runtime_idle = default_platform_runtime_idle,
USE_PLATFORM_PM_SLEEP_OPS
},
};

-static int platform_bus_notify(struct notifier_block *nb,
- unsigned long action, void *data)
-{
- struct device *dev = data;
- struct pm_runtime_data *prd;
+#define DEFAULT_PWR_DOMAIN_PTR (&default_power_domain)

- dev_dbg(dev, "platform_bus_notify() %ld !\n", action);
+#else

- 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;
-}
+#define DEFAULT_PWR_DOMAIN_PTR NULL

#endif /* CONFIG_PM_RUNTIME */

-static struct notifier_block platform_bus_notifier = {
- .notifier_call = platform_bus_notify
+static struct pm_clk_notifier_block platform_bus_notifier = {
+ .pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+ .con_ids = { NULL, },
};

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;
}
core_initcall(sh_pm_runtime_init);

2011-05-03 17:01:16

by Stephen Boyd

[permalink] [raw]
Subject: Re: [Update x3][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v6)

On 04/29/2011 03:04 PM, Rafael J. Wysocki wrote:
> +
> +/**
> + * enable_clock - Enable a device clock.
> + * @dev: Device whose clock is to be enabled.
> + * @con_id: Connection ID of the clock.
> + */
> +static void enable_clock(struct device *dev, const char *con_id)
> +{
> + struct clk *clk;
> +
> + clk = clk_get(dev, con_id);
> + if (!IS_ERR(clk)) {
> + clk_enable(clk);
> + clk_put(clk);
> + dev_info(dev, "Runtime PM disabled, clock forced on.\n");
> + }
> +}

This doesn't make much sense to me. You're getting a clock and then
enabling it and then putting the clock? How can you be so sure that
clk_put() won't one day do some kind of lower power mode on the clock
when clk_put() is called on it? I don't think anyone does anything
today, but I don't think its safe to assume that clk_put() won't try to
forcibly shut off the clock once all clk_get() callers have clk_put().

Perhaps we should document the meaning of clk_enable() followed by
clk_put() somewhere instead?

> +
> +/**
> + * disable_clock - Disable a device clock.
> + * @dev: Device whose clock is to be disabled.
> + * @con_id: Connection ID of the clock.
> + */
> +static void disable_clock(struct device *dev, const char *con_id)
> +{
> + struct clk *clk;
> +
> + clk = clk_get(dev, con_id);
> + if (!IS_ERR(clk)) {
> + clk_disable(clk);
> + clk_put(clk);
> + dev_info(dev, "Runtime PM disabled, clock forced off.\n");
> + }
> +}

This might not be as bad, but it looks like a similar problem.

> -
> -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");
> - }

Ah ok I see that it's coming from here.

BTW, whatever is in linux-next is failing to compile:

drivers/base/power/clock_ops.c:391: error: 'con_id' undeclared (first
use in this function)
drivers/base/power/clock_ops.c:391: error: (Each undeclared identifier
is reported only once
drivers/base/power/clock_ops.c:391: error: for each function it appears in.)

--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.

2011-05-03 17:38:13

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [Update x3][PATCH 7/9] PM / Runtime: Generic clock manipulation rountines for runtime PM (v6)

On Tuesday, May 03, 2011, Stephen Boyd wrote:
> On 04/29/2011 03:04 PM, Rafael J. Wysocki wrote:
> > +
> > +/**
> > + * enable_clock - Enable a device clock.
> > + * @dev: Device whose clock is to be enabled.
> > + * @con_id: Connection ID of the clock.
> > + */
> > +static void enable_clock(struct device *dev, const char *con_id)
> > +{
> > + struct clk *clk;
> > +
> > + clk = clk_get(dev, con_id);
> > + if (!IS_ERR(clk)) {
> > + clk_enable(clk);
> > + clk_put(clk);
> > + dev_info(dev, "Runtime PM disabled, clock forced on.\n");
> > + }
> > +}
>
> This doesn't make much sense to me. You're getting a clock and then
> enabling it and then putting the clock? How can you be so sure that
> clk_put() won't one day do some kind of lower power mode on the clock
> when clk_put() is called on it? I don't think anyone does anything
> today, but I don't think its safe to assume that clk_put() won't try to
> forcibly shut off the clock once all clk_get() callers have clk_put().
>
> Perhaps we should document the meaning of clk_enable() followed by
> clk_put() somewhere instead?

That's coming from some existing ARM shmobile code.

There are two alternatives, one not to do any clock management at all
when CONFIG_PM_RUNTIME is unset, the other to remove the "put" from
enable_clock(), but then disable_clock() would need to do "put" twice.
I didn't think any of them was better than the current code.

> > +
> > +/**
> > + * disable_clock - Disable a device clock.
> > + * @dev: Device whose clock is to be disabled.
> > + * @con_id: Connection ID of the clock.
> > + */
> > +static void disable_clock(struct device *dev, const char *con_id)
> > +{
> > + struct clk *clk;
> > +
> > + clk = clk_get(dev, con_id);
> > + if (!IS_ERR(clk)) {
> > + clk_disable(clk);
> > + clk_put(clk);
> > + dev_info(dev, "Runtime PM disabled, clock forced off.\n");
> > + }
> > +}
>
> This might not be as bad, but it looks like a similar problem.
>
> > -
> > -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");
> > - }
>
> Ah ok I see that it's coming from here.

Yes, it is.

> BTW, whatever is in linux-next is failing to compile:
>
> drivers/base/power/clock_ops.c:391: error: 'con_id' undeclared (first
> use in this function)
> drivers/base/power/clock_ops.c:391: error: (Each undeclared identifier
> is reported only once
> drivers/base/power/clock_ops.c:391: error: for each function it appears in.)

I guess that's with CONFIG_PM_RUNTIME unset, right?

Sorry about that, the appended patch should fix the issue.

Thanks,
Rafael


---
From: Rafael J. Wysocki <[email protected]>
Subject: PM: Fix build issue in clock_ops.c for CONFIG_PM_RUNTIME unset

Fix a build issue in drivers/base/power/clock_ops.c occuring when
CONFIG_PM_RUNTIME is not set.

Signed-off-by: Rafael J. Wysocki <[email protected]>
---
drivers/base/power/clock_ops.c | 1 +
1 file changed, 1 insertion(+)

Index: linux-2.6/drivers/base/power/clock_ops.c
===================================================================
--- linux-2.6.orig/drivers/base/power/clock_ops.c
+++ linux-2.6/drivers/base/power/clock_ops.c
@@ -380,6 +380,7 @@ static int pm_runtime_clk_notify(struct
{
struct pm_clk_notifier_block *clknb;
struct device *dev = data;
+ char *con_id;

dev_dbg(dev, "%s() %ld\n", __func__, action);

2011-05-16 10:16:30

by Kevin Hilman

[permalink] [raw]
Subject: Re: [PATCH 8/9] OMAP1 / PM: Use generic clock manipulation routines for runtime PM

"Rafael J. Wysocki" <[email protected]> writes:

> From: Rafael J. Wysocki <[email protected]>
>
> Convert OMAP1 to using the new generic clock manipulation routines
> and a device power domain for runtime PM instead of overriding the
> platform bus type's runtime PM callbacks. This allows us to simplify
> OMAP1-specific code and to share some code with other platforms
> (shmobile in particular).
>
> Signed-off-by: Rafael J. Wysocki <[email protected]>

Acked-by: Kevin Hilman <[email protected]>

2011-05-16 18:25:58

by Rafael J. Wysocki

[permalink] [raw]
Subject: Re: [PATCH 8/9] OMAP1 / PM: Use generic clock manipulation routines for runtime PM

On Monday, May 16, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <[email protected]> writes:
>
> > From: Rafael J. Wysocki <[email protected]>
> >
> > Convert OMAP1 to using the new generic clock manipulation routines
> > and a device power domain for runtime PM instead of overriding the
> > platform bus type's runtime PM callbacks. This allows us to simplify
> > OMAP1-specific code and to share some code with other platforms
> > (shmobile in particular).
> >
> > Signed-off-by: Rafael J. Wysocki <[email protected]>
>
> Acked-by: Kevin Hilman <[email protected]>

Thanks!