All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-11 20:23 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman
  Cc: Linux PM mailing list, LKML, linux-sh

Hi,

This is the 4th update of the patchset adding support for generic I/O PM
domains.  The patches have been reworked quite a bit to take feedback into
account, but I left the Greg's ACK in [4/8] in the hope it still applies
(Greg, please let me know in case it doesn't :-)).

The model here is that a bunch of devices share a common power resource
that can be turned on and off by software.  In addition to that, there
are means to start and stop the activity of each device, for example
by manipulating their clocks.  Moreover, there may be hierarchy of
such things, for example power resource A may be necessary for devices
a, b, c, which don't rely on any other power resources, and for devices
x, y, z that also rely on power resource X.  In that case there one PM
domain object representing devices a, b, c and power resource A, and 
another PM domain object will represent devices x, y, z with power
resource X, plus the first object will be the second one's parent.

Note to Kevin: I know you'd like each PM domain to be able to go into several
different states, but the situation will always be that in some of those
states the devices' registers will remain intact, while in the rest of those
states they will be reset.  Say, there are states 1, 2, 3, 4 and states
1-3 preserve device registers.  Then it is not necessary to save device
registers for "domain" states 1-3 and it only is necessary to save them
when going to state 4.  In that case, .power_off() may map to the "go to
state 4" operation (and analogously .power_on()), while the rest may be
done by .stop_device() and .start_device().  IOW, .power_is_off = true
means "the devices' registers have to be restored", so it need not map to
any particular physical state of a (hardware) power domain.

Note to Magnus and Paul: I didn't use a global lock as suggested, because
I think it may lead to completely unnecessary congestion in situations in
which there are no hierarchies of PM domains.  It is quite easy to show that
the code doesn't deadlock, because (1) no more than 2 locks are held by the
same thread at a time (parent lock and child lock) and (2) they are always
acquired in the same order (parent before the child).

Overall, I think I've taken all of the important dependencies into
consideration, but if you spot something suspicious, please let me know. :-)
Wakeup is not covered at this point, because it's not necessary for the
SH7372's A4LC power domain that's the first user of the new code, but it
is quite clear how add the support for it.  Also, for more complicated
cases it is necessary to take QoS requirements (latencies) into account,
which is in the works (kind of).

[1/8] - Update documentation to reflect the fact that struct dev_power_domain
        callbacks take precedence over subsystem PM callbacks.

[2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
        fact that those objects need not correspond to hardware power domains
        directly.

[3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME

[4/8] - Introduce runtime PM support for generic I/O PM domains.

[5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
        (that's necessary for the next patches).

[6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME

[7/8] - Add system-wide PM support for generic I/O PM domains.

[8/8] - Use the new code to represent the SH7372's A4MP power domain.

The patchset has been tested on SH7372 Mackerel board and appears to work
correctly.

I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
which people may possibly use the naming that's going to change in their new
code.  If you agree with that, please let me know, I'll need some serious
ACKs below that patch if it's to be pushed for 3.0. ;-)

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-11 20:23 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman
  Cc: Alan Stern, LKML, Linux PM mailing list, linux-sh

Hi,

This is the 4th update of the patchset adding support for generic I/O PM
domains.  The patches have been reworked quite a bit to take feedback into
account, but I left the Greg's ACK in [4/8] in the hope it still applies
(Greg, please let me know in case it doesn't :-)).

The model here is that a bunch of devices share a common power resource
that can be turned on and off by software.  In addition to that, there
are means to start and stop the activity of each device, for example
by manipulating their clocks.  Moreover, there may be hierarchy of
such things, for example power resource A may be necessary for devices
a, b, c, which don't rely on any other power resources, and for devices
x, y, z that also rely on power resource X.  In that case there one PM
domain object representing devices a, b, c and power resource A, and 
another PM domain object will represent devices x, y, z with power
resource X, plus the first object will be the second one's parent.

Note to Kevin: I know you'd like each PM domain to be able to go into several
different states, but the situation will always be that in some of those
states the devices' registers will remain intact, while in the rest of those
states they will be reset.  Say, there are states 1, 2, 3, 4 and states
1-3 preserve device registers.  Then it is not necessary to save device
registers for "domain" states 1-3 and it only is necessary to save them
when going to state 4.  In that case, .power_off() may map to the "go to
state 4" operation (and analogously .power_on()), while the rest may be
done by .stop_device() and .start_device().  IOW, .power_is_off == true
means "the devices' registers have to be restored", so it need not map to
any particular physical state of a (hardware) power domain.

Note to Magnus and Paul: I didn't use a global lock as suggested, because
I think it may lead to completely unnecessary congestion in situations in
which there are no hierarchies of PM domains.  It is quite easy to show that
the code doesn't deadlock, because (1) no more than 2 locks are held by the
same thread at a time (parent lock and child lock) and (2) they are always
acquired in the same order (parent before the child).

Overall, I think I've taken all of the important dependencies into
consideration, but if you spot something suspicious, please let me know. :-)
Wakeup is not covered at this point, because it's not necessary for the
SH7372's A4LC power domain that's the first user of the new code, but it
is quite clear how add the support for it.  Also, for more complicated
cases it is necessary to take QoS requirements (latencies) into account,
which is in the works (kind of).

[1/8] - Update documentation to reflect the fact that struct dev_power_domain
        callbacks take precedence over subsystem PM callbacks.

[2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
        fact that those objects need not correspond to hardware power domains
        directly.

[3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME

[4/8] - Introduce runtime PM support for generic I/O PM domains.

[5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
        (that's necessary for the next patches).

[6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME

[7/8] - Add system-wide PM support for generic I/O PM domains.

[8/8] - Use the new code to represent the SH7372's A4MP power domain.

The patchset has been tested on SH7372 Mackerel board and appears to work
correctly.

I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
which people may possibly use the naming that's going to change in their new
code.  If you agree with that, please let me know, I'll need some serious
ACKs below that patch if it's to be pushed for 3.0. ;-)

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-11 20:23 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:23 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman
  Cc: Linux PM mailing list, LKML, linux-sh

Hi,

This is the 4th update of the patchset adding support for generic I/O PM
domains.  The patches have been reworked quite a bit to take feedback into
account, but I left the Greg's ACK in [4/8] in the hope it still applies
(Greg, please let me know in case it doesn't :-)).

The model here is that a bunch of devices share a common power resource
that can be turned on and off by software.  In addition to that, there
are means to start and stop the activity of each device, for example
by manipulating their clocks.  Moreover, there may be hierarchy of
such things, for example power resource A may be necessary for devices
a, b, c, which don't rely on any other power resources, and for devices
x, y, z that also rely on power resource X.  In that case there one PM
domain object representing devices a, b, c and power resource A, and 
another PM domain object will represent devices x, y, z with power
resource X, plus the first object will be the second one's parent.

Note to Kevin: I know you'd like each PM domain to be able to go into several
different states, but the situation will always be that in some of those
states the devices' registers will remain intact, while in the rest of those
states they will be reset.  Say, there are states 1, 2, 3, 4 and states
1-3 preserve device registers.  Then it is not necessary to save device
registers for "domain" states 1-3 and it only is necessary to save them
when going to state 4.  In that case, .power_off() may map to the "go to
state 4" operation (and analogously .power_on()), while the rest may be
done by .stop_device() and .start_device().  IOW, .power_is_off == true
means "the devices' registers have to be restored", so it need not map to
any particular physical state of a (hardware) power domain.

Note to Magnus and Paul: I didn't use a global lock as suggested, because
I think it may lead to completely unnecessary congestion in situations in
which there are no hierarchies of PM domains.  It is quite easy to show that
the code doesn't deadlock, because (1) no more than 2 locks are held by the
same thread at a time (parent lock and child lock) and (2) they are always
acquired in the same order (parent before the child).

Overall, I think I've taken all of the important dependencies into
consideration, but if you spot something suspicious, please let me know. :-)
Wakeup is not covered at this point, because it's not necessary for the
SH7372's A4LC power domain that's the first user of the new code, but it
is quite clear how add the support for it.  Also, for more complicated
cases it is necessary to take QoS requirements (latencies) into account,
which is in the works (kind of).

[1/8] - Update documentation to reflect the fact that struct dev_power_domain
        callbacks take precedence over subsystem PM callbacks.

[2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
        fact that those objects need not correspond to hardware power domains
        directly.

[3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME

[4/8] - Introduce runtime PM support for generic I/O PM domains.

[5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
        (that's necessary for the next patches).

[6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME

[7/8] - Add system-wide PM support for generic I/O PM domains.

[8/8] - Use the new code to represent the SH7372's A4MP power domain.

The patchset has been tested on SH7372 Mackerel board and appears to work
correctly.

I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
which people may possibly use the naming that's going to change in their new
code.  If you agree with that, please let me know, I'll need some serious
ACKs below that patch if it's to be pushed for 3.0. ;-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 1/8] PM / Domains: Update documentation
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:25   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:25 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Commit 4d27e9dcff00a6425d779b065ec8892e4f391661 (PM: Make power
domain callbacks take precedence over subsystem ones) forgot to
update the device power management documentation to take changes
made by it into account.  Correct that mistake.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt |   39 +++++++++++++--------------------------
 1 file changed, 13 insertions(+), 26 deletions(-)

Index: linux-2.6/Documentation/power/devices.txt
=================================--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -520,33 +520,20 @@ Support for power domains is provided th
 device.  This field is a pointer to an object of type struct dev_power_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
-for the given device during all power transitions, in addition to the respective
-subsystem-level callbacks.  Specifically, the power domain "suspend" callbacks
-(i.e. ->runtime_suspend(), ->suspend(), ->freeze(), ->poweroff(), etc.) are
-executed after the analogous subsystem-level callbacks, while the power domain
-"resume" callbacks (i.e. ->runtime_resume(), ->resume(), ->thaw(), ->restore,
-etc.) are executed before the analogous subsystem-level callbacks.  Error codes
-returned by the "suspend" and "resume" power domain callbacks are ignored.
+for the given device during all power transitions, instead of the respective
+subsystem-level callbacks.  Specifically, if a device's pm_domain pointer is
+not NULL, the ->suspend() callback from the object pointed to by it will be
+executed instead of its subsystem's (e.g. bus type's) ->suspend() callback and
+anlogously for all of the remaining callbacks.  In other words, power management
+domain callbacks, if defined for the given device, always take precedence over
+the callbacks provided by the device's subsystem (e.g. bus type).
 
-Power domain ->runtime_idle() callback is executed before the subsystem-level
-->runtime_idle() callback and the result returned by it is not ignored.  Namely,
-if it returns error code, the subsystem-level ->runtime_idle() callback will not
-be called and the helper function rpm_idle() executing it will return error
-code.  This mechanism is intended to help platforms where saving device state
-is a time consuming operation and should only be carried out if all devices
-in the power domain are idle, before turning off the shared power resource(s).
-Namely, the power domain ->runtime_idle() callback may return error code until
-the pm_runtime_idle() helper (or its asychronous version) has been called for
-all devices in the power domain (it is recommended that the returned error code
-be -EBUSY in those cases), preventing the subsystem-level ->runtime_idle()
-callback from being run prematurely.
-
-The support for device power domains is only relevant to platforms needing to
-use the same subsystem-level (e.g. platform bus type) and device driver power
-management callbacks in many different power domain configurations and wanting
-to avoid incorporating the support for power domains into the subsystem-level
-callbacks.  The other platforms need not implement it or take it into account
-in any way.
+The support for device power management domains is only relevant to platforms
+needing to use the same device driver power management callbacks in many
+different power domain configurations and wanting to avoid incorporating the
+support for power domains into subsystem-level callbacks, for example by
+modifying the platform bus type.  Other platforms need not implement it or take
+it into account in any way.
 
 
 Device Low Power (suspend) States


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 1/8] PM / Domains: Update documentation
@ 2011-06-11 20:25   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:25 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Commit 4d27e9dcff00a6425d779b065ec8892e4f391661 (PM: Make power
domain callbacks take precedence over subsystem ones) forgot to
update the device power management documentation to take changes
made by it into account.  Correct that mistake.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt |   39 +++++++++++++--------------------------
 1 file changed, 13 insertions(+), 26 deletions(-)

Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -520,33 +520,20 @@ Support for power domains is provided th
 device.  This field is a pointer to an object of type struct dev_power_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
-for the given device during all power transitions, in addition to the respective
-subsystem-level callbacks.  Specifically, the power domain "suspend" callbacks
-(i.e. ->runtime_suspend(), ->suspend(), ->freeze(), ->poweroff(), etc.) are
-executed after the analogous subsystem-level callbacks, while the power domain
-"resume" callbacks (i.e. ->runtime_resume(), ->resume(), ->thaw(), ->restore,
-etc.) are executed before the analogous subsystem-level callbacks.  Error codes
-returned by the "suspend" and "resume" power domain callbacks are ignored.
+for the given device during all power transitions, instead of the respective
+subsystem-level callbacks.  Specifically, if a device's pm_domain pointer is
+not NULL, the ->suspend() callback from the object pointed to by it will be
+executed instead of its subsystem's (e.g. bus type's) ->suspend() callback and
+anlogously for all of the remaining callbacks.  In other words, power management
+domain callbacks, if defined for the given device, always take precedence over
+the callbacks provided by the device's subsystem (e.g. bus type).
 
-Power domain ->runtime_idle() callback is executed before the subsystem-level
-->runtime_idle() callback and the result returned by it is not ignored.  Namely,
-if it returns error code, the subsystem-level ->runtime_idle() callback will not
-be called and the helper function rpm_idle() executing it will return error
-code.  This mechanism is intended to help platforms where saving device state
-is a time consuming operation and should only be carried out if all devices
-in the power domain are idle, before turning off the shared power resource(s).
-Namely, the power domain ->runtime_idle() callback may return error code until
-the pm_runtime_idle() helper (or its asychronous version) has been called for
-all devices in the power domain (it is recommended that the returned error code
-be -EBUSY in those cases), preventing the subsystem-level ->runtime_idle()
-callback from being run prematurely.
-
-The support for device power domains is only relevant to platforms needing to
-use the same subsystem-level (e.g. platform bus type) and device driver power
-management callbacks in many different power domain configurations and wanting
-to avoid incorporating the support for power domains into the subsystem-level
-callbacks.  The other platforms need not implement it or take it into account
-in any way.
+The support for device power management domains is only relevant to platforms
+needing to use the same device driver power management callbacks in many
+different power domain configurations and wanting to avoid incorporating the
+support for power domains into subsystem-level callbacks, for example by
+modifying the platform bus type.  Other platforms need not implement it or take
+it into account in any way.
 
 
 Device Low Power (suspend) States


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 1/8] PM / Domains: Update documentation
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (2 preceding siblings ...)
  (?)
@ 2011-06-11 20:25 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:25 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Commit 4d27e9dcff00a6425d779b065ec8892e4f391661 (PM: Make power
domain callbacks take precedence over subsystem ones) forgot to
update the device power management documentation to take changes
made by it into account.  Correct that mistake.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt |   39 +++++++++++++--------------------------
 1 file changed, 13 insertions(+), 26 deletions(-)

Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -520,33 +520,20 @@ Support for power domains is provided th
 device.  This field is a pointer to an object of type struct dev_power_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
-for the given device during all power transitions, in addition to the respective
-subsystem-level callbacks.  Specifically, the power domain "suspend" callbacks
-(i.e. ->runtime_suspend(), ->suspend(), ->freeze(), ->poweroff(), etc.) are
-executed after the analogous subsystem-level callbacks, while the power domain
-"resume" callbacks (i.e. ->runtime_resume(), ->resume(), ->thaw(), ->restore,
-etc.) are executed before the analogous subsystem-level callbacks.  Error codes
-returned by the "suspend" and "resume" power domain callbacks are ignored.
+for the given device during all power transitions, instead of the respective
+subsystem-level callbacks.  Specifically, if a device's pm_domain pointer is
+not NULL, the ->suspend() callback from the object pointed to by it will be
+executed instead of its subsystem's (e.g. bus type's) ->suspend() callback and
+anlogously for all of the remaining callbacks.  In other words, power management
+domain callbacks, if defined for the given device, always take precedence over
+the callbacks provided by the device's subsystem (e.g. bus type).
 
-Power domain ->runtime_idle() callback is executed before the subsystem-level
-->runtime_idle() callback and the result returned by it is not ignored.  Namely,
-if it returns error code, the subsystem-level ->runtime_idle() callback will not
-be called and the helper function rpm_idle() executing it will return error
-code.  This mechanism is intended to help platforms where saving device state
-is a time consuming operation and should only be carried out if all devices
-in the power domain are idle, before turning off the shared power resource(s).
-Namely, the power domain ->runtime_idle() callback may return error code until
-the pm_runtime_idle() helper (or its asychronous version) has been called for
-all devices in the power domain (it is recommended that the returned error code
-be -EBUSY in those cases), preventing the subsystem-level ->runtime_idle()
-callback from being run prematurely.
-
-The support for device power domains is only relevant to platforms needing to
-use the same subsystem-level (e.g. platform bus type) and device driver power
-management callbacks in many different power domain configurations and wanting
-to avoid incorporating the support for power domains into the subsystem-level
-callbacks.  The other platforms need not implement it or take it into account
-in any way.
+The support for device power management domains is only relevant to platforms
+needing to use the same device driver power management callbacks in many
+different power domain configurations and wanting to avoid incorporating the
+support for power domains into subsystem-level callbacks, for example by
+modifying the platform bus type.  Other platforms need not implement it or take
+it into account in any way.
 
 
 Device Low Power (suspend) States

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:26   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:26 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
(PM: Add support for device power domains), which introduced the
struct dev_power_domain type for representing device power domains,
evidently confuses some developers who tend to think that objects
of this type must correspond to "power domains" as defined by
hardware, which is not the case.  Namely, at the kernel level, a
struct dev_power_domain object can represent arbitrary set of devices
that are mutually dependent power management-wise and need not belong
to one hardware power domain.  To avoid that confusion, rename struct
dev_power_domain to struct dev_pm_domain and rename the related
pointers in struct device and struct pm_clk_notifier_block from
pwr_domain to pm_domain.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt          |    8 ++++----
 arch/arm/mach-omap1/pm_bus.c             |    4 ++--
 arch/arm/mach-shmobile/pm_runtime.c      |    8 ++++----
 arch/arm/plat-omap/omap_device.c         |    4 ++--
 arch/sh/kernel/cpu/shmobile/pm_runtime.c |    6 +++---
 drivers/base/power/clock_ops.c           |   14 +++++++-------
 drivers/base/power/main.c                |   30 +++++++++++++++---------------
 drivers/base/power/runtime.c             |   12 ++++++------
 include/linux/device.h                   |    4 ++--
 include/linux/pm.h                       |    2 +-
 include/linux/pm_runtime.h               |    2 +-
 11 files changed, 47 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
@@ -49,7 +49,7 @@ static int omap1_pm_runtime_resume(struc
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = omap1_pm_runtime_suspend,
 		.runtime_resume = omap1_pm_runtime_resume,
@@ -58,7 +58,7 @@ static struct dev_power_domain default_p
 };
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = &default_power_domain,
+	.pm_domain = &default_pm_domain,
 	.con_ids = { "ick", "fck", NULL, },
 };
 
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
@@ -28,7 +28,7 @@ static int default_platform_runtime_idle
 	return pm_runtime_suspend(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = pm_runtime_clk_suspend,
 		.runtime_resume = pm_runtime_clk_resume,
@@ -37,16 +37,16 @@ static struct dev_power_domain default_p
 	},
 };
 
-#define DEFAULT_PWR_DOMAIN_PTR	(&default_power_domain)
+#define DEFAULT_PM_DOMAIN_PTR	(&default_pm_domain)
 
 #else
 
-#define DEFAULT_PWR_DOMAIN_PTR	NULL
+#define DEFAULT_PM_DOMAIN_PTR	NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+	.pm_domain = DEFAULT_PM_DOMAIN_PTR,
 	.con_ids = { NULL, },
 };
 
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
@@ -550,7 +550,7 @@ static int _od_runtime_resume(struct dev
 	return omap_device_enable(pdev);
 }
 
-static struct dev_power_domain omap_device_power_domain = {
+static struct dev_pm_domain omap_device_pm_domain = {
 	.ops = {
 		.runtime_suspend = _od_runtime_suspend,
 		.runtime_resume = _od_runtime_resume,
@@ -571,7 +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;
+	od->pdev.dev.pwr_domain = &omap_device_pm_domain;
 	return platform_device_register(&od->pdev);
 }
 
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
@@ -256,7 +256,7 @@ out:
 	return ret;
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_power_domain = {
 	.ops = {
 		.runtime_suspend = default_platform_runtime_suspend,
 		.runtime_resume = default_platform_runtime_resume,
@@ -285,7 +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;
+		dev->pm_domain = &default_power_domain;
 		break;
 	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
 	case BUS_NOTIFY_BOUND_DRIVER:
@@ -299,7 +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;
+		dev->pm_domain = NULL;
 		break;
 	}
 	return 0;
Index: linux-2.6/Documentation/power/devices.txt
=================================--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -506,8 +506,8 @@ routines.  Nevertheless, different callb
 situation where it actually matters.
 
 
-Device Power Domains
---------------------
+Device Power Management Domains
+-------------------------------
 Sometimes devices share reference clocks or other power resources.  In those
 cases it generally is not possible to put devices into low-power states
 individually.  Instead, a set of devices sharing a power resource can be put
@@ -516,8 +516,8 @@ power resource.  Of course, they also ne
 together, by turning the shared power resource on.  A set of devices with this
 property is often referred to as a power domain.
 
-Support for power domains is provided through the pwr_domain field of struct
-device.  This field is a pointer to an object of type struct dev_power_domain,
+Support for power domains is provided through the pm_domain field of struct
+device.  This field is a pointer to an object of type struct dev_pm_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
 for the given device during all power transitions, instead of the respective
Index: linux-2.6/include/linux/device.h
=================================--- linux-2.6.orig/include/linux/device.h
+++ linux-2.6/include/linux/device.h
@@ -516,7 +516,7 @@ struct device_dma_parameters {
  * 		minimizes board-specific #ifdefs in drivers.
  * @power:	For device power management.
  * 		See Documentation/power/devices.txt for details.
- * @pwr_domain:	Provide callbacks that are executed during system suspend,
+ * @pm_domain:	Provide callbacks that are executed during system suspend,
  * 		hibernation, system resume and during runtime PM transitions
  * 		along with subsystem-level and driver-level callbacks.
  * @numa_node:	NUMA node this device is close to.
@@ -568,7 +568,7 @@ struct device {
 	void		*platform_data;	/* Platform specific data, device
 					   core doesn't touch it */
 	struct dev_pm_info	power;
-	struct dev_power_domain	*pwr_domain;
+	struct dev_pm_domain	*pm_domain;
 
 #ifdef CONFIG_NUMA
 	int		numa_node;	/* NUMA node this device is close to */
Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -471,7 +471,7 @@ extern void update_pm_runtime_accounting
  * hibernation, system resume and during runtime PM transitions along with
  * subsystem-level and driver-level callbacks.
  */
-struct dev_power_domain {
+struct dev_pm_domain {
 	struct dev_pm_ops	ops;
 };
 
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
@@ -247,7 +247,7 @@ static inline void pm_runtime_dont_use_a
 
 struct pm_clk_notifier_block {
 	struct notifier_block nb;
-	struct dev_power_domain *pwr_domain;
+	struct dev_pm_domain *pm_domain;
 	char *con_ids[];
 };
 
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
@@ -278,11 +278,11 @@ int pm_runtime_clk_resume(struct device
  *
  * 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
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_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
+ * If the device's pm_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.
  */
@@ -300,14 +300,14 @@ static int pm_runtime_clk_notify(struct
 
 	switch (action) {
 	case BUS_NOTIFY_ADD_DEVICE:
-		if (dev->pwr_domain)
+		if (dev->pm_domain)
 			break;
 
 		error = pm_runtime_clk_init(dev);
 		if (error)
 			break;
 
-		dev->pwr_domain = clknb->pwr_domain;
+		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
 				pm_runtime_clk_add(dev, *con_id);
@@ -317,10 +317,10 @@ static int pm_runtime_clk_notify(struct
 
 		break;
 	case BUS_NOTIFY_DEL_DEVICE:
-		if (dev->pwr_domain != clknb->pwr_domain)
+		if (dev->pm_domain != clknb->pm_domain)
 			break;
 
-		dev->pwr_domain = NULL;
+		dev->pm_domain = NULL;
 		pm_runtime_clk_destroy(dev);
 		break;
 	}
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
@@ -424,9 +424,9 @@ static int device_resume_noirq(struct de
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "EARLY power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_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);
@@ -513,9 +513,9 @@ static int device_resume(struct device *
 
 	dev->power.in_suspend = false;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -630,10 +630,10 @@ static void device_complete(struct devic
 {
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "completing power domain ");
-		if (dev->pwr_domain->ops.complete)
-			dev->pwr_domain->ops.complete(dev);
+		if (dev->pm_domain->ops.complete)
+			dev->pm_domain->ops.complete(dev);
 	} else if (dev->type && dev->type->pm) {
 		pm_dev_dbg(dev, state, "completing type ");
 		if (dev->type->pm->complete)
@@ -733,9 +733,9 @@ static int device_suspend_noirq(struct d
 {
 	int error;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "LATE power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
 		if (error)
 			return error;
 	} else if (dev->type && dev->type->pm) {
@@ -842,9 +842,9 @@ static int __device_suspend(struct devic
 		goto End;
 	}
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -968,11 +968,11 @@ static int device_prepare(struct device
 
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_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 (dev->pm_domain->ops.prepare)
+			error = dev->pm_domain->ops.prepare(dev);
+		suspend_report_result(dev->pm_domain->ops.prepare, error);
 		if (error)
 			goto End;
 	} else if (dev->type && dev->type->pm) {
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
@@ -213,8 +213,8 @@ static int rpm_idle(struct device *dev,
 
 	dev->power.idle_notification = true;
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_idle;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_idle;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_idle;
 	else if (dev->class && dev->class->pm)
@@ -374,8 +374,8 @@ static int rpm_suspend(struct device *de
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_suspend;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_suspend;
 	else if (dev->class && dev->class->pm)
@@ -573,8 +573,8 @@ static int rpm_resume(struct device *dev
 
 	__update_runtime_status(dev, RPM_RESUMING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_resume;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_resume;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_resume;
 	else if (dev->class && dev->class->pm)


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
@ 2011-06-11 20:26   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:26 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
(PM: Add support for device power domains), which introduced the
struct dev_power_domain type for representing device power domains,
evidently confuses some developers who tend to think that objects
of this type must correspond to "power domains" as defined by
hardware, which is not the case.  Namely, at the kernel level, a
struct dev_power_domain object can represent arbitrary set of devices
that are mutually dependent power management-wise and need not belong
to one hardware power domain.  To avoid that confusion, rename struct
dev_power_domain to struct dev_pm_domain and rename the related
pointers in struct device and struct pm_clk_notifier_block from
pwr_domain to pm_domain.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt          |    8 ++++----
 arch/arm/mach-omap1/pm_bus.c             |    4 ++--
 arch/arm/mach-shmobile/pm_runtime.c      |    8 ++++----
 arch/arm/plat-omap/omap_device.c         |    4 ++--
 arch/sh/kernel/cpu/shmobile/pm_runtime.c |    6 +++---
 drivers/base/power/clock_ops.c           |   14 +++++++-------
 drivers/base/power/main.c                |   30 +++++++++++++++---------------
 drivers/base/power/runtime.c             |   12 ++++++------
 include/linux/device.h                   |    4 ++--
 include/linux/pm.h                       |    2 +-
 include/linux/pm_runtime.h               |    2 +-
 11 files changed, 47 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
@@ -49,7 +49,7 @@ static int omap1_pm_runtime_resume(struc
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = omap1_pm_runtime_suspend,
 		.runtime_resume = omap1_pm_runtime_resume,
@@ -58,7 +58,7 @@ static struct dev_power_domain default_p
 };
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = &default_power_domain,
+	.pm_domain = &default_pm_domain,
 	.con_ids = { "ick", "fck", NULL, },
 };
 
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
@@ -28,7 +28,7 @@ static int default_platform_runtime_idle
 	return pm_runtime_suspend(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = pm_runtime_clk_suspend,
 		.runtime_resume = pm_runtime_clk_resume,
@@ -37,16 +37,16 @@ static struct dev_power_domain default_p
 	},
 };
 
-#define DEFAULT_PWR_DOMAIN_PTR	(&default_power_domain)
+#define DEFAULT_PM_DOMAIN_PTR	(&default_pm_domain)
 
 #else
 
-#define DEFAULT_PWR_DOMAIN_PTR	NULL
+#define DEFAULT_PM_DOMAIN_PTR	NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+	.pm_domain = DEFAULT_PM_DOMAIN_PTR,
 	.con_ids = { NULL, },
 };
 
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
@@ -550,7 +550,7 @@ static int _od_runtime_resume(struct dev
 	return omap_device_enable(pdev);
 }
 
-static struct dev_power_domain omap_device_power_domain = {
+static struct dev_pm_domain omap_device_pm_domain = {
 	.ops = {
 		.runtime_suspend = _od_runtime_suspend,
 		.runtime_resume = _od_runtime_resume,
@@ -571,7 +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;
+	od->pdev.dev.pwr_domain = &omap_device_pm_domain;
 	return platform_device_register(&od->pdev);
 }
 
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
@@ -256,7 +256,7 @@ out:
 	return ret;
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_power_domain = {
 	.ops = {
 		.runtime_suspend = default_platform_runtime_suspend,
 		.runtime_resume = default_platform_runtime_resume,
@@ -285,7 +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;
+		dev->pm_domain = &default_power_domain;
 		break;
 	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
 	case BUS_NOTIFY_BOUND_DRIVER:
@@ -299,7 +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;
+		dev->pm_domain = NULL;
 		break;
 	}
 	return 0;
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -506,8 +506,8 @@ routines.  Nevertheless, different callb
 situation where it actually matters.
 
 
-Device Power Domains
---------------------
+Device Power Management Domains
+-------------------------------
 Sometimes devices share reference clocks or other power resources.  In those
 cases it generally is not possible to put devices into low-power states
 individually.  Instead, a set of devices sharing a power resource can be put
@@ -516,8 +516,8 @@ power resource.  Of course, they also ne
 together, by turning the shared power resource on.  A set of devices with this
 property is often referred to as a power domain.
 
-Support for power domains is provided through the pwr_domain field of struct
-device.  This field is a pointer to an object of type struct dev_power_domain,
+Support for power domains is provided through the pm_domain field of struct
+device.  This field is a pointer to an object of type struct dev_pm_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
 for the given device during all power transitions, instead of the respective
Index: linux-2.6/include/linux/device.h
===================================================================
--- linux-2.6.orig/include/linux/device.h
+++ linux-2.6/include/linux/device.h
@@ -516,7 +516,7 @@ struct device_dma_parameters {
  * 		minimizes board-specific #ifdefs in drivers.
  * @power:	For device power management.
  * 		See Documentation/power/devices.txt for details.
- * @pwr_domain:	Provide callbacks that are executed during system suspend,
+ * @pm_domain:	Provide callbacks that are executed during system suspend,
  * 		hibernation, system resume and during runtime PM transitions
  * 		along with subsystem-level and driver-level callbacks.
  * @numa_node:	NUMA node this device is close to.
@@ -568,7 +568,7 @@ struct device {
 	void		*platform_data;	/* Platform specific data, device
 					   core doesn't touch it */
 	struct dev_pm_info	power;
-	struct dev_power_domain	*pwr_domain;
+	struct dev_pm_domain	*pm_domain;
 
 #ifdef CONFIG_NUMA
 	int		numa_node;	/* NUMA node this device is close to */
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -471,7 +471,7 @@ extern void update_pm_runtime_accounting
  * hibernation, system resume and during runtime PM transitions along with
  * subsystem-level and driver-level callbacks.
  */
-struct dev_power_domain {
+struct dev_pm_domain {
 	struct dev_pm_ops	ops;
 };
 
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
@@ -247,7 +247,7 @@ static inline void pm_runtime_dont_use_a
 
 struct pm_clk_notifier_block {
 	struct notifier_block nb;
-	struct dev_power_domain *pwr_domain;
+	struct dev_pm_domain *pm_domain;
 	char *con_ids[];
 };
 
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
@@ -278,11 +278,11 @@ int pm_runtime_clk_resume(struct device
  *
  * 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
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_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
+ * If the device's pm_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.
  */
@@ -300,14 +300,14 @@ static int pm_runtime_clk_notify(struct
 
 	switch (action) {
 	case BUS_NOTIFY_ADD_DEVICE:
-		if (dev->pwr_domain)
+		if (dev->pm_domain)
 			break;
 
 		error = pm_runtime_clk_init(dev);
 		if (error)
 			break;
 
-		dev->pwr_domain = clknb->pwr_domain;
+		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
 				pm_runtime_clk_add(dev, *con_id);
@@ -317,10 +317,10 @@ static int pm_runtime_clk_notify(struct
 
 		break;
 	case BUS_NOTIFY_DEL_DEVICE:
-		if (dev->pwr_domain != clknb->pwr_domain)
+		if (dev->pm_domain != clknb->pm_domain)
 			break;
 
-		dev->pwr_domain = NULL;
+		dev->pm_domain = NULL;
 		pm_runtime_clk_destroy(dev);
 		break;
 	}
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
@@ -424,9 +424,9 @@ static int device_resume_noirq(struct de
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "EARLY power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_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);
@@ -513,9 +513,9 @@ static int device_resume(struct device *
 
 	dev->power.in_suspend = false;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -630,10 +630,10 @@ static void device_complete(struct devic
 {
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "completing power domain ");
-		if (dev->pwr_domain->ops.complete)
-			dev->pwr_domain->ops.complete(dev);
+		if (dev->pm_domain->ops.complete)
+			dev->pm_domain->ops.complete(dev);
 	} else if (dev->type && dev->type->pm) {
 		pm_dev_dbg(dev, state, "completing type ");
 		if (dev->type->pm->complete)
@@ -733,9 +733,9 @@ static int device_suspend_noirq(struct d
 {
 	int error;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "LATE power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
 		if (error)
 			return error;
 	} else if (dev->type && dev->type->pm) {
@@ -842,9 +842,9 @@ static int __device_suspend(struct devic
 		goto End;
 	}
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -968,11 +968,11 @@ static int device_prepare(struct device
 
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_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 (dev->pm_domain->ops.prepare)
+			error = dev->pm_domain->ops.prepare(dev);
+		suspend_report_result(dev->pm_domain->ops.prepare, error);
 		if (error)
 			goto End;
 	} else if (dev->type && dev->type->pm) {
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
@@ -213,8 +213,8 @@ static int rpm_idle(struct device *dev,
 
 	dev->power.idle_notification = true;
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_idle;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_idle;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_idle;
 	else if (dev->class && dev->class->pm)
@@ -374,8 +374,8 @@ static int rpm_suspend(struct device *de
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_suspend;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_suspend;
 	else if (dev->class && dev->class->pm)
@@ -573,8 +573,8 @@ static int rpm_resume(struct device *dev
 
 	__update_runtime_status(dev, RPM_RESUMING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_resume;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_resume;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_resume;
 	else if (dev->class && dev->class->pm)


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (4 preceding siblings ...)
  (?)
@ 2011-06-11 20:26 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:26 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
(PM: Add support for device power domains), which introduced the
struct dev_power_domain type for representing device power domains,
evidently confuses some developers who tend to think that objects
of this type must correspond to "power domains" as defined by
hardware, which is not the case.  Namely, at the kernel level, a
struct dev_power_domain object can represent arbitrary set of devices
that are mutually dependent power management-wise and need not belong
to one hardware power domain.  To avoid that confusion, rename struct
dev_power_domain to struct dev_pm_domain and rename the related
pointers in struct device and struct pm_clk_notifier_block from
pwr_domain to pm_domain.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt          |    8 ++++----
 arch/arm/mach-omap1/pm_bus.c             |    4 ++--
 arch/arm/mach-shmobile/pm_runtime.c      |    8 ++++----
 arch/arm/plat-omap/omap_device.c         |    4 ++--
 arch/sh/kernel/cpu/shmobile/pm_runtime.c |    6 +++---
 drivers/base/power/clock_ops.c           |   14 +++++++-------
 drivers/base/power/main.c                |   30 +++++++++++++++---------------
 drivers/base/power/runtime.c             |   12 ++++++------
 include/linux/device.h                   |    4 ++--
 include/linux/pm.h                       |    2 +-
 include/linux/pm_runtime.h               |    2 +-
 11 files changed, 47 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
@@ -49,7 +49,7 @@ static int omap1_pm_runtime_resume(struc
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = omap1_pm_runtime_suspend,
 		.runtime_resume = omap1_pm_runtime_resume,
@@ -58,7 +58,7 @@ static struct dev_power_domain default_p
 };
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = &default_power_domain,
+	.pm_domain = &default_pm_domain,
 	.con_ids = { "ick", "fck", NULL, },
 };
 
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
@@ -28,7 +28,7 @@ static int default_platform_runtime_idle
 	return pm_runtime_suspend(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = pm_runtime_clk_suspend,
 		.runtime_resume = pm_runtime_clk_resume,
@@ -37,16 +37,16 @@ static struct dev_power_domain default_p
 	},
 };
 
-#define DEFAULT_PWR_DOMAIN_PTR	(&default_power_domain)
+#define DEFAULT_PM_DOMAIN_PTR	(&default_pm_domain)
 
 #else
 
-#define DEFAULT_PWR_DOMAIN_PTR	NULL
+#define DEFAULT_PM_DOMAIN_PTR	NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+	.pm_domain = DEFAULT_PM_DOMAIN_PTR,
 	.con_ids = { NULL, },
 };
 
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
@@ -550,7 +550,7 @@ static int _od_runtime_resume(struct dev
 	return omap_device_enable(pdev);
 }
 
-static struct dev_power_domain omap_device_power_domain = {
+static struct dev_pm_domain omap_device_pm_domain = {
 	.ops = {
 		.runtime_suspend = _od_runtime_suspend,
 		.runtime_resume = _od_runtime_resume,
@@ -571,7 +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;
+	od->pdev.dev.pwr_domain = &omap_device_pm_domain;
 	return platform_device_register(&od->pdev);
 }
 
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
@@ -256,7 +256,7 @@ out:
 	return ret;
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_power_domain = {
 	.ops = {
 		.runtime_suspend = default_platform_runtime_suspend,
 		.runtime_resume = default_platform_runtime_resume,
@@ -285,7 +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;
+		dev->pm_domain = &default_power_domain;
 		break;
 	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
 	case BUS_NOTIFY_BOUND_DRIVER:
@@ -299,7 +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;
+		dev->pm_domain = NULL;
 		break;
 	}
 	return 0;
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -506,8 +506,8 @@ routines.  Nevertheless, different callb
 situation where it actually matters.
 
 
-Device Power Domains
---------------------
+Device Power Management Domains
+-------------------------------
 Sometimes devices share reference clocks or other power resources.  In those
 cases it generally is not possible to put devices into low-power states
 individually.  Instead, a set of devices sharing a power resource can be put
@@ -516,8 +516,8 @@ power resource.  Of course, they also ne
 together, by turning the shared power resource on.  A set of devices with this
 property is often referred to as a power domain.
 
-Support for power domains is provided through the pwr_domain field of struct
-device.  This field is a pointer to an object of type struct dev_power_domain,
+Support for power domains is provided through the pm_domain field of struct
+device.  This field is a pointer to an object of type struct dev_pm_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
 for the given device during all power transitions, instead of the respective
Index: linux-2.6/include/linux/device.h
===================================================================
--- linux-2.6.orig/include/linux/device.h
+++ linux-2.6/include/linux/device.h
@@ -516,7 +516,7 @@ struct device_dma_parameters {
  * 		minimizes board-specific #ifdefs in drivers.
  * @power:	For device power management.
  * 		See Documentation/power/devices.txt for details.
- * @pwr_domain:	Provide callbacks that are executed during system suspend,
+ * @pm_domain:	Provide callbacks that are executed during system suspend,
  * 		hibernation, system resume and during runtime PM transitions
  * 		along with subsystem-level and driver-level callbacks.
  * @numa_node:	NUMA node this device is close to.
@@ -568,7 +568,7 @@ struct device {
 	void		*platform_data;	/* Platform specific data, device
 					   core doesn't touch it */
 	struct dev_pm_info	power;
-	struct dev_power_domain	*pwr_domain;
+	struct dev_pm_domain	*pm_domain;
 
 #ifdef CONFIG_NUMA
 	int		numa_node;	/* NUMA node this device is close to */
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -471,7 +471,7 @@ extern void update_pm_runtime_accounting
  * hibernation, system resume and during runtime PM transitions along with
  * subsystem-level and driver-level callbacks.
  */
-struct dev_power_domain {
+struct dev_pm_domain {
 	struct dev_pm_ops	ops;
 };
 
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
@@ -247,7 +247,7 @@ static inline void pm_runtime_dont_use_a
 
 struct pm_clk_notifier_block {
 	struct notifier_block nb;
-	struct dev_power_domain *pwr_domain;
+	struct dev_pm_domain *pm_domain;
 	char *con_ids[];
 };
 
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
@@ -278,11 +278,11 @@ int pm_runtime_clk_resume(struct device
  *
  * 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
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_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
+ * If the device's pm_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.
  */
@@ -300,14 +300,14 @@ static int pm_runtime_clk_notify(struct
 
 	switch (action) {
 	case BUS_NOTIFY_ADD_DEVICE:
-		if (dev->pwr_domain)
+		if (dev->pm_domain)
 			break;
 
 		error = pm_runtime_clk_init(dev);
 		if (error)
 			break;
 
-		dev->pwr_domain = clknb->pwr_domain;
+		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
 				pm_runtime_clk_add(dev, *con_id);
@@ -317,10 +317,10 @@ static int pm_runtime_clk_notify(struct
 
 		break;
 	case BUS_NOTIFY_DEL_DEVICE:
-		if (dev->pwr_domain != clknb->pwr_domain)
+		if (dev->pm_domain != clknb->pm_domain)
 			break;
 
-		dev->pwr_domain = NULL;
+		dev->pm_domain = NULL;
 		pm_runtime_clk_destroy(dev);
 		break;
 	}
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
@@ -424,9 +424,9 @@ static int device_resume_noirq(struct de
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "EARLY power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_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);
@@ -513,9 +513,9 @@ static int device_resume(struct device *
 
 	dev->power.in_suspend = false;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -630,10 +630,10 @@ static void device_complete(struct devic
 {
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "completing power domain ");
-		if (dev->pwr_domain->ops.complete)
-			dev->pwr_domain->ops.complete(dev);
+		if (dev->pm_domain->ops.complete)
+			dev->pm_domain->ops.complete(dev);
 	} else if (dev->type && dev->type->pm) {
 		pm_dev_dbg(dev, state, "completing type ");
 		if (dev->type->pm->complete)
@@ -733,9 +733,9 @@ static int device_suspend_noirq(struct d
 {
 	int error;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "LATE power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
 		if (error)
 			return error;
 	} else if (dev->type && dev->type->pm) {
@@ -842,9 +842,9 @@ static int __device_suspend(struct devic
 		goto End;
 	}
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -968,11 +968,11 @@ static int device_prepare(struct device
 
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_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 (dev->pm_domain->ops.prepare)
+			error = dev->pm_domain->ops.prepare(dev);
+		suspend_report_result(dev->pm_domain->ops.prepare, error);
 		if (error)
 			goto End;
 	} else if (dev->type && dev->type->pm) {
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
@@ -213,8 +213,8 @@ static int rpm_idle(struct device *dev,
 
 	dev->power.idle_notification = true;
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_idle;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_idle;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_idle;
 	else if (dev->class && dev->class->pm)
@@ -374,8 +374,8 @@ static int rpm_suspend(struct device *de
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_suspend;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_suspend;
 	else if (dev->class && dev->class->pm)
@@ -573,8 +573,8 @@ static int rpm_resume(struct device *dev
 
 	__update_runtime_status(dev, RPM_RESUMING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_resume;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_resume;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_resume;
 	else if (dev->class && dev->class->pm)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 3/8] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:27   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:27 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

The subsys_data field of struct dev_pm_info, introduced by commit
1d2b71f61b6a10216274e27b717becf9ae101fc7 (PM / Runtime: Add subsystem
data field to struct dev_pm_info), is going to be used even if
CONFIG_PM_RUNTIME is not set, so move it from under the #ifdef.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 include/linux/pm.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -460,8 +460,8 @@ struct dev_pm_info {
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	void			*subsys_data;  /* Owned by the subsystem. */
 #endif
+	void			*subsys_data;  /* Owned by the subsystem. */
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 3/8] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME
@ 2011-06-11 20:27   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:27 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

The subsys_data field of struct dev_pm_info, introduced by commit
1d2b71f61b6a10216274e27b717becf9ae101fc7 (PM / Runtime: Add subsystem
data field to struct dev_pm_info), is going to be used even if
CONFIG_PM_RUNTIME is not set, so move it from under the #ifdef.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 include/linux/pm.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -460,8 +460,8 @@ struct dev_pm_info {
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	void			*subsys_data;  /* Owned by the subsystem. */
 #endif
+	void			*subsys_data;  /* Owned by the subsystem. */
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 3/8] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (6 preceding siblings ...)
  (?)
@ 2011-06-11 20:27 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:27 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

The subsys_data field of struct dev_pm_info, introduced by commit
1d2b71f61b6a10216274e27b717becf9ae101fc7 (PM / Runtime: Add subsystem
data field to struct dev_pm_info), is going to be used even if
CONFIG_PM_RUNTIME is not set, so move it from under the #ifdef.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 include/linux/pm.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -460,8 +460,8 @@ struct dev_pm_info {
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	void			*subsys_data;  /* Owned by the subsystem. */
 #endif
+	void			*subsys_data;  /* Owned by the subsystem. */
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:31   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:31 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  467 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm.h          |    3 
 include/linux/pm_domain.h   |   78 +++++++
 kernel/power/Kconfig        |    4 
 5 files changed, 552 insertions(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm_domain.h
=================================--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct dev_pm_domain *domain);
+	int (*power_on)(struct dev_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -472,7 +472,8 @@ extern void update_pm_runtime_accounting
  * subsystem-level and driver-level callbacks.
  */
 struct dev_pm_domain {
-	struct dev_pm_ops	ops;
+	struct dev_pm_ops ops;
+	void *platform_data;
 };
 
 /*
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
=================================--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,467 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count = 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_resume)
+		drv->pm->runtime_resume(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		struct device *dev = dle->dev;
+		struct device_driver *drv = dev->driver;
+
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		if (drv && drv->pm && drv->pm->runtime_suspend)
+			ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+
+		if (ret)
+			goto err_dev;
+		else
+			dle->need_restore = true;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count = 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(&genpd->domain);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev = dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain = new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
=================================--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-11 20:31   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:31 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  467 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm.h          |    3 
 include/linux/pm_domain.h   |   78 +++++++
 kernel/power/Kconfig        |    4 
 5 files changed, 552 insertions(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct dev_pm_domain *domain);
+	int (*power_on)(struct dev_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -472,7 +472,8 @@ extern void update_pm_runtime_accounting
  * subsystem-level and driver-level callbacks.
  */
 struct dev_pm_domain {
-	struct dev_pm_ops	ops;
+	struct dev_pm_ops ops;
+	void *platform_data;
 };
 
 /*
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,467 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_resume)
+		drv->pm->runtime_resume(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		struct device *dev = dle->dev;
+		struct device_driver *drv = dev->driver;
+
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		if (drv && drv->pm && drv->pm->runtime_suspend)
+			ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+
+		if (ret)
+			goto err_dev;
+		else
+			dle->need_restore = true;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(&genpd->domain);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (7 preceding siblings ...)
  (?)
@ 2011-06-11 20:31 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:31 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  467 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm.h          |    3 
 include/linux/pm_domain.h   |   78 +++++++
 kernel/power/Kconfig        |    4 
 5 files changed, 552 insertions(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct dev_pm_domain *domain);
+	int (*power_on)(struct dev_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -472,7 +472,8 @@ extern void update_pm_runtime_accounting
  * subsystem-level and driver-level callbacks.
  */
 struct dev_pm_domain {
-	struct dev_pm_ops	ops;
+	struct dev_pm_ops ops;
+	void *platform_data;
 };
 
 /*
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,467 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_resume)
+		drv->pm->runtime_resume(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		struct device *dev = dle->dev;
+		struct device_driver *drv = dev->driver;
+
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		if (drv && drv->pm && drv->pm->runtime_suspend)
+			ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+
+		if (ret)
+			goto err_dev;
+		else
+			dle->need_restore = true;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(&genpd->domain);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 5/8] PM: Introduce generic "noirq" callback routines for subsystems
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:36   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:36 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce generic "noirq" power management callback routines for
subsystems in addition to the "regular" generic PM callback routines.

The new routines will be used, among other things, for implementing
system-wide PM transitions support for generic PM domains.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/generic_ops.c |   90 ++++++++++++++++++++++++++++++++-------
 include/linux/pm.h               |    6 ++
 2 files changed, 82 insertions(+), 14 deletions(-)

Index: linux-2.6/drivers/base/power/generic_ops.c
=================================--- linux-2.6.orig/drivers/base/power/generic_ops.c
+++ linux-2.6/drivers/base/power/generic_ops.c
@@ -94,12 +94,13 @@ int pm_generic_prepare(struct device *de
  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * If the device has not been suspended at run time, execute the
  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  * return its error code.  Otherwise, return zero.
  */
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -109,16 +110,16 @@ static int __pm_generic_call(struct devi
 
 	switch (event) {
 	case PM_EVENT_SUSPEND:
-		callback = pm->suspend;
+		callback = noirq ? pm->suspend_noirq : pm->suspend;
 		break;
 	case PM_EVENT_FREEZE:
-		callback = pm->freeze;
+		callback = noirq ? pm->freeze_noirq : pm->freeze;
 		break;
 	case PM_EVENT_HIBERNATE:
-		callback = pm->poweroff;
+		callback = noirq ? pm->poweroff_noirq : pm->poweroff;
 		break;
 	case PM_EVENT_THAW:
-		callback = pm->thaw;
+		callback = noirq ? pm->thaw_noirq : pm->thaw;
 		break;
 	default:
 		callback = NULL;
@@ -129,42 +130,82 @@ static int __pm_generic_call(struct devi
 }
 
 /**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
  * pm_generic_suspend - Generic suspend callback for subsystems.
  * @dev: Device to suspend.
  */
 int pm_generic_suspend(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_suspend);
 
 /**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
  * pm_generic_freeze - Generic freeze callback for subsystems.
  * @dev: Device to freeze.
  */
 int pm_generic_freeze(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_FREEZE);
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_freeze);
 
 /**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
  * pm_generic_poweroff - Generic poweroff callback for subsystems.
  * @dev: Device to handle.
  */
 int pm_generic_poweroff(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
 
 /**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
  * pm_generic_thaw - Generic thaw callback for subsystems.
  * @dev: Device to thaw.
  */
 int pm_generic_thaw(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_THAW);
+	return __pm_generic_call(dev, PM_EVENT_THAW, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_thaw);
 
@@ -172,12 +213,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
  * __pm_generic_resume - Generic resume/restore callback for subsystems.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * Execute the resume/resotre callback provided by the @dev's driver, if
  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
  * Return the callback's error code.
  */
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -188,10 +230,10 @@ static int __pm_generic_resume(struct de
 
 	switch (event) {
 	case PM_EVENT_RESUME:
-		callback = pm->resume;
+		callback = noirq ? pm->resume_noirq : pm->resume;
 		break;
 	case PM_EVENT_RESTORE:
-		callback = pm->restore;
+		callback = noirq ? pm->restore_noirq : pm->restore;
 		break;
 	default:
 		callback = NULL;
@@ -212,22 +254,42 @@ static int __pm_generic_resume(struct de
 }
 
 /**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
  * pm_generic_resume - Generic resume callback for subsystems.
  * @dev: Device to resume.
  */
 int pm_generic_resume(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESUME);
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_resume);
 
 /**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
  * pm_generic_restore - Generic restore callback for subsystems.
  * @dev: Device to restore.
  */
 int pm_generic_restore(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_restore);
 
Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -553,11 +553,17 @@ extern void __suspend_report_result(cons
 extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
 
 extern int pm_generic_prepare(struct device *dev);
+extern int pm_generic_suspend_noirq(struct device *dev);
 extern int pm_generic_suspend(struct device *dev);
+extern int pm_generic_resume_noirq(struct device *dev);
 extern int pm_generic_resume(struct device *dev);
+extern int pm_generic_freeze_noirq(struct device *dev);
 extern int pm_generic_freeze(struct device *dev);
+extern int pm_generic_thaw_noirq(struct device *dev);
 extern int pm_generic_thaw(struct device *dev);
+extern int pm_generic_restore_noirq(struct device *dev);
 extern int pm_generic_restore(struct device *dev);
+extern int pm_generic_poweroff_noirq(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 5/8] PM: Introduce generic "noirq" callback routines for subsystems
@ 2011-06-11 20:36   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:36 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce generic "noirq" power management callback routines for
subsystems in addition to the "regular" generic PM callback routines.

The new routines will be used, among other things, for implementing
system-wide PM transitions support for generic PM domains.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/generic_ops.c |   90 ++++++++++++++++++++++++++++++++-------
 include/linux/pm.h               |    6 ++
 2 files changed, 82 insertions(+), 14 deletions(-)

Index: linux-2.6/drivers/base/power/generic_ops.c
===================================================================
--- linux-2.6.orig/drivers/base/power/generic_ops.c
+++ linux-2.6/drivers/base/power/generic_ops.c
@@ -94,12 +94,13 @@ int pm_generic_prepare(struct device *de
  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * If the device has not been suspended at run time, execute the
  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  * return its error code.  Otherwise, return zero.
  */
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -109,16 +110,16 @@ static int __pm_generic_call(struct devi
 
 	switch (event) {
 	case PM_EVENT_SUSPEND:
-		callback = pm->suspend;
+		callback = noirq ? pm->suspend_noirq : pm->suspend;
 		break;
 	case PM_EVENT_FREEZE:
-		callback = pm->freeze;
+		callback = noirq ? pm->freeze_noirq : pm->freeze;
 		break;
 	case PM_EVENT_HIBERNATE:
-		callback = pm->poweroff;
+		callback = noirq ? pm->poweroff_noirq : pm->poweroff;
 		break;
 	case PM_EVENT_THAW:
-		callback = pm->thaw;
+		callback = noirq ? pm->thaw_noirq : pm->thaw;
 		break;
 	default:
 		callback = NULL;
@@ -129,42 +130,82 @@ static int __pm_generic_call(struct devi
 }
 
 /**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
  * pm_generic_suspend - Generic suspend callback for subsystems.
  * @dev: Device to suspend.
  */
 int pm_generic_suspend(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_suspend);
 
 /**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
  * pm_generic_freeze - Generic freeze callback for subsystems.
  * @dev: Device to freeze.
  */
 int pm_generic_freeze(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_FREEZE);
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_freeze);
 
 /**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
  * pm_generic_poweroff - Generic poweroff callback for subsystems.
  * @dev: Device to handle.
  */
 int pm_generic_poweroff(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
 
 /**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
  * pm_generic_thaw - Generic thaw callback for subsystems.
  * @dev: Device to thaw.
  */
 int pm_generic_thaw(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_THAW);
+	return __pm_generic_call(dev, PM_EVENT_THAW, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_thaw);
 
@@ -172,12 +213,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
  * __pm_generic_resume - Generic resume/restore callback for subsystems.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * Execute the resume/resotre callback provided by the @dev's driver, if
  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
  * Return the callback's error code.
  */
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -188,10 +230,10 @@ static int __pm_generic_resume(struct de
 
 	switch (event) {
 	case PM_EVENT_RESUME:
-		callback = pm->resume;
+		callback = noirq ? pm->resume_noirq : pm->resume;
 		break;
 	case PM_EVENT_RESTORE:
-		callback = pm->restore;
+		callback = noirq ? pm->restore_noirq : pm->restore;
 		break;
 	default:
 		callback = NULL;
@@ -212,22 +254,42 @@ static int __pm_generic_resume(struct de
 }
 
 /**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
  * pm_generic_resume - Generic resume callback for subsystems.
  * @dev: Device to resume.
  */
 int pm_generic_resume(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESUME);
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_resume);
 
 /**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
  * pm_generic_restore - Generic restore callback for subsystems.
  * @dev: Device to restore.
  */
 int pm_generic_restore(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_restore);
 
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -553,11 +553,17 @@ extern void __suspend_report_result(cons
 extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
 
 extern int pm_generic_prepare(struct device *dev);
+extern int pm_generic_suspend_noirq(struct device *dev);
 extern int pm_generic_suspend(struct device *dev);
+extern int pm_generic_resume_noirq(struct device *dev);
 extern int pm_generic_resume(struct device *dev);
+extern int pm_generic_freeze_noirq(struct device *dev);
 extern int pm_generic_freeze(struct device *dev);
+extern int pm_generic_thaw_noirq(struct device *dev);
 extern int pm_generic_thaw(struct device *dev);
+extern int pm_generic_restore_noirq(struct device *dev);
 extern int pm_generic_restore(struct device *dev);
+extern int pm_generic_poweroff_noirq(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 5/8] PM: Introduce generic "noirq" callback routines for subsystems
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (10 preceding siblings ...)
  (?)
@ 2011-06-11 20:36 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:36 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce generic "noirq" power management callback routines for
subsystems in addition to the "regular" generic PM callback routines.

The new routines will be used, among other things, for implementing
system-wide PM transitions support for generic PM domains.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/generic_ops.c |   90 ++++++++++++++++++++++++++++++++-------
 include/linux/pm.h               |    6 ++
 2 files changed, 82 insertions(+), 14 deletions(-)

Index: linux-2.6/drivers/base/power/generic_ops.c
===================================================================
--- linux-2.6.orig/drivers/base/power/generic_ops.c
+++ linux-2.6/drivers/base/power/generic_ops.c
@@ -94,12 +94,13 @@ int pm_generic_prepare(struct device *de
  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * If the device has not been suspended at run time, execute the
  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  * return its error code.  Otherwise, return zero.
  */
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -109,16 +110,16 @@ static int __pm_generic_call(struct devi
 
 	switch (event) {
 	case PM_EVENT_SUSPEND:
-		callback = pm->suspend;
+		callback = noirq ? pm->suspend_noirq : pm->suspend;
 		break;
 	case PM_EVENT_FREEZE:
-		callback = pm->freeze;
+		callback = noirq ? pm->freeze_noirq : pm->freeze;
 		break;
 	case PM_EVENT_HIBERNATE:
-		callback = pm->poweroff;
+		callback = noirq ? pm->poweroff_noirq : pm->poweroff;
 		break;
 	case PM_EVENT_THAW:
-		callback = pm->thaw;
+		callback = noirq ? pm->thaw_noirq : pm->thaw;
 		break;
 	default:
 		callback = NULL;
@@ -129,42 +130,82 @@ static int __pm_generic_call(struct devi
 }
 
 /**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
  * pm_generic_suspend - Generic suspend callback for subsystems.
  * @dev: Device to suspend.
  */
 int pm_generic_suspend(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_suspend);
 
 /**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
  * pm_generic_freeze - Generic freeze callback for subsystems.
  * @dev: Device to freeze.
  */
 int pm_generic_freeze(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_FREEZE);
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_freeze);
 
 /**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
  * pm_generic_poweroff - Generic poweroff callback for subsystems.
  * @dev: Device to handle.
  */
 int pm_generic_poweroff(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
 
 /**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
  * pm_generic_thaw - Generic thaw callback for subsystems.
  * @dev: Device to thaw.
  */
 int pm_generic_thaw(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_THAW);
+	return __pm_generic_call(dev, PM_EVENT_THAW, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_thaw);
 
@@ -172,12 +213,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
  * __pm_generic_resume - Generic resume/restore callback for subsystems.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * Execute the resume/resotre callback provided by the @dev's driver, if
  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
  * Return the callback's error code.
  */
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -188,10 +230,10 @@ static int __pm_generic_resume(struct de
 
 	switch (event) {
 	case PM_EVENT_RESUME:
-		callback = pm->resume;
+		callback = noirq ? pm->resume_noirq : pm->resume;
 		break;
 	case PM_EVENT_RESTORE:
-		callback = pm->restore;
+		callback = noirq ? pm->restore_noirq : pm->restore;
 		break;
 	default:
 		callback = NULL;
@@ -212,22 +254,42 @@ static int __pm_generic_resume(struct de
 }
 
 /**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
  * pm_generic_resume - Generic resume callback for subsystems.
  * @dev: Device to resume.
  */
 int pm_generic_resume(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESUME);
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_resume);
 
 /**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
  * pm_generic_restore - Generic restore callback for subsystems.
  * @dev: Device to restore.
  */
 int pm_generic_restore(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_restore);
 
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -553,11 +553,17 @@ extern void __suspend_report_result(cons
 extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
 
 extern int pm_generic_prepare(struct device *dev);
+extern int pm_generic_suspend_noirq(struct device *dev);
 extern int pm_generic_suspend(struct device *dev);
+extern int pm_generic_resume_noirq(struct device *dev);
 extern int pm_generic_resume(struct device *dev);
+extern int pm_generic_freeze_noirq(struct device *dev);
 extern int pm_generic_freeze(struct device *dev);
+extern int pm_generic_thaw_noirq(struct device *dev);
 extern int pm_generic_thaw(struct device *dev);
+extern int pm_generic_restore_noirq(struct device *dev);
 extern int pm_generic_restore(struct device *dev);
+extern int pm_generic_poweroff_noirq(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 6/8] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:37   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:37 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

There is some code in drivers/base/power/domain.c that will be useful
for both runtime PM and system-wide power transitions, so make it
depend on CONFIG_PM instead of CONFIG_PM_RUNTIME.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  118 +++++++++++++++++++++++---------------------
 1 file changed, 64 insertions(+), 54 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -14,6 +14,66 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return ERR_PTR(-EINVAL);
+
+	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(&genpd->domain);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+#endif /* CONFIG_PM */
+
 #ifdef CONFIG_PM_RUNTIME
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -155,11 +215,10 @@ static int pm_genpd_runtime_suspend(stru
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	if (genpd->parent)
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
@@ -182,54 +241,6 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int pm_genpd_poweron(struct generic_pm_domain *genpd)
-{
-	int ret = 0;
-
- start:
-	if (genpd->parent)
-		mutex_lock(&genpd->parent->lock);
-	mutex_lock(&genpd->lock);
-
-	if (!genpd->power_is_off)
-		goto out;
-
-	if (genpd->parent && genpd->parent->power_is_off) {
-		mutex_unlock(&genpd->lock);
-		mutex_unlock(&genpd->parent->lock);
-
-		ret = pm_genpd_poweron(genpd->parent);
-		if (ret)
-			return ret;
-
-		goto start;
-	}
-
-	if (genpd->power_on) {
-		int ret = genpd->power_on(&genpd->domain);
-		if (ret)
-			goto out;
-	}
-
-	genpd->power_is_off = false;
-	if (genpd->parent)
-		genpd->parent->sd_count++;
-
- out:
-	mutex_unlock(&genpd->lock);
-	if (genpd->parent)
-		mutex_unlock(&genpd->parent->lock);
-
-	return ret;
-}
-
-/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -245,11 +256,10 @@ static int pm_genpd_runtime_resume(struc
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	ret = pm_genpd_poweron(genpd);
 	if (ret)
 		return ret;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 6/8] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME
@ 2011-06-11 20:37   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:37 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

There is some code in drivers/base/power/domain.c that will be useful
for both runtime PM and system-wide power transitions, so make it
depend on CONFIG_PM instead of CONFIG_PM_RUNTIME.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  118 +++++++++++++++++++++++---------------------
 1 file changed, 64 insertions(+), 54 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -14,6 +14,66 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return ERR_PTR(-EINVAL);
+
+	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(&genpd->domain);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+#endif /* CONFIG_PM */
+
 #ifdef CONFIG_PM_RUNTIME
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -155,11 +215,10 @@ static int pm_genpd_runtime_suspend(stru
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	if (genpd->parent)
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
@@ -182,54 +241,6 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int pm_genpd_poweron(struct generic_pm_domain *genpd)
-{
-	int ret = 0;
-
- start:
-	if (genpd->parent)
-		mutex_lock(&genpd->parent->lock);
-	mutex_lock(&genpd->lock);
-
-	if (!genpd->power_is_off)
-		goto out;
-
-	if (genpd->parent && genpd->parent->power_is_off) {
-		mutex_unlock(&genpd->lock);
-		mutex_unlock(&genpd->parent->lock);
-
-		ret = pm_genpd_poweron(genpd->parent);
-		if (ret)
-			return ret;
-
-		goto start;
-	}
-
-	if (genpd->power_on) {
-		int ret = genpd->power_on(&genpd->domain);
-		if (ret)
-			goto out;
-	}
-
-	genpd->power_is_off = false;
-	if (genpd->parent)
-		genpd->parent->sd_count++;
-
- out:
-	mutex_unlock(&genpd->lock);
-	if (genpd->parent)
-		mutex_unlock(&genpd->parent->lock);
-
-	return ret;
-}
-
-/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -245,11 +256,10 @@ static int pm_genpd_runtime_resume(struc
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	ret = pm_genpd_poweron(genpd);
 	if (ret)
 		return ret;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 6/8] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (11 preceding siblings ...)
  (?)
@ 2011-06-11 20:37 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:37 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

There is some code in drivers/base/power/domain.c that will be useful
for both runtime PM and system-wide power transitions, so make it
depend on CONFIG_PM instead of CONFIG_PM_RUNTIME.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  118 +++++++++++++++++++++++---------------------
 1 file changed, 64 insertions(+), 54 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -14,6 +14,66 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return ERR_PTR(-EINVAL);
+
+	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(&genpd->domain);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+#endif /* CONFIG_PM */
+
 #ifdef CONFIG_PM_RUNTIME
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -155,11 +215,10 @@ static int pm_genpd_runtime_suspend(stru
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	if (genpd->parent)
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
@@ -182,54 +241,6 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int pm_genpd_poweron(struct generic_pm_domain *genpd)
-{
-	int ret = 0;
-
- start:
-	if (genpd->parent)
-		mutex_lock(&genpd->parent->lock);
-	mutex_lock(&genpd->lock);
-
-	if (!genpd->power_is_off)
-		goto out;
-
-	if (genpd->parent && genpd->parent->power_is_off) {
-		mutex_unlock(&genpd->lock);
-		mutex_unlock(&genpd->parent->lock);
-
-		ret = pm_genpd_poweron(genpd->parent);
-		if (ret)
-			return ret;
-
-		goto start;
-	}
-
-	if (genpd->power_on) {
-		int ret = genpd->power_on(&genpd->domain);
-		if (ret)
-			goto out;
-	}
-
-	genpd->power_is_off = false;
-	if (genpd->parent)
-		genpd->parent->sd_count++;
-
- out:
-	mutex_unlock(&genpd->lock);
-	if (genpd->parent)
-		mutex_unlock(&genpd->parent->lock);
-
-	return ret;
-}
-
-/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -245,11 +256,10 @@ static int pm_genpd_runtime_resume(struc
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	ret = pm_genpd_poweron(genpd);
 	if (ret)
 		return ret;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 7/8] PM / Domains: System-wide transitions support for generic PM domains
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:39   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:39 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and
its subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  544 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_domain.h   |    7 
 2 files changed, 539 insertions(+), 12 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -123,7 +124,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -241,6 +242,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -251,7 +273,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -265,28 +286,498 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev = dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
 	}
+}
 
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ = 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	mutex_unlock(&genpd->lock);
+
+	ret = pm_generic_prepare(dev);
+	/*
+	 * This should be done by the core, after calling the subsystem-level
+	 * .prepare(), but moving it to the core at this point would break a
+	 * number of platform drivers.
+	 */
+	pm_runtime_disable(dev);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(&genpd->domain);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	pm_runtime_enable(dev);
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count = 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -305,6 +796,11 @@ int pm_genpd_add_device(struct generic_p
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev = dev) {
 			ret = -EINVAL;
@@ -319,6 +815,7 @@ int pm_genpd_add_device(struct generic_p
 
 	dle->dev = dev;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -348,6 +845,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -356,6 +858,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -363,6 +866,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -471,7 +975,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
=================================--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,6 +30,10 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct dev_pm_domain *domain);
 	int (*power_on)(struct dev_pm_domain *domain);
 	int (*start_device)(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 7/8] PM / Domains: System-wide transitions support for generic PM domains
@ 2011-06-11 20:39   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:39 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and
its subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  544 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_domain.h   |    7 
 2 files changed, 539 insertions(+), 12 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -123,7 +124,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -241,6 +242,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -251,7 +273,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -265,28 +286,498 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
 	}
+}
 
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	mutex_unlock(&genpd->lock);
+
+	ret = pm_generic_prepare(dev);
+	/*
+	 * This should be done by the core, after calling the subsystem-level
+	 * .prepare(), but moving it to the core at this point would break a
+	 * number of platform drivers.
+	 */
+	pm_runtime_disable(dev);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(&genpd->domain);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	pm_runtime_enable(dev);
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -305,6 +796,11 @@ int pm_genpd_add_device(struct generic_p
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -319,6 +815,7 @@ int pm_genpd_add_device(struct generic_p
 
 	dle->dev = dev;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -348,6 +845,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -356,6 +858,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -363,6 +866,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -471,7 +975,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,6 +30,10 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct dev_pm_domain *domain);
 	int (*power_on)(struct dev_pm_domain *domain);
 	int (*start_device)(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 7/8] PM / Domains: System-wide transitions support for generic PM domains
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (14 preceding siblings ...)
  (?)
@ 2011-06-11 20:39 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:39 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and
its subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  544 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_domain.h   |    7 
 2 files changed, 539 insertions(+), 12 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -123,7 +124,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -241,6 +242,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -251,7 +273,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -265,28 +286,498 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
 	}
+}
 
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	mutex_unlock(&genpd->lock);
+
+	ret = pm_generic_prepare(dev);
+	/*
+	 * This should be done by the core, after calling the subsystem-level
+	 * .prepare(), but moving it to the core at this point would break a
+	 * number of platform drivers.
+	 */
+	pm_runtime_disable(dev);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(&genpd->domain);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	pm_runtime_enable(dev);
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -305,6 +796,11 @@ int pm_genpd_add_device(struct generic_p
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -319,6 +815,7 @@ int pm_genpd_add_device(struct generic_p
 
 	dle->dev = dev;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -348,6 +845,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -356,6 +858,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -363,6 +866,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -471,7 +975,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,6 +30,10 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct dev_pm_domain *domain);
 	int (*power_on)(struct dev_pm_domain *domain);
 	int (*start_device)(struct device *dev);

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:40   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:40 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-shmobile/Kconfig               |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    3 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   16 +++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   81 +++++++++++++++++++++++++++
 5 files changed, 105 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1496,6 +1496,9 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,19 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+#ifdef CONFIG_PM
+extern struct generic_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_add_device_to_domain(struct generic_pm_domain *domain,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_add_device_to_domain(struct generic_pm_domain *dom,
+					       struct platform_device *pd) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/Kconfig
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
+++ linux-2.6/arch/arm/mach-shmobile/Kconfig
@@ -19,6 +19,7 @@ config ARCH_SH7372
 	select CPU_V7
 	select SH_CLK_CPG
 	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select PM_GENERIC_DOMAINS
 
 config ARCH_SH73A0
 	bool "SH-Mobile AG5 (R8A73A00)"
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,97 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+struct sh7372_domain_data {
+	unsigned int bit_shift;
+};
+
+static int pd_power_down(struct dev_pm_domain *domain)
+{
+	struct sh7372_domain_data *dd = domain->platform_data;
+	unsigned int mask = 1 << dd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		__raw_writel(mask, SPDCR);
+
+		while (__raw_readl(SPDCR) & mask) {}
+
+		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static int pd_power_up(struct dev_pm_domain *domain)
+{
+	struct sh7372_domain_data *dd = domain->platform_data;
+	unsigned int mask = 1 << dd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		__raw_writel(mask, SWUCR);
+
+		while (__raw_readl(SWUCR) & mask) {}
+
+		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static void sh7372_init_domain(struct generic_pm_domain *domain,
+			       struct sh7372_domain_data *pdata)
+{
+	pm_genpd_init(domain, NULL, false);
+	domain->domain.platform_data = pdata;
+	domain->stop_device = pm_runtime_clk_suspend;
+	domain->start_device = pm_runtime_clk_resume;
+	domain->power_off = pd_power_down;
+	domain->power_on = pd_power_up;
+}
+
+void sh7372_add_device_to_domain(struct generic_pm_domain *domain,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(domain, dev);
+}
+
+static struct sh7372_domain_data sh7372_a4lc_domain_data = {
+	.bit_shift = 1,
+};
+
+struct generic_pm_domain sh7372_a4lc_domain;
+
+static int __init sh7372_power_domains_init(void)
+{
+	sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
+	return 0;
+}
+core_initcall(sh7372_power_domains_init);
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
@ 2011-06-11 20:40   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:40 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-shmobile/Kconfig               |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    3 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   16 +++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   81 +++++++++++++++++++++++++++
 5 files changed, 105 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1496,6 +1496,9 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,19 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+#ifdef CONFIG_PM
+extern struct generic_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_add_device_to_domain(struct generic_pm_domain *domain,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_add_device_to_domain(struct generic_pm_domain *dom,
+					       struct platform_device *pd) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
+++ linux-2.6/arch/arm/mach-shmobile/Kconfig
@@ -19,6 +19,7 @@ config ARCH_SH7372
 	select CPU_V7
 	select SH_CLK_CPG
 	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select PM_GENERIC_DOMAINS
 
 config ARCH_SH73A0
 	bool "SH-Mobile AG5 (R8A73A00)"
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,97 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+struct sh7372_domain_data {
+	unsigned int bit_shift;
+};
+
+static int pd_power_down(struct dev_pm_domain *domain)
+{
+	struct sh7372_domain_data *dd = domain->platform_data;
+	unsigned int mask = 1 << dd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		__raw_writel(mask, SPDCR);
+
+		while (__raw_readl(SPDCR) & mask) {}
+
+		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static int pd_power_up(struct dev_pm_domain *domain)
+{
+	struct sh7372_domain_data *dd = domain->platform_data;
+	unsigned int mask = 1 << dd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		__raw_writel(mask, SWUCR);
+
+		while (__raw_readl(SWUCR) & mask) {}
+
+		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static void sh7372_init_domain(struct generic_pm_domain *domain,
+			       struct sh7372_domain_data *pdata)
+{
+	pm_genpd_init(domain, NULL, false);
+	domain->domain.platform_data = pdata;
+	domain->stop_device = pm_runtime_clk_suspend;
+	domain->start_device = pm_runtime_clk_resume;
+	domain->power_off = pd_power_down;
+	domain->power_on = pd_power_up;
+}
+
+void sh7372_add_device_to_domain(struct generic_pm_domain *domain,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(domain, dev);
+}
+
+static struct sh7372_domain_data sh7372_a4lc_domain_data = {
+	.bit_shift = 1,
+};
+
+struct generic_pm_domain sh7372_a4lc_domain;
+
+static int __init sh7372_power_domains_init(void)
+{
+	sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
+	return 0;
+}
+core_initcall(sh7372_power_domains_init);
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (15 preceding siblings ...)
  (?)
@ 2011-06-11 20:40 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 20:40 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-shmobile/Kconfig               |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    3 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   16 +++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   81 +++++++++++++++++++++++++++
 5 files changed, 105 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1496,6 +1496,9 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,19 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+#ifdef CONFIG_PM
+extern struct generic_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_add_device_to_domain(struct generic_pm_domain *domain,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_add_device_to_domain(struct generic_pm_domain *dom,
+					       struct platform_device *pd) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
+++ linux-2.6/arch/arm/mach-shmobile/Kconfig
@@ -19,6 +19,7 @@ config ARCH_SH7372
 	select CPU_V7
 	select SH_CLK_CPG
 	select ARCH_WANT_OPTIONAL_GPIOLIB
+	select PM_GENERIC_DOMAINS
 
 config ARCH_SH73A0
 	bool "SH-Mobile AG5 (R8A73A00)"
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,97 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+struct sh7372_domain_data {
+	unsigned int bit_shift;
+};
+
+static int pd_power_down(struct dev_pm_domain *domain)
+{
+	struct sh7372_domain_data *dd = domain->platform_data;
+	unsigned int mask = 1 << dd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		__raw_writel(mask, SPDCR);
+
+		while (__raw_readl(SPDCR) & mask) {}
+
+		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static int pd_power_up(struct dev_pm_domain *domain)
+{
+	struct sh7372_domain_data *dd = domain->platform_data;
+	unsigned int mask = 1 << dd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		__raw_writel(mask, SWUCR);
+
+		while (__raw_readl(SWUCR) & mask) {}
+
+		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static void sh7372_init_domain(struct generic_pm_domain *domain,
+			       struct sh7372_domain_data *pdata)
+{
+	pm_genpd_init(domain, NULL, false);
+	domain->domain.platform_data = pdata;
+	domain->stop_device = pm_runtime_clk_suspend;
+	domain->start_device = pm_runtime_clk_resume;
+	domain->power_off = pd_power_down;
+	domain->power_on = pd_power_up;
+}
+
+void sh7372_add_device_to_domain(struct generic_pm_domain *domain,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(domain, dev);
+}
+
+static struct sh7372_domain_data sh7372_a4lc_domain_data = {
+	.bit_shift = 1,
+};
+
+struct generic_pm_domain sh7372_a4lc_domain;
+
+static int __init sh7372_power_domains_init(void)
+{
+	sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
+	return 0;
+}
+core_initcall(sh7372_power_domains_init);
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-11 20:57   ` Greg KH
  -1 siblings, 0 replies; 261+ messages in thread
From: Greg KH @ 2011-06-11 20:57 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Magnus Damm, Paul Walmsley, Kevin Hilman, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

On Sat, Jun 11, 2011 at 10:23:04PM +0200, Rafael J. Wysocki wrote:
> Hi,
> 
> This is the 4th update of the patchset adding support for generic I/O PM
> domains.  The patches have been reworked quite a bit to take feedback into
> account, but I left the Greg's ACK in [4/8] in the hope it still applies
> (Greg, please let me know in case it doesn't :-)).

It still applies, thanks for leaving it in.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-11 20:57   ` Greg KH
  0 siblings, 0 replies; 261+ messages in thread
From: Greg KH @ 2011-06-11 20:57 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Magnus Damm, Paul Walmsley, Kevin Hilman, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

On Sat, Jun 11, 2011 at 10:23:04PM +0200, Rafael J. Wysocki wrote:
> Hi,
> 
> This is the 4th update of the patchset adding support for generic I/O PM
> domains.  The patches have been reworked quite a bit to take feedback into
> account, but I left the Greg's ACK in [4/8] in the hope it still applies
> (Greg, please let me know in case it doesn't :-)).

It still applies, thanks for leaving it in.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (17 preceding siblings ...)
  (?)
@ 2011-06-11 20:57 ` Greg KH
  -1 siblings, 0 replies; 261+ messages in thread
From: Greg KH @ 2011-06-11 20:57 UTC (permalink / raw)
  To: Rafael J. Wysocki; +Cc: linux-sh, LKML, Linux PM mailing list

On Sat, Jun 11, 2011 at 10:23:04PM +0200, Rafael J. Wysocki wrote:
> Hi,
> 
> This is the 4th update of the patchset adding support for generic I/O PM
> domains.  The patches have been reworked quite a bit to take feedback into
> account, but I left the Greg's ACK in [4/8] in the hope it still applies
> (Greg, please let me know in case it doesn't :-)).

It still applies, thanks for leaving it in.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v2)
  2011-06-11 20:39   ` Rafael J. Wysocki
@ 2011-06-11 23:28     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 23:28 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

One fix here.  Namely, the pm_runtime_disable() in pm_genpd_prepare() has
to go under the genpd lock to prevent runtime PM from happening after we've
done __pm_genpd_runtime_resume(), because pm_genpd_suspend() assumes that
the device will be active.  Of course, that causes pm_generic_prepare()
to be called after pm_runtime_disable(), so it makes sense to call
pm_generic_complete() before pm_runtime_enable() in pm_genpd_complete().

Thanks,
Rafael

---
 drivers/base/power/domain.c |  537 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_domain.h   |    7 
 2 files changed, 532 insertions(+), 12 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -123,7 +124,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -241,6 +242,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -251,7 +273,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -265,28 +286,491 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev = dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ = 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
 	}
 
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(&genpd->domain);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count = 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -305,6 +789,11 @@ int pm_genpd_add_device(struct generic_p
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev = dev) {
 			ret = -EINVAL;
@@ -319,6 +808,7 @@ int pm_genpd_add_device(struct generic_p
 
 	dle->dev = dev;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -348,6 +838,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -356,6 +851,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -363,6 +859,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -471,7 +968,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
=================================--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,6 +30,10 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct dev_pm_domain *domain);
 	int (*power_on)(struct dev_pm_domain *domain);
 	int (*start_device)(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v2)
@ 2011-06-11 23:28     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 23:28 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

One fix here.  Namely, the pm_runtime_disable() in pm_genpd_prepare() has
to go under the genpd lock to prevent runtime PM from happening after we've
done __pm_genpd_runtime_resume(), because pm_genpd_suspend() assumes that
the device will be active.  Of course, that causes pm_generic_prepare()
to be called after pm_runtime_disable(), so it makes sense to call
pm_generic_complete() before pm_runtime_enable() in pm_genpd_complete().

Thanks,
Rafael

---
 drivers/base/power/domain.c |  537 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_domain.h   |    7 
 2 files changed, 532 insertions(+), 12 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -123,7 +124,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -241,6 +242,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -251,7 +273,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -265,28 +286,491 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
 	}
 
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(&genpd->domain);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -305,6 +789,11 @@ int pm_genpd_add_device(struct generic_p
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -319,6 +808,7 @@ int pm_genpd_add_device(struct generic_p
 
 	dle->dev = dev;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -348,6 +838,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -356,6 +851,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -363,6 +859,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -471,7 +968,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,6 +30,10 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct dev_pm_domain *domain);
 	int (*power_on)(struct dev_pm_domain *domain);
 	int (*start_device)(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v2)
  2011-06-11 20:39   ` Rafael J. Wysocki
  (?)
@ 2011-06-11 23:28   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-11 23:28 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

One fix here.  Namely, the pm_runtime_disable() in pm_genpd_prepare() has
to go under the genpd lock to prevent runtime PM from happening after we've
done __pm_genpd_runtime_resume(), because pm_genpd_suspend() assumes that
the device will be active.  Of course, that causes pm_generic_prepare()
to be called after pm_runtime_disable(), so it makes sense to call
pm_generic_complete() before pm_runtime_enable() in pm_genpd_complete().

Thanks,
Rafael

---
 drivers/base/power/domain.c |  537 +++++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_domain.h   |    7 
 2 files changed, 532 insertions(+), 12 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -123,7 +124,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -241,6 +242,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -251,7 +273,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -265,28 +286,491 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(&genpd->domain);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
 	}
 
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(&genpd->domain);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -305,6 +789,11 @@ int pm_genpd_add_device(struct generic_p
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -319,6 +808,7 @@ int pm_genpd_add_device(struct generic_p
 
 	dle->dev = dev;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -348,6 +838,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -356,6 +851,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -363,6 +859,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -471,7 +968,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,6 +30,10 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct dev_pm_domain *domain);
 	int (*power_on)(struct dev_pm_domain *domain);
 	int (*start_device)(struct device *dev);

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-11 20:40   ` Rafael J. Wysocki
@ 2011-06-14 13:12     ` Magnus Damm
  -1 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-14 13:12 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

Hi Rafael,

On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Use the generic power domains support introduced by the previous
> patch to implement support for power domains on SH7372.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

Thanks for your work on this. I just tried this on my Mackerel board,
but I can't seem to get the pd_power_up() and pd_power_down()
callbacks to be executed. It is probably a misconfiguration from my
side.

Here's some feedback on the sh7372-specific code:

> --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> @@ -19,6 +19,7 @@ config ARCH_SH7372
>        select CPU_V7
>        select SH_CLK_CPG
>        select ARCH_WANT_OPTIONAL_GPIOLIB
> +       select PM_GENERIC_DOMAINS

We want to support a single ARM binary for multiple boards, so this
should be enabled for all SoCs in mach-shmobile as a whole.

> --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> @@ -15,16 +15,97 @@
>  #include <linux/list.h>
>  #include <linux/err.h>
>  #include <linux/slab.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/platform_device.h>
>  #include <asm/system.h>
>  #include <asm/io.h>
>  #include <asm/tlbflush.h>
>  #include <mach/common.h>
> +#include <mach/sh7372.h>
>
>  #define SMFRAM 0xe6a70000
>  #define SYSTBCR 0xe6150024
>  #define SBAR 0xe6180020
>  #define APARMBAREA 0xe6f10020
>
> +#define SPDCR 0xe6180008
> +#define SWUCR 0xe6180014
> +#define PSTR 0xe6180080
> +
> +struct sh7372_domain_data {
> +       unsigned int bit_shift;
> +};

Is it possible to make struct sh7372_domain_data include struct
generic_pm_domain? I suspect so since these two data types seem to be
linked together. I guess container_of() can be used for conversion
between the types?

> +
> +static int pd_power_down(struct dev_pm_domain *domain)
> +{
> +       struct sh7372_domain_data *dd = domain->platform_data;
> +       unsigned int mask = 1 << dd->bit_shift;
> +
> +       if (__raw_readl(PSTR) & mask) {
> +               __raw_writel(mask, SPDCR);
> +
> +               while (__raw_readl(SPDCR) & mask) {}

This probably wants a cpu_relax() in the polling loop. I realize that
my prototype hack lacked that...

> +               pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> +                        mask, __raw_readl(PSTR));
> +       }
> +
> +       return 0;
> +}
> +
> +static int pd_power_up(struct dev_pm_domain *domain)
> +{
> +       struct sh7372_domain_data *dd = domain->platform_data;
> +       unsigned int mask = 1 << dd->bit_shift;
> +
> +       if (!(__raw_readl(PSTR) & mask)) {
> +               __raw_writel(mask, SWUCR);
> +
> +               while (__raw_readl(SWUCR) & mask) {}

Same cpu_relax() here.

> +static int __init sh7372_power_domains_init(void)
> +{
> +       sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
> +       return 0;
> +}
> +core_initcall(sh7372_power_domains_init);

This initcall is going to be executed regardless which SoC we're
running on. We only want it called for sh7372 though.

If you look at other SoC-specific code then you will notice that
initcalls are only used for functions in mach-shmobile/ that are
common for all SoCs implemented under mach-shmobile.

You most likely want to initialize from sh7372_pm_init(), but for that
to work you probably have to reorder your code inside mackerel_init().

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
@ 2011-06-14 13:12     ` Magnus Damm
  0 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-14 13:12 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

Hi Rafael,

On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Use the generic power domains support introduced by the previous
> patch to implement support for power domains on SH7372.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

Thanks for your work on this. I just tried this on my Mackerel board,
but I can't seem to get the pd_power_up() and pd_power_down()
callbacks to be executed. It is probably a misconfiguration from my
side.

Here's some feedback on the sh7372-specific code:

> --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> @@ -19,6 +19,7 @@ config ARCH_SH7372
>        select CPU_V7
>        select SH_CLK_CPG
>        select ARCH_WANT_OPTIONAL_GPIOLIB
> +       select PM_GENERIC_DOMAINS

We want to support a single ARM binary for multiple boards, so this
should be enabled for all SoCs in mach-shmobile as a whole.

> --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> @@ -15,16 +15,97 @@
>  #include <linux/list.h>
>  #include <linux/err.h>
>  #include <linux/slab.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/platform_device.h>
>  #include <asm/system.h>
>  #include <asm/io.h>
>  #include <asm/tlbflush.h>
>  #include <mach/common.h>
> +#include <mach/sh7372.h>
>
>  #define SMFRAM 0xe6a70000
>  #define SYSTBCR 0xe6150024
>  #define SBAR 0xe6180020
>  #define APARMBAREA 0xe6f10020
>
> +#define SPDCR 0xe6180008
> +#define SWUCR 0xe6180014
> +#define PSTR 0xe6180080
> +
> +struct sh7372_domain_data {
> +       unsigned int bit_shift;
> +};

Is it possible to make struct sh7372_domain_data include struct
generic_pm_domain? I suspect so since these two data types seem to be
linked together. I guess container_of() can be used for conversion
between the types?

> +
> +static int pd_power_down(struct dev_pm_domain *domain)
> +{
> +       struct sh7372_domain_data *dd = domain->platform_data;
> +       unsigned int mask = 1 << dd->bit_shift;
> +
> +       if (__raw_readl(PSTR) & mask) {
> +               __raw_writel(mask, SPDCR);
> +
> +               while (__raw_readl(SPDCR) & mask) {}

This probably wants a cpu_relax() in the polling loop. I realize that
my prototype hack lacked that...

> +               pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> +                        mask, __raw_readl(PSTR));
> +       }
> +
> +       return 0;
> +}
> +
> +static int pd_power_up(struct dev_pm_domain *domain)
> +{
> +       struct sh7372_domain_data *dd = domain->platform_data;
> +       unsigned int mask = 1 << dd->bit_shift;
> +
> +       if (!(__raw_readl(PSTR) & mask)) {
> +               __raw_writel(mask, SWUCR);
> +
> +               while (__raw_readl(SWUCR) & mask) {}

Same cpu_relax() here.

> +static int __init sh7372_power_domains_init(void)
> +{
> +       sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
> +       return 0;
> +}
> +core_initcall(sh7372_power_domains_init);

This initcall is going to be executed regardless which SoC we're
running on. We only want it called for sh7372 though.

If you look at other SoC-specific code then you will notice that
initcalls are only used for functions in mach-shmobile/ that are
common for all SoCs implemented under mach-shmobile.

You most likely want to initialize from sh7372_pm_init(), but for that
to work you probably have to reorder your code inside mackerel_init().

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-11 20:40   ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-14 13:12   ` Magnus Damm
  -1 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-14 13:12 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

Hi Rafael,

On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Use the generic power domains support introduced by the previous
> patch to implement support for power domains on SH7372.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

Thanks for your work on this. I just tried this on my Mackerel board,
but I can't seem to get the pd_power_up() and pd_power_down()
callbacks to be executed. It is probably a misconfiguration from my
side.

Here's some feedback on the sh7372-specific code:

> --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> @@ -19,6 +19,7 @@ config ARCH_SH7372
>        select CPU_V7
>        select SH_CLK_CPG
>        select ARCH_WANT_OPTIONAL_GPIOLIB
> +       select PM_GENERIC_DOMAINS

We want to support a single ARM binary for multiple boards, so this
should be enabled for all SoCs in mach-shmobile as a whole.

> --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> @@ -15,16 +15,97 @@
>  #include <linux/list.h>
>  #include <linux/err.h>
>  #include <linux/slab.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/platform_device.h>
>  #include <asm/system.h>
>  #include <asm/io.h>
>  #include <asm/tlbflush.h>
>  #include <mach/common.h>
> +#include <mach/sh7372.h>
>
>  #define SMFRAM 0xe6a70000
>  #define SYSTBCR 0xe6150024
>  #define SBAR 0xe6180020
>  #define APARMBAREA 0xe6f10020
>
> +#define SPDCR 0xe6180008
> +#define SWUCR 0xe6180014
> +#define PSTR 0xe6180080
> +
> +struct sh7372_domain_data {
> +       unsigned int bit_shift;
> +};

Is it possible to make struct sh7372_domain_data include struct
generic_pm_domain? I suspect so since these two data types seem to be
linked together. I guess container_of() can be used for conversion
between the types?

> +
> +static int pd_power_down(struct dev_pm_domain *domain)
> +{
> +       struct sh7372_domain_data *dd = domain->platform_data;
> +       unsigned int mask = 1 << dd->bit_shift;
> +
> +       if (__raw_readl(PSTR) & mask) {
> +               __raw_writel(mask, SPDCR);
> +
> +               while (__raw_readl(SPDCR) & mask) {}

This probably wants a cpu_relax() in the polling loop. I realize that
my prototype hack lacked that...

> +               pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> +                        mask, __raw_readl(PSTR));
> +       }
> +
> +       return 0;
> +}
> +
> +static int pd_power_up(struct dev_pm_domain *domain)
> +{
> +       struct sh7372_domain_data *dd = domain->platform_data;
> +       unsigned int mask = 1 << dd->bit_shift;
> +
> +       if (!(__raw_readl(PSTR) & mask)) {
> +               __raw_writel(mask, SWUCR);
> +
> +               while (__raw_readl(SWUCR) & mask) {}

Same cpu_relax() here.

> +static int __init sh7372_power_domains_init(void)
> +{
> +       sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
> +       return 0;
> +}
> +core_initcall(sh7372_power_domains_init);

This initcall is going to be executed regardless which SoC we're
running on. We only want it called for sh7372 though.

If you look at other SoC-specific code then you will notice that
initcalls are only used for functions in mach-shmobile/ that are
common for all SoCs implemented under mach-shmobile.

You most likely want to initialize from sh7372_pm_init(), but for that
to work you probably have to reorder your code inside mackerel_init().

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-14 13:12     ` Magnus Damm
@ 2011-06-14 21:16       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-14 21:16 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

[-- Attachment #1: Type: Text/Plain, Size: 4290 bytes --]

On Tuesday, June 14, 2011, Magnus Damm wrote:
> Hi Rafael,

Hi,

> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Use the generic power domains support introduced by the previous
> > patch to implement support for power domains on SH7372.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> 
> Thanks for your work on this. I just tried this on my Mackerel board,
> but I can't seem to get the pd_power_up() and pd_power_down()
> callbacks to be executed. It is probably a misconfiguration from my
> side.

They trigger for me e.g. after doing

# echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank

Attached is the .config I've been using.

> Here's some feedback on the sh7372-specific code:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> > @@ -19,6 +19,7 @@ config ARCH_SH7372
> >        select CPU_V7
> >        select SH_CLK_CPG
> >        select ARCH_WANT_OPTIONAL_GPIOLIB
> > +       select PM_GENERIC_DOMAINS
> 
> We want to support a single ARM binary for multiple boards,

Surely CONFIG_ARCH_SH7372 will be set in that binary?

> so this should be enabled for all SoCs in mach-shmobile as a whole.

OK, where exactly do you want me to move it?

> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> > @@ -15,16 +15,97 @@
> >  #include <linux/list.h>
> >  #include <linux/err.h>
> >  #include <linux/slab.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/platform_device.h>
> >  #include <asm/system.h>
> >  #include <asm/io.h>
> >  #include <asm/tlbflush.h>
> >  #include <mach/common.h>
> > +#include <mach/sh7372.h>
> >
> >  #define SMFRAM 0xe6a70000
> >  #define SYSTBCR 0xe6150024
> >  #define SBAR 0xe6180020
> >  #define APARMBAREA 0xe6f10020
> >
> > +#define SPDCR 0xe6180008
> > +#define SWUCR 0xe6180014
> > +#define PSTR 0xe6180080
> > +
> > +struct sh7372_domain_data {
> > +       unsigned int bit_shift;
> > +};
> 
> Is it possible to make struct sh7372_domain_data include struct
> generic_pm_domain?

It should be possible to do that.

Do I understand it correctly that you want one structure definition per
power domain instead of the two?

> I suspect so since these two data types seem to be linked together.
> I guess container_of() can be used for conversion between the types?

I don't think that will be necessary. :-)

> > +
> > +static int pd_power_down(struct dev_pm_domain *domain)
> > +{
> > +       struct sh7372_domain_data *dd = domain->platform_data;
> > +       unsigned int mask = 1 << dd->bit_shift;
> > +
> > +       if (__raw_readl(PSTR) & mask) {
> > +               __raw_writel(mask, SPDCR);
> > +
> > +               while (__raw_readl(SPDCR) & mask) {}
> 
> This probably wants a cpu_relax() in the polling loop. I realize that
> my prototype hack lacked that...

OK

> > +               pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > +                        mask, __raw_readl(PSTR));
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int pd_power_up(struct dev_pm_domain *domain)
> > +{
> > +       struct sh7372_domain_data *dd = domain->platform_data;
> > +       unsigned int mask = 1 << dd->bit_shift;
> > +
> > +       if (!(__raw_readl(PSTR) & mask)) {
> > +               __raw_writel(mask, SWUCR);
> > +
> > +               while (__raw_readl(SWUCR) & mask) {}
> 
> Same cpu_relax() here.

OK

> > +static int __init sh7372_power_domains_init(void)
> > +{
> > +       sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
> > +       return 0;
> > +}
> > +core_initcall(sh7372_power_domains_init);
> 
> This initcall is going to be executed regardless which SoC we're
> running on. We only want it called for sh7372 though.

OK

> If you look at other SoC-specific code then you will notice that
> initcalls are only used for functions in mach-shmobile/ that are
> common for all SoCs implemented under mach-shmobile.
> 
> You most likely want to initialize from sh7372_pm_init(), but for that
> to work you probably have to reorder your code inside mackerel_init().

OK, I'll figure out how to do the initialization correctly.

Thanks,
Rafael

[-- Attachment #2: mackerel-config --]
[-- Type: text/plain, Size: 44458 bytes --]

#
# Automatically generated make config: don't edit
# Linux/arm 3.0.0-rc2 Kernel Configuration
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_KTIME_SCALAR=y
CONFIG_HAVE_PROC_CPU=y
CONFIG_NO_IOPORT=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_VECTORS_BASE=0xffff0000
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
CONFIG_CONSTRUCTORS=y
CONFIG_HAVE_IRQ_WORK=y

#
# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_CROSS_COMPILE=""
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_KERNEL_GZIP=y
# CONFIG_KERNEL_LZMA is not set
# CONFIG_KERNEL_LZO is not set
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_FHANDLE is not set
# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
CONFIG_HAVE_GENERIC_HARDIRQS=y

#
# IRQ subsystem
#
CONFIG_GENERIC_HARDIRQS=y
CONFIG_HAVE_SPARSE_IRQ=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_SPARSE_IRQ=y

#
# RCU Subsystem
#
CONFIG_TINY_RCU=y
# CONFIG_PREEMPT_RCU is not set
# CONFIG_RCU_TRACE is not set
# CONFIG_TREE_RCU_TRACE is not set
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=16
# CONFIG_CGROUPS is not set
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_IPC_NS is not set
# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
# CONFIG_NET_NS is not set
# CONFIG_SCHED_AUTOGROUP is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="/home/rafael/src/arm/initramfs_data.cpio"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
CONFIG_RD_GZIP=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
CONFIG_RD_XZ=y
CONFIG_RD_LZO=y
CONFIG_INITRAMFS_COMPRESSION_NONE=y
# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set
# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set
# CONFIG_INITRAMFS_COMPRESSION_XZ is not set
# CONFIG_INITRAMFS_COMPRESSION_LZO is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
CONFIG_ANON_INODES=y
# CONFIG_EXPERT is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
# CONFIG_EMBEDDED is not set
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_PERF_USE_VMALLOC=y

#
# Kernel Performance Events And Counters
#
# CONFIG_PERF_EVENTS is not set
# CONFIG_PERF_COUNTERS is not set
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_PROFILING is not set
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_DMA_API_DEBUG=y

#
# GCOV-based kernel profiling
#
# CONFIG_GCOV_KERNEL is not set
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
# CONFIG_MODULES is not set
CONFIG_BLOCK=y
CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set

#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
# CONFIG_IOSCHED_DEADLINE is not set
# CONFIG_IOSCHED_CFQ is not set
CONFIG_DEFAULT_NOOP=y
CONFIG_DEFAULT_IOSCHED="noop"
# CONFIG_INLINE_SPIN_TRYLOCK is not set
# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
# CONFIG_INLINE_SPIN_LOCK is not set
# CONFIG_INLINE_SPIN_LOCK_BH is not set
# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
CONFIG_INLINE_SPIN_UNLOCK=y
# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
# CONFIG_INLINE_READ_TRYLOCK is not set
# CONFIG_INLINE_READ_LOCK is not set
# CONFIG_INLINE_READ_LOCK_BH is not set
# CONFIG_INLINE_READ_LOCK_IRQ is not set
# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
CONFIG_INLINE_READ_UNLOCK=y
# CONFIG_INLINE_READ_UNLOCK_BH is not set
CONFIG_INLINE_READ_UNLOCK_IRQ=y
# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
# CONFIG_INLINE_WRITE_TRYLOCK is not set
# CONFIG_INLINE_WRITE_LOCK is not set
# CONFIG_INLINE_WRITE_LOCK_BH is not set
# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
CONFIG_INLINE_WRITE_UNLOCK=y
# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
# CONFIG_MUTEX_SPIN_ON_OWNER is not set
CONFIG_FREEZER=y

#
# System Type
#
CONFIG_MMU=y
# CONFIG_ARCH_INTEGRATOR is not set
# CONFIG_ARCH_REALVIEW is not set
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_VEXPRESS is not set
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_BCMRING is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_CNS3XXX is not set
# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_MXC is not set
# CONFIG_ARCH_MXS is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
# CONFIG_ARCH_IXP23XX is not set
# CONFIG_ARCH_IXP2000 is not set
# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_DOVE is not set
# CONFIG_ARCH_KIRKWOOD is not set
# CONFIG_ARCH_LOKI is not set
# CONFIG_ARCH_LPC32XX is not set
# CONFIG_ARCH_MV78XX0 is not set
# CONFIG_ARCH_ORION5X is not set
# CONFIG_ARCH_MMP is not set
# CONFIG_ARCH_KS8695 is not set
# CONFIG_ARCH_W90X900 is not set
# CONFIG_ARCH_NUC93X is not set
# CONFIG_ARCH_TEGRA is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_MSM is not set
CONFIG_ARCH_SHMOBILE=y
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_S3C64XX is not set
# CONFIG_ARCH_S5P64X0 is not set
# CONFIG_ARCH_S5PC100 is not set
# CONFIG_ARCH_S5PV210 is not set
# CONFIG_ARCH_EXYNOS4 is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_TCC_926 is not set
# CONFIG_ARCH_U300 is not set
# CONFIG_ARCH_U8500 is not set
# CONFIG_ARCH_NOMADIK is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
# CONFIG_PLAT_SPEAR is not set
# CONFIG_ARCH_VT8500 is not set
# CONFIG_GPIO_PCA953X is not set
# CONFIG_KEYBOARD_GPIO_POLLED is not set

#
# System MMU
#

#
# SH-Mobile System Type
#
# CONFIG_ARCH_SH7367 is not set
# CONFIG_ARCH_SH7377 is not set
CONFIG_ARCH_SH7372=y
# CONFIG_ARCH_SH73A0 is not set

#
# SH-Mobile Board Type
#
# CONFIG_MACH_AP4EVB is not set
CONFIG_MACH_MACKEREL=y

#
# SH-Mobile System Configuration
#

#
# Memory configuration
#
CONFIG_MEMORY_START=0x40000000
CONFIG_MEMORY_SIZE=0x10000000

#
# Timer and clock configuration
#
CONFIG_SHMOBILE_TIMER_HZ=128
CONFIG_SH_TIMER_CMT=y
# CONFIG_SH_TIMER_TMU is not set
CONFIG_SH_CLK_CPG=y

#
# SuperH / SH-Mobile Driver Options
#

#
# Interrupt controller options
#
# CONFIG_INTC_USERIMASK is not set
# CONFIG_INTC_MAPPING_DEBUG is not set

#
# Processor Type
#
CONFIG_CPU_V7=y
CONFIG_CPU_32v6K=y
CONFIG_CPU_32v7=y
CONFIG_CPU_ABRT_EV7=y
CONFIG_CPU_PABRT_V7=y
CONFIG_CPU_CACHE_V7=y
CONFIG_CPU_CACHE_VIPT=y
CONFIG_CPU_COPY_V6=y
CONFIG_CPU_TLB_V7=y
CONFIG_CPU_HAS_ASID=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y

#
# Processor Features
#
CONFIG_ARM_THUMB=y
# CONFIG_ARM_THUMBEE is not set
# CONFIG_SWP_EMULATE is not set
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
CONFIG_CPU_BPREDICT_DISABLE=y
CONFIG_OUTER_CACHE=y
CONFIG_OUTER_CACHE_SYNC=y
CONFIG_CACHE_L2X0=y
CONFIG_CACHE_PL310=y
CONFIG_ARM_L1_CACHE_SHIFT=5
CONFIG_ARM_DMA_MEM_BUFFERABLE=y
CONFIG_CPU_HAS_PMU=y
CONFIG_MULTI_IRQ_HANDLER=y
CONFIG_ARM_ERRATA_430973=y
CONFIG_ARM_ERRATA_458693=y
# CONFIG_ARM_ERRATA_460075 is not set
# CONFIG_PL310_ERRATA_588369 is not set
# CONFIG_PL310_ERRATA_727915 is not set
# CONFIG_ARM_ERRATA_743622 is not set
CONFIG_ARM_ERRATA_753970=y
# CONFIG_ARM_ERRATA_754322 is not set

#
# Bus support
#
# CONFIG_PCI_SYSCALL is not set
# CONFIG_ARCH_SUPPORTS_MSI is not set
# CONFIG_PCCARD is not set

#
# Kernel Features
#
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
# CONFIG_SMP is not set
CONFIG_VMSPLIT_3G=y
# CONFIG_VMSPLIT_2G is not set
# CONFIG_VMSPLIT_1G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
CONFIG_HZ=128
# CONFIG_THUMB2_KERNEL is not set
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_HAVE_ARCH_PFN_VALID=y
# CONFIG_HIGHMEM is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_HAVE_MEMBLOCK=y
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_COMPACTION is not set
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
# CONFIG_KSM is not set
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_NEED_PER_CPU_KM=y
# CONFIG_CLEANCACHE is not set
CONFIG_FORCE_MAX_ZONEORDER=13
CONFIG_ALIGNMENT_TRAP=y
# CONFIG_UACCESS_WITH_MEMCPY is not set
# CONFIG_SECCOMP is not set
# CONFIG_CC_STACKPROTECTOR is not set
# CONFIG_DEPRECATED_PARAM_STRUCT is not set

#
# Boot options
#
CONFIG_USE_OF=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="console=tty0 console=ttySC0,115200 earlyprintk=sh-sci.0,115200 ignore_loglevel memblock=debug memchunk.veu0=4m no_console_suspend"
# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
# CONFIG_CMDLINE_EXTEND is not set
CONFIG_CMDLINE_FORCE=y
# CONFIG_XIP_KERNEL is not set
CONFIG_KEXEC=y
CONFIG_ATAGS_PROC=y
# CONFIG_CRASH_DUMP is not set
# CONFIG_AUTO_ZRELADDR is not set

#
# CPU Power Management
#
# CONFIG_CPU_IDLE is not set

#
# Floating point emulation
#

#
# At least one emulation must be selected
#
# CONFIG_VFP is not set

#
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set

#
# Power management options
#
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
CONFIG_PM_SLEEP=y
CONFIG_PM_RUNTIME=y
CONFIG_PM=y
CONFIG_PM_DEBUG=y
CONFIG_PM_ADVANCED_DEBUG=y
# CONFIG_PM_TEST_SUSPEND is not set
CONFIG_CAN_PM_TRACE=y
# CONFIG_APM_EMULATION is not set
CONFIG_PM_RUNTIME_CLK=y
CONFIG_PM_GENERIC_DOMAINS=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y

#
# Networking options
#
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM=y
# CONFIG_XFRM_USER is not set
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE_DEMUX is not set
# CONFIG_IP_MROUTE is not set
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_INET_XFRM_MODE_BEET=y
CONFIG_INET_LRO=y
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_RDS is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_L2TP is not set
# CONFIG_BRIDGE is not set
# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
# CONFIG_BATMAN_ADV is not set

#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
# CONFIG_WIRELESS is not set
# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
# CONFIG_CAIF is not set
# CONFIG_CEPH_LIB is not set

#
# Device Drivers
#

#
# Generic Driver Options
#
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
# CONFIG_DEVTMPFS is not set
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_REDBOOT_PARTS is not set
# CONFIG_MTD_CMDLINE_PARTS is not set
# CONFIG_MTD_AFS_PARTS is not set
CONFIG_MTD_OF_PARTS=y
# CONFIG_MTD_AR7_PARTS is not set

#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
# CONFIG_RFD_FTL is not set
# CONFIG_SSFDC is not set
# CONFIG_SM_FTL is not set
# CONFIG_MTD_OOPS is not set
# CONFIG_MTD_SWAP is not set

#
# RAM/ROM/Flash chip drivers
#
CONFIG_MTD_CFI=y
# CONFIG_MTD_JEDECPROBE is not set
CONFIG_MTD_GEN_PROBE=y
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
CONFIG_MTD_MAP_BANK_WIDTH_1=y
CONFIG_MTD_MAP_BANK_WIDTH_2=y
CONFIG_MTD_MAP_BANK_WIDTH_4=y
# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=y
# CONFIG_MTD_CFI_AMDSTD is not set
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set

#
# Mapping drivers for chip access
#
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_PHYSMAP=y
# CONFIG_MTD_PHYSMAP_COMPAT is not set
CONFIG_MTD_PHYSMAP_OF=y
# CONFIG_MTD_ARM_INTEGRATOR is not set
# CONFIG_MTD_PLATRAM is not set

#
# Self-contained MTD device drivers
#
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
# CONFIG_MTD_BLOCK2MTD is not set

#
# Disk-On-Chip Device Drivers
#
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
CONFIG_MTD_NAND_ECC=y
# CONFIG_MTD_NAND_ECC_SMC is not set
CONFIG_MTD_NAND=y
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
# CONFIG_MTD_NAND_ECC_BCH is not set
# CONFIG_MTD_SM_COMMON is not set
# CONFIG_MTD_NAND_MUSEUM_IDS is not set
# CONFIG_MTD_NAND_GPIO is not set
CONFIG_MTD_NAND_IDS=y
# CONFIG_MTD_NAND_DISKONCHIP is not set
# CONFIG_MTD_NAND_NANDSIM is not set
# CONFIG_MTD_NAND_PLATFORM is not set
# CONFIG_MTD_ALAUDA is not set
CONFIG_MTD_NAND_SH_FLCTL=y
# CONFIG_MTD_ONENAND is not set

#
# LPDDR flash memory drivers
#
# CONFIG_MTD_LPDDR is not set
# CONFIG_MTD_UBI is not set
CONFIG_DTC=y
CONFIG_OF=y

#
# Device Tree and Open Firmware support
#
CONFIG_PROC_DEVICETREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_IRQ=y
CONFIG_OF_DEVICE=y
CONFIG_OF_GPIO=y
CONFIG_OF_I2C=y
CONFIG_OF_NET=y
CONFIG_OF_MDIO=y
# CONFIG_PARPORT is not set
# CONFIG_BLK_DEV is not set
# CONFIG_SENSORS_LIS3LV02D is not set
# CONFIG_MISC_DEVICES is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set

#
# SCSI device support
#
CONFIG_SCSI_MOD=y
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
# CONFIG_SCSI_DMA is not set
# CONFIG_SCSI_NETLINK is not set
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_VETH is not set
CONFIG_MII=y
CONFIG_PHYLIB=y

#
# MII PHY device drivers
#
# CONFIG_MARVELL_PHY is not set
# CONFIG_DAVICOM_PHY is not set
# CONFIG_QSEMI_PHY is not set
# CONFIG_LXT_PHY is not set
# CONFIG_CICADA_PHY is not set
# CONFIG_VITESSE_PHY is not set
# CONFIG_SMSC_PHY is not set
# CONFIG_BROADCOM_PHY is not set
# CONFIG_BCM63XX_PHY is not set
# CONFIG_ICPLUS_PHY is not set
# CONFIG_REALTEK_PHY is not set
# CONFIG_NATIONAL_PHY is not set
# CONFIG_STE10XP is not set
# CONFIG_LSI_ET1011C_PHY is not set
# CONFIG_MICREL_PHY is not set
# CONFIG_FIXED_PHY is not set
# CONFIG_MDIO_BITBANG is not set
CONFIG_NET_ETHERNET=y
# CONFIG_AX88796 is not set
# CONFIG_SMC91X is not set
# CONFIG_DM9000 is not set
# CONFIG_ETHOC is not set
# CONFIG_SMC911X is not set
CONFIG_SMSC911X=y
# CONFIG_SMSC911X_ARCH_HOOKS is not set
# CONFIG_DNET is not set
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
# CONFIG_KS8851_MLL is not set
# CONFIG_FTMAC100 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
# CONFIG_WLAN is not set

#
# Enable WiMAX (Networking options) to see the WiMAX drivers
#

#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
CONFIG_USB_USBNET=y
# CONFIG_USB_NET_AX8817X is not set
# CONFIG_USB_NET_CDCETHER is not set
# CONFIG_USB_NET_CDC_EEM is not set
CONFIG_USB_NET_CDC_NCM=y
CONFIG_USB_NET_DM9601=y
# CONFIG_USB_NET_SMSC75XX is not set
# CONFIG_USB_NET_SMSC95XX is not set
# CONFIG_USB_NET_GL620A is not set
# CONFIG_USB_NET_NET1080 is not set
# CONFIG_USB_NET_PLUSB is not set
# CONFIG_USB_NET_MCS7830 is not set
# CONFIG_USB_NET_RNDIS_HOST is not set
# CONFIG_USB_NET_CDC_SUBSET is not set
# CONFIG_USB_NET_ZAURUS is not set
# CONFIG_USB_NET_CX82310_ETH is not set
# CONFIG_USB_NET_INT51X1 is not set
# CONFIG_USB_IPHETH is not set
# CONFIG_USB_SIERRA_NET is not set
# CONFIG_WAN is not set

#
# CAIF transport drivers
#
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
# CONFIG_ISDN is not set
# CONFIG_PHONE is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set
# CONFIG_INPUT_POLLDEV is not set
# CONFIG_INPUT_SPARSEKMAP is not set

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5588 is not set
# CONFIG_KEYBOARD_ADP5589 is not set
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_KEYBOARD_TCA6416 is not set
# CONFIG_KEYBOARD_MATRIX is not set
CONFIG_KEYBOARD_LM8323=y
# CONFIG_KEYBOARD_MAX7359 is not set
# CONFIG_KEYBOARD_MCS is not set
# CONFIG_KEYBOARD_MPR121 is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_OPENCORES is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
CONFIG_KEYBOARD_SH_KEYSC=y
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
# CONFIG_TOUCHSCREEN_BU21013 is not set
# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
# CONFIG_TOUCHSCREEN_DYNAPRO is not set
# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
# CONFIG_TOUCHSCREEN_EETI is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
# CONFIG_TOUCHSCREEN_MAX11801 is not set
# CONFIG_TOUCHSCREEN_MCS5000 is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
CONFIG_TOUCHSCREEN_TSC2007=y
# CONFIG_TOUCHSCREEN_W90X900 is not set
# CONFIG_TOUCHSCREEN_ST1232 is not set
# CONFIG_TOUCHSCREEN_TPS6507X is not set
# CONFIG_INPUT_MISC is not set

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_RAW is not set
# CONFIG_SERIO_ALTERA_PS2 is not set
# CONFIG_SERIO_PS2MULT is not set
# CONFIG_GAMEPORT is not set

#
# Character devices
#
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_VT_HW_CONSOLE_BINDING is not set
CONFIG_UNIX98_PTYS=y
# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_N_GSM is not set
# CONFIG_TRACE_SINK is not set
CONFIG_DEVKMEM=y

#
# Serial drivers
#
# CONFIG_SERIAL_8250 is not set

#
# Non-8250 serial port support
#
CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_SH_SCI_NR_UARTS=8
CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_TIMBERDALE is not set
# CONFIG_SERIAL_ALTERA_JTAGUART is not set
# CONFIG_SERIAL_ALTERA_UART is not set
# CONFIG_SERIAL_XILINX_PS_UART is not set
# CONFIG_HVC_DCC is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_RAMOOPS is not set
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
# CONFIG_I2C_CHARDEV is not set
# CONFIG_I2C_MUX is not set
CONFIG_I2C_HELPER_AUTO=y

#
# I2C Hardware Bus support
#

#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_GPIO is not set
# CONFIG_I2C_OCORES is not set
# CONFIG_I2C_PCA_PLATFORM is not set
# CONFIG_I2C_PXA_PCI is not set
CONFIG_I2C_SH_MOBILE=y
# CONFIG_I2C_SIMTEC is not set
# CONFIG_I2C_XILINX is not set

#
# External I2C/SMBus adapter drivers
#
CONFIG_I2C_DIOLAN_U2C=y
# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_I2C_TAOS_EVM is not set
# CONFIG_I2C_TINY_USB is not set

#
# Other I2C/SMBus bus drivers
#
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_SPI is not set

#
# PPS support
#
# CONFIG_PPS is not set

#
# PPS generators support
#

#
# PTP clock support
#

#
# Enable Device Drivers -> PPS to see the PTP clock options.
#
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
CONFIG_ARCH_REQUIRE_GPIOLIB=y
CONFIG_GPIOLIB=y
# CONFIG_DEBUG_GPIO is not set
# CONFIG_GPIO_SYSFS is not set

#
# Memory mapped GPIO drivers:
#
# CONFIG_GPIO_BASIC_MMIO is not set
# CONFIG_GPIO_IT8761E is not set

#
# I2C GPIO expanders:
#
# CONFIG_GPIO_MAX7300 is not set
# CONFIG_GPIO_MAX732X is not set
# CONFIG_GPIO_PCF857X is not set
# CONFIG_GPIO_SX150X is not set
# CONFIG_GPIO_ADP5588 is not set

#
# PCI GPIO expanders:
#

#
# SPI GPIO expanders:
#

#
# AC97 GPIO expanders:
#

#
# MODULbus GPIO expanders:
#
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
# CONFIG_THERMAL is not set
# CONFIG_WATCHDOG is not set
CONFIG_SSB_POSSIBLE=y

#
# Sonics Silicon Backplane
#
# CONFIG_SSB is not set
CONFIG_BCMA_POSSIBLE=y

#
# Broadcom specific AMBA
#
# CONFIG_BCMA is not set
CONFIG_MFD_SUPPORT=y
# CONFIG_MFD_CORE is not set
# CONFIG_MFD_88PM860X is not set
# CONFIG_MFD_SM501 is not set
# CONFIG_MFD_ASIC3 is not set
# CONFIG_HTC_EGPIO is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_HTC_I2CPLD is not set
# CONFIG_TPS6105X is not set
# CONFIG_TPS65010 is not set
# CONFIG_TPS6507X is not set
# CONFIG_MFD_TPS6586X is not set
# CONFIG_TWL4030_CORE is not set
# CONFIG_MFD_STMPE is not set
# CONFIG_MFD_TC3589X is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_MFD_T7L66XB is not set
# CONFIG_MFD_TC6387XB is not set
# CONFIG_MFD_TC6393XB is not set
# CONFIG_PMIC_DA903X is not set
# CONFIG_PMIC_ADP5520 is not set
# CONFIG_MFD_MAX8925 is not set
# CONFIG_MFD_MAX8997 is not set
# CONFIG_MFD_MAX8998 is not set
# CONFIG_MFD_WM8400 is not set
# CONFIG_MFD_WM831X_I2C is not set
# CONFIG_MFD_WM8350_I2C is not set
# CONFIG_MFD_WM8994 is not set
# CONFIG_MFD_PCF50633 is not set
# CONFIG_ABX500_CORE is not set
# CONFIG_MFD_WL1273_CORE is not set
# CONFIG_MFD_TPS65910 is not set
# CONFIG_REGULATOR is not set
CONFIG_MEDIA_SUPPORT=y

#
# Multimedia core support
#
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_COMMON=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_DVB_CORE is not set
CONFIG_VIDEO_MEDIA=y

#
# Multimedia drivers
#
CONFIG_RC_CORE=y
CONFIG_LIRC=y
CONFIG_RC_MAP=y
CONFIG_IR_NEC_DECODER=y
CONFIG_IR_RC5_DECODER=y
CONFIG_IR_RC6_DECODER=y
CONFIG_IR_JVC_DECODER=y
CONFIG_IR_SONY_DECODER=y
CONFIG_IR_RC5_SZ_DECODER=y
CONFIG_IR_LIRC_CODEC=y
# CONFIG_IR_IMON is not set
# CONFIG_IR_MCEUSB is not set
# CONFIG_IR_REDRAT3 is not set
# CONFIG_IR_STREAMZAP is not set
# CONFIG_RC_LOOPBACK is not set
CONFIG_MEDIA_TUNER=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
CONFIG_MEDIA_TUNER_SIMPLE=y
CONFIG_MEDIA_TUNER_TDA8290=y
CONFIG_MEDIA_TUNER_TDA827X=y
CONFIG_MEDIA_TUNER_TDA18271=y
CONFIG_MEDIA_TUNER_TDA9887=y
CONFIG_MEDIA_TUNER_TEA5761=y
CONFIG_MEDIA_TUNER_TEA5767=y
CONFIG_MEDIA_TUNER_MT20XX=y
CONFIG_MEDIA_TUNER_XC2028=y
CONFIG_MEDIA_TUNER_XC5000=y
CONFIG_MEDIA_TUNER_MC44S803=y
CONFIG_VIDEO_V4L2=y
CONFIG_VIDEOBUF_GEN=y
CONFIG_VIDEOBUF2_CORE=y
CONFIG_VIDEOBUF2_MEMOPS=y
CONFIG_VIDEOBUF2_DMA_CONTIG=y
CONFIG_VIDEO_CAPTURE_DRIVERS=y
# CONFIG_VIDEO_ADV_DEBUG is not set
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_VIDEO_IR_I2C=y

#
# Audio decoders, processors and mixers
#

#
# RDS decoders
#

#
# Video decoders
#

#
# Video and audio decoders
#

#
# MPEG video encoders
#

#
# Video encoders
#

#
# Camera sensor devices
#

#
# Video improvement chips
#

#
# Miscelaneous helper chips
#
# CONFIG_VIDEO_SH_VOU is not set
# CONFIG_VIDEO_VIVI is not set
# CONFIG_VIDEO_CPIA2 is not set
# CONFIG_VIDEO_TIMBERDALE is not set
# CONFIG_VIDEO_SR030PC30 is not set
CONFIG_VIDEO_NOON010PC30=y
# CONFIG_VIDEO_M5MOLS is not set
CONFIG_SOC_CAMERA=y
CONFIG_SOC_CAMERA_IMX074=y
# CONFIG_SOC_CAMERA_MT9M001 is not set
# CONFIG_SOC_CAMERA_MT9M111 is not set
# CONFIG_SOC_CAMERA_MT9T031 is not set
# CONFIG_SOC_CAMERA_MT9T112 is not set
# CONFIG_SOC_CAMERA_MT9V022 is not set
# CONFIG_SOC_CAMERA_RJ54N1 is not set
# CONFIG_SOC_CAMERA_TW9910 is not set
CONFIG_SOC_CAMERA_PLATFORM=y
# CONFIG_SOC_CAMERA_OV2640 is not set
# CONFIG_SOC_CAMERA_OV6650 is not set
# CONFIG_SOC_CAMERA_OV772X is not set
# CONFIG_SOC_CAMERA_OV9640 is not set
CONFIG_SOC_CAMERA_OV9740=y
CONFIG_VIDEO_SH_MOBILE_CSI2=y
CONFIG_VIDEO_SH_MOBILE_CEU=y
# CONFIG_V4L_USB_DRIVERS is not set
# CONFIG_V4L_MEM2MEM_DRIVERS is not set
# CONFIG_RADIO_ADAPTERS is not set

#
# Graphics support
#
# CONFIG_DRM is not set
# CONFIG_VGASTATE is not set
# CONFIG_VIDEO_OUTPUT_CONTROL is not set
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
# CONFIG_FB_BOOT_VESA_SUPPORT is not set
# CONFIG_FB_CFB_FILLRECT is not set
# CONFIG_FB_CFB_COPYAREA is not set
# CONFIG_FB_CFB_IMAGEBLIT is not set
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_IMAGEBLIT=y
# CONFIG_FB_FOREIGN_ENDIAN is not set
CONFIG_FB_SYS_FOPS=y
# CONFIG_FB_WMT_GE_ROPS is not set
CONFIG_FB_DEFERRED_IO=y
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
CONFIG_FB_BACKLIGHT=y
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set

#
# Frame buffer hardware drivers
#
# CONFIG_FB_S1D13XXX is not set
CONFIG_FB_SH_MOBILE_LCDC=y
CONFIG_FB_SH_MOBILE_HDMI=y
CONFIG_FB_SH_MOBILE_MERAM=y
# CONFIG_FB_UDL is not set
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FB_METRONOME is not set
# CONFIG_FB_BROADSHEET is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_GENERIC=y
CONFIG_BACKLIGHT_ADP8860=y

#
# Display device support
#
# CONFIG_DISPLAY_SUPPORT is not set

#
# Console display driver support
#
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_6x11 is not set
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_SUN8x16 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_10x18 is not set
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
CONFIG_LOGO_LINUX_VGA16=y
# CONFIG_LOGO_LINUX_CLUT224 is not set
CONFIG_SOUND=y
CONFIG_SOUND_OSS_CORE=y
CONFIG_SOUND_OSS_CORE_PRECLAIM=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_JACK=y
# CONFIG_SND_SEQUENCER is not set
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_SND_PCM_OSS_PLUGINS=y
# CONFIG_SND_DYNAMIC_MINORS is not set
CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
# CONFIG_SND_RAWMIDI_SEQ is not set
# CONFIG_SND_OPL3_LIB_SEQ is not set
# CONFIG_SND_OPL4_LIB_SEQ is not set
# CONFIG_SND_SBAWE_SEQ is not set
# CONFIG_SND_EMU10K1_SEQ is not set
# CONFIG_SND_DRIVERS is not set
# CONFIG_SND_ARM is not set
# CONFIG_SND_USB is not set
CONFIG_SND_SOC=y
# CONFIG_SND_SOC_CACHE_LZO is not set

#
# SoC Audio support for SuperH
#
CONFIG_SND_SOC_SH4_FSI=y
CONFIG_SND_FSI_AK4642=y
# CONFIG_SND_FSI_DA7210 is not set
# CONFIG_SND_FSI_HDMI is not set
CONFIG_SND_SOC_I2C_AND_SPI=y
# CONFIG_SND_SOC_ALL_CODECS is not set
CONFIG_SND_SOC_AK4642=y
# CONFIG_SOUND_PRIME is not set
# CONFIG_HID_SUPPORT is not set
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
# CONFIG_USB_ARCH_HAS_OHCI is not set
# CONFIG_USB_ARCH_HAS_EHCI is not set
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set
# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set

#
# Miscellaneous USB options
#
# CONFIG_USB_DEVICEFS is not set
CONFIG_USB_DEVICE_CLASS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_MON is not set
# CONFIG_USB_WUSB is not set
# CONFIG_USB_WUSB_CBAF is not set

#
# USB Host Controller Drivers
#
# CONFIG_USB_C67X00_HCD is not set
# CONFIG_USB_OXU210HP_HCD is not set
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1760_HCD is not set
# CONFIG_USB_ISP1362_HCD is not set
# CONFIG_USB_SL811_HCD is not set
CONFIG_USB_R8A66597_HCD=y
# CONFIG_USB_HWA_HCD is not set
# CONFIG_USB_MUSB_HDRC is not set
CONFIG_USB_RENESAS_USBHS=y

#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
# CONFIG_USB_PRINTER is not set
# CONFIG_USB_WDM is not set
# CONFIG_USB_TMC is not set

#
# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
#

#
# also be needed; see USB_STORAGE Help for more info
#
# CONFIG_USB_LIBUSUAL is not set

#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set

#
# USB port drivers
#
# CONFIG_USB_SERIAL is not set

#
# USB Miscellaneous drivers
#
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_ADUTUX is not set
# CONFIG_USB_SEVSEG is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
# CONFIG_USB_LED is not set
# CONFIG_USB_CYPRESS_CY7C63 is not set
# CONFIG_USB_CYTHERM is not set
# CONFIG_USB_IDMOUSE is not set
# CONFIG_USB_FTDI_ELAN is not set
# CONFIG_USB_APPLEDISPLAY is not set
# CONFIG_USB_LD is not set
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_IOWARRIOR is not set
# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
# CONFIG_USB_YUREX is not set
# CONFIG_USB_GADGET is not set

#
# OTG and related infrastructure
#
# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_USB_ULPI is not set
# CONFIG_NOP_USB_XCEIV is not set
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
# CONFIG_MMC_CLKGATE is not set

#
# MMC/SD/SDIO Card Drivers
#
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_MINORS=8
CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_SDIO_UART is not set
# CONFIG_MMC_TEST is not set

#
# MMC/SD/SDIO Host Controller Drivers
#
# CONFIG_MMC_SDHCI is not set
CONFIG_MMC_TMIO_CORE=y
CONFIG_MMC_SDHI=y
# CONFIG_MMC_DW is not set
CONFIG_MMC_SH_MMCIF=y
CONFIG_MMC_VUB300=y
# CONFIG_MMC_USHC is not set
# CONFIG_MEMSTICK is not set
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y

#
# LED drivers
#
CONFIG_LEDS_LM3530=y
CONFIG_LEDS_PCA9532=y
# CONFIG_LEDS_PCA9532_GPIO is not set
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_GPIO_PLATFORM=y
CONFIG_LEDS_GPIO_OF=y
CONFIG_LEDS_LP3944=y
CONFIG_LEDS_LP5521=y
CONFIG_LEDS_LP5523=y
CONFIG_LEDS_PCA955X=y
CONFIG_LEDS_BD2802=y
CONFIG_LEDS_LT3593=y
CONFIG_LEDS_TRIGGERS=y

#
# LED Triggers
#
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y

#
# iptables trigger is under Netfilter config (LED target)
#
# CONFIG_NFC_DEVICES is not set
# CONFIG_ACCESSIBILITY is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# CONFIG_RTC_DEBUG is not set

#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
CONFIG_RTC_INTF_DEV=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
# CONFIG_RTC_DRV_TEST is not set

#
# I2C RTC drivers
#
# CONFIG_RTC_DRV_DS1307 is not set
# CONFIG_RTC_DRV_DS1374 is not set
# CONFIG_RTC_DRV_DS1672 is not set
# CONFIG_RTC_DRV_DS3232 is not set
# CONFIG_RTC_DRV_MAX6900 is not set
CONFIG_RTC_DRV_RS5C372=y
# CONFIG_RTC_DRV_ISL1208 is not set
# CONFIG_RTC_DRV_ISL12022 is not set
# CONFIG_RTC_DRV_X1205 is not set
# CONFIG_RTC_DRV_PCF8563 is not set
# CONFIG_RTC_DRV_PCF8583 is not set
# CONFIG_RTC_DRV_M41T80 is not set
# CONFIG_RTC_DRV_BQ32K is not set
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
# CONFIG_RTC_DRV_RX8581 is not set
# CONFIG_RTC_DRV_RX8025 is not set
# CONFIG_RTC_DRV_EM3027 is not set
# CONFIG_RTC_DRV_RV3029C2 is not set

#
# SPI RTC drivers
#

#
# Platform RTC drivers
#
# CONFIG_RTC_DRV_CMOS is not set
# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
# CONFIG_RTC_DRV_MSM6242 is not set
# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_RP5C01 is not set
# CONFIG_RTC_DRV_V3020 is not set

#
# on-CPU RTC drivers
#
CONFIG_DMADEVICES=y
# CONFIG_DMADEVICES_DEBUG is not set

#
# DMA Devices
#
# CONFIG_DW_DMAC is not set
# CONFIG_SH_DMAE is not set
# CONFIG_TIMB_DMA is not set
# CONFIG_AUXDISPLAY is not set
CONFIG_UIO=y
# CONFIG_UIO_PDRV is not set
CONFIG_UIO_PDRV_GENIRQ=y
# CONFIG_STAGING is not set
CONFIG_CLKDEV_LOOKUP=y

#
# File systems
#
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_NILFS2_FS is not set
# CONFIG_FS_POSIX_ACL is not set
CONFIG_FILE_LOCKING=y
CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
CONFIG_INOTIFY_USER=y
# CONFIG_FANOTIFY is not set
# CONFIG_QUOTA is not set
# CONFIG_QUOTACTL is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set

#
# Caches
#
# CONFIG_FSCACHE is not set

#
# CD-ROM/DVD Filesystems
#
# CONFIG_ISO9660_FS is not set
# CONFIG_UDF_FS is not set

#
# DOS/FAT/NT Filesystems
#
# CONFIG_MSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_NTFS_FS is not set

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_TMPFS_XATTR is not set
# CONFIG_HUGETLB_PAGE is not set
# CONFIG_CONFIGFS_FS is not set
CONFIG_MISC_FILESYSTEMS=y
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_JFFS2_FS is not set
# CONFIG_LOGFS is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
# CONFIG_PSTORE is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
CONFIG_NETWORK_FILESYSTEMS=y
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_CEPH_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set

#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
# CONFIG_NLS_ASCII is not set
# CONFIG_NLS_ISO8859_1 is not set
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set

#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
CONFIG_ENABLE_WARN_DEPRECATED=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_FRAME_WARN=1024
CONFIG_MAGIC_SYSRQ=y
# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_DEBUG_FS=y
# CONFIG_HEADERS_CHECK is not set
# CONFIG_DEBUG_SECTION_MISMATCH is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_LOCKUP_DETECTOR=y
# CONFIG_HARDLOCKUP_DETECTOR is not set
# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
# CONFIG_DETECT_HUNG_TASK is not set
# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
# CONFIG_TIMER_STATS is not set
# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_KMEMLEAK is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_PROVE_LOCKING is not set
# CONFIG_SPARSE_RCU_POINTER is not set
# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_WRITECOUNT is not set
CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_LIST is not set
# CONFIG_TEST_LIST_SORT is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_DEBUG_CREDENTIALS is not set
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
# CONFIG_SYSCTL_SYSCALL_CHECK is not set
# CONFIG_DEBUG_PAGEALLOC is not set
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_TRACING_SUPPORT=y
# CONFIG_FTRACE is not set
CONFIG_DYNAMIC_DEBUG=y
# CONFIG_DMA_API_DEBUG is not set
# CONFIG_ATOMIC64_SELFTEST is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
# CONFIG_TEST_KSTRTOX is not set
# CONFIG_STRICT_DEVMEM is not set
CONFIG_ARM_UNWIND=y
# CONFIG_DEBUG_USER is not set
# CONFIG_DEBUG_LL is not set
# CONFIG_OC_ETM is not set

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SECURITY is not set
# CONFIG_SECURITYFS is not set
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_DEFAULT_SECURITY=""
CONFIG_CRYPTO=y

#
# Crypto core or helper
#
# CONFIG_CRYPTO_FIPS is not set
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
# CONFIG_CRYPTO_MANAGER is not set
# CONFIG_CRYPTO_MANAGER2 is not set
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_CRYPTD is not set
# CONFIG_CRYPTO_AUTHENC is not set

#
# Authenticated Encryption with Associated Data
#
# CONFIG_CRYPTO_CCM is not set
# CONFIG_CRYPTO_GCM is not set
# CONFIG_CRYPTO_SEQIV is not set

#
# Block modes
#
# CONFIG_CRYPTO_CBC is not set
# CONFIG_CRYPTO_CTR is not set
# CONFIG_CRYPTO_CTS is not set
# CONFIG_CRYPTO_ECB is not set
# CONFIG_CRYPTO_LRW is not set
# CONFIG_CRYPTO_PCBC is not set
# CONFIG_CRYPTO_XTS is not set

#
# Hash modes
#
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_XCBC is not set
# CONFIG_CRYPTO_VMAC is not set

#
# Digest
#
# CONFIG_CRYPTO_CRC32C is not set
# CONFIG_CRYPTO_GHASH is not set
# CONFIG_CRYPTO_MD4 is not set
# CONFIG_CRYPTO_MD5 is not set
# CONFIG_CRYPTO_MICHAEL_MIC is not set
# CONFIG_CRYPTO_RMD128 is not set
# CONFIG_CRYPTO_RMD160 is not set
# CONFIG_CRYPTO_RMD256 is not set
# CONFIG_CRYPTO_RMD320 is not set
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_SHA512 is not set
# CONFIG_CRYPTO_TGR192 is not set
# CONFIG_CRYPTO_WP512 is not set

#
# Ciphers
#
CONFIG_CRYPTO_AES=y
# CONFIG_CRYPTO_ANUBIS is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_CAMELLIA is not set
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST6 is not set
# CONFIG_CRYPTO_DES is not set
# CONFIG_CRYPTO_FCRYPT is not set
# CONFIG_CRYPTO_KHAZAD is not set
# CONFIG_CRYPTO_SALSA20 is not set
# CONFIG_CRYPTO_SEED is not set
# CONFIG_CRYPTO_SERPENT is not set
# CONFIG_CRYPTO_TEA is not set
# CONFIG_CRYPTO_TWOFISH is not set

#
# Compression
#
# CONFIG_CRYPTO_DEFLATE is not set
# CONFIG_CRYPTO_ZLIB is not set
# CONFIG_CRYPTO_LZO is not set

#
# Random Number Generation
#
CONFIG_CRYPTO_ANSI_CPRNG=y
# CONFIG_CRYPTO_USER_API_HASH is not set
# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
CONFIG_CRYPTO_HW=y
# CONFIG_BINARY_PRINTF is not set

#
# Library routines
#
CONFIG_BITREVERSE=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_XZ_DEC=y
CONFIG_XZ_DEC_X86=y
CONFIG_XZ_DEC_POWERPC=y
CONFIG_XZ_DEC_IA64=y
CONFIG_XZ_DEC_ARM=y
CONFIG_XZ_DEC_ARMTHUMB=y
CONFIG_XZ_DEC_SPARC=y
CONFIG_XZ_DEC_BCJ=y
# CONFIG_XZ_DEC_TEST is not set
CONFIG_DECOMPRESS_GZIP=y
CONFIG_DECOMPRESS_BZIP2=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_DECOMPRESS_XZ=y
CONFIG_DECOMPRESS_LZO=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_DMA=y
CONFIG_NLATTR=y
# CONFIG_AVERAGE is not set

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
@ 2011-06-14 21:16       ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-14 21:16 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

[-- Attachment #1: Type: Text/Plain, Size: 4290 bytes --]

On Tuesday, June 14, 2011, Magnus Damm wrote:
> Hi Rafael,

Hi,

> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Use the generic power domains support introduced by the previous
> > patch to implement support for power domains on SH7372.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> 
> Thanks for your work on this. I just tried this on my Mackerel board,
> but I can't seem to get the pd_power_up() and pd_power_down()
> callbacks to be executed. It is probably a misconfiguration from my
> side.

They trigger for me e.g. after doing

# echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank

Attached is the .config I've been using.

> Here's some feedback on the sh7372-specific code:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> > @@ -19,6 +19,7 @@ config ARCH_SH7372
> >        select CPU_V7
> >        select SH_CLK_CPG
> >        select ARCH_WANT_OPTIONAL_GPIOLIB
> > +       select PM_GENERIC_DOMAINS
> 
> We want to support a single ARM binary for multiple boards,

Surely CONFIG_ARCH_SH7372 will be set in that binary?

> so this should be enabled for all SoCs in mach-shmobile as a whole.

OK, where exactly do you want me to move it?

> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> > @@ -15,16 +15,97 @@
> >  #include <linux/list.h>
> >  #include <linux/err.h>
> >  #include <linux/slab.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/platform_device.h>
> >  #include <asm/system.h>
> >  #include <asm/io.h>
> >  #include <asm/tlbflush.h>
> >  #include <mach/common.h>
> > +#include <mach/sh7372.h>
> >
> >  #define SMFRAM 0xe6a70000
> >  #define SYSTBCR 0xe6150024
> >  #define SBAR 0xe6180020
> >  #define APARMBAREA 0xe6f10020
> >
> > +#define SPDCR 0xe6180008
> > +#define SWUCR 0xe6180014
> > +#define PSTR 0xe6180080
> > +
> > +struct sh7372_domain_data {
> > +       unsigned int bit_shift;
> > +};
> 
> Is it possible to make struct sh7372_domain_data include struct
> generic_pm_domain?

It should be possible to do that.

Do I understand it correctly that you want one structure definition per
power domain instead of the two?

> I suspect so since these two data types seem to be linked together.
> I guess container_of() can be used for conversion between the types?

I don't think that will be necessary. :-)

> > +
> > +static int pd_power_down(struct dev_pm_domain *domain)
> > +{
> > +       struct sh7372_domain_data *dd = domain->platform_data;
> > +       unsigned int mask = 1 << dd->bit_shift;
> > +
> > +       if (__raw_readl(PSTR) & mask) {
> > +               __raw_writel(mask, SPDCR);
> > +
> > +               while (__raw_readl(SPDCR) & mask) {}
> 
> This probably wants a cpu_relax() in the polling loop. I realize that
> my prototype hack lacked that...

OK

> > +               pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > +                        mask, __raw_readl(PSTR));
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int pd_power_up(struct dev_pm_domain *domain)
> > +{
> > +       struct sh7372_domain_data *dd = domain->platform_data;
> > +       unsigned int mask = 1 << dd->bit_shift;
> > +
> > +       if (!(__raw_readl(PSTR) & mask)) {
> > +               __raw_writel(mask, SWUCR);
> > +
> > +               while (__raw_readl(SWUCR) & mask) {}
> 
> Same cpu_relax() here.

OK

> > +static int __init sh7372_power_domains_init(void)
> > +{
> > +       sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
> > +       return 0;
> > +}
> > +core_initcall(sh7372_power_domains_init);
> 
> This initcall is going to be executed regardless which SoC we're
> running on. We only want it called for sh7372 though.

OK

> If you look at other SoC-specific code then you will notice that
> initcalls are only used for functions in mach-shmobile/ that are
> common for all SoCs implemented under mach-shmobile.
> 
> You most likely want to initialize from sh7372_pm_init(), but for that
> to work you probably have to reorder your code inside mackerel_init().

OK, I'll figure out how to do the initialization correctly.

Thanks,
Rafael

[-- Attachment #2: mackerel-config --]
[-- Type: text/plain, Size: 44458 bytes --]

#
# Automatically generated make config: don't edit
# Linux/arm 3.0.0-rc2 Kernel Configuration
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_KTIME_SCALAR=y
CONFIG_HAVE_PROC_CPU=y
CONFIG_NO_IOPORT=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_VECTORS_BASE=0xffff0000
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
CONFIG_CONSTRUCTORS=y
CONFIG_HAVE_IRQ_WORK=y

#
# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_CROSS_COMPILE=""
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_KERNEL_GZIP=y
# CONFIG_KERNEL_LZMA is not set
# CONFIG_KERNEL_LZO is not set
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_FHANDLE is not set
# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
CONFIG_HAVE_GENERIC_HARDIRQS=y

#
# IRQ subsystem
#
CONFIG_GENERIC_HARDIRQS=y
CONFIG_HAVE_SPARSE_IRQ=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_SPARSE_IRQ=y

#
# RCU Subsystem
#
CONFIG_TINY_RCU=y
# CONFIG_PREEMPT_RCU is not set
# CONFIG_RCU_TRACE is not set
# CONFIG_TREE_RCU_TRACE is not set
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=16
# CONFIG_CGROUPS is not set
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_IPC_NS is not set
# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
# CONFIG_NET_NS is not set
# CONFIG_SCHED_AUTOGROUP is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="/home/rafael/src/arm/initramfs_data.cpio"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
CONFIG_RD_GZIP=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
CONFIG_RD_XZ=y
CONFIG_RD_LZO=y
CONFIG_INITRAMFS_COMPRESSION_NONE=y
# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set
# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set
# CONFIG_INITRAMFS_COMPRESSION_XZ is not set
# CONFIG_INITRAMFS_COMPRESSION_LZO is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
CONFIG_ANON_INODES=y
# CONFIG_EXPERT is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
# CONFIG_EMBEDDED is not set
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_PERF_USE_VMALLOC=y

#
# Kernel Performance Events And Counters
#
# CONFIG_PERF_EVENTS is not set
# CONFIG_PERF_COUNTERS is not set
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_PROFILING is not set
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_DMA_API_DEBUG=y

#
# GCOV-based kernel profiling
#
# CONFIG_GCOV_KERNEL is not set
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
# CONFIG_MODULES is not set
CONFIG_BLOCK=y
CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set

#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
# CONFIG_IOSCHED_DEADLINE is not set
# CONFIG_IOSCHED_CFQ is not set
CONFIG_DEFAULT_NOOP=y
CONFIG_DEFAULT_IOSCHED="noop"
# CONFIG_INLINE_SPIN_TRYLOCK is not set
# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
# CONFIG_INLINE_SPIN_LOCK is not set
# CONFIG_INLINE_SPIN_LOCK_BH is not set
# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
CONFIG_INLINE_SPIN_UNLOCK=y
# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
# CONFIG_INLINE_READ_TRYLOCK is not set
# CONFIG_INLINE_READ_LOCK is not set
# CONFIG_INLINE_READ_LOCK_BH is not set
# CONFIG_INLINE_READ_LOCK_IRQ is not set
# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
CONFIG_INLINE_READ_UNLOCK=y
# CONFIG_INLINE_READ_UNLOCK_BH is not set
CONFIG_INLINE_READ_UNLOCK_IRQ=y
# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
# CONFIG_INLINE_WRITE_TRYLOCK is not set
# CONFIG_INLINE_WRITE_LOCK is not set
# CONFIG_INLINE_WRITE_LOCK_BH is not set
# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
CONFIG_INLINE_WRITE_UNLOCK=y
# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
# CONFIG_MUTEX_SPIN_ON_OWNER is not set
CONFIG_FREEZER=y

#
# System Type
#
CONFIG_MMU=y
# CONFIG_ARCH_INTEGRATOR is not set
# CONFIG_ARCH_REALVIEW is not set
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_VEXPRESS is not set
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_BCMRING is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_CNS3XXX is not set
# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_MXC is not set
# CONFIG_ARCH_MXS is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
# CONFIG_ARCH_IXP23XX is not set
# CONFIG_ARCH_IXP2000 is not set
# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_DOVE is not set
# CONFIG_ARCH_KIRKWOOD is not set
# CONFIG_ARCH_LOKI is not set
# CONFIG_ARCH_LPC32XX is not set
# CONFIG_ARCH_MV78XX0 is not set
# CONFIG_ARCH_ORION5X is not set
# CONFIG_ARCH_MMP is not set
# CONFIG_ARCH_KS8695 is not set
# CONFIG_ARCH_W90X900 is not set
# CONFIG_ARCH_NUC93X is not set
# CONFIG_ARCH_TEGRA is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_MSM is not set
CONFIG_ARCH_SHMOBILE=y
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_S3C64XX is not set
# CONFIG_ARCH_S5P64X0 is not set
# CONFIG_ARCH_S5PC100 is not set
# CONFIG_ARCH_S5PV210 is not set
# CONFIG_ARCH_EXYNOS4 is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_TCC_926 is not set
# CONFIG_ARCH_U300 is not set
# CONFIG_ARCH_U8500 is not set
# CONFIG_ARCH_NOMADIK is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
# CONFIG_PLAT_SPEAR is not set
# CONFIG_ARCH_VT8500 is not set
# CONFIG_GPIO_PCA953X is not set
# CONFIG_KEYBOARD_GPIO_POLLED is not set

#
# System MMU
#

#
# SH-Mobile System Type
#
# CONFIG_ARCH_SH7367 is not set
# CONFIG_ARCH_SH7377 is not set
CONFIG_ARCH_SH7372=y
# CONFIG_ARCH_SH73A0 is not set

#
# SH-Mobile Board Type
#
# CONFIG_MACH_AP4EVB is not set
CONFIG_MACH_MACKEREL=y

#
# SH-Mobile System Configuration
#

#
# Memory configuration
#
CONFIG_MEMORY_START=0x40000000
CONFIG_MEMORY_SIZE=0x10000000

#
# Timer and clock configuration
#
CONFIG_SHMOBILE_TIMER_HZ=128
CONFIG_SH_TIMER_CMT=y
# CONFIG_SH_TIMER_TMU is not set
CONFIG_SH_CLK_CPG=y

#
# SuperH / SH-Mobile Driver Options
#

#
# Interrupt controller options
#
# CONFIG_INTC_USERIMASK is not set
# CONFIG_INTC_MAPPING_DEBUG is not set

#
# Processor Type
#
CONFIG_CPU_V7=y
CONFIG_CPU_32v6K=y
CONFIG_CPU_32v7=y
CONFIG_CPU_ABRT_EV7=y
CONFIG_CPU_PABRT_V7=y
CONFIG_CPU_CACHE_V7=y
CONFIG_CPU_CACHE_VIPT=y
CONFIG_CPU_COPY_V6=y
CONFIG_CPU_TLB_V7=y
CONFIG_CPU_HAS_ASID=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y

#
# Processor Features
#
CONFIG_ARM_THUMB=y
# CONFIG_ARM_THUMBEE is not set
# CONFIG_SWP_EMULATE is not set
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
CONFIG_CPU_BPREDICT_DISABLE=y
CONFIG_OUTER_CACHE=y
CONFIG_OUTER_CACHE_SYNC=y
CONFIG_CACHE_L2X0=y
CONFIG_CACHE_PL310=y
CONFIG_ARM_L1_CACHE_SHIFT=5
CONFIG_ARM_DMA_MEM_BUFFERABLE=y
CONFIG_CPU_HAS_PMU=y
CONFIG_MULTI_IRQ_HANDLER=y
CONFIG_ARM_ERRATA_430973=y
CONFIG_ARM_ERRATA_458693=y
# CONFIG_ARM_ERRATA_460075 is not set
# CONFIG_PL310_ERRATA_588369 is not set
# CONFIG_PL310_ERRATA_727915 is not set
# CONFIG_ARM_ERRATA_743622 is not set
CONFIG_ARM_ERRATA_753970=y
# CONFIG_ARM_ERRATA_754322 is not set

#
# Bus support
#
# CONFIG_PCI_SYSCALL is not set
# CONFIG_ARCH_SUPPORTS_MSI is not set
# CONFIG_PCCARD is not set

#
# Kernel Features
#
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
# CONFIG_SMP is not set
CONFIG_VMSPLIT_3G=y
# CONFIG_VMSPLIT_2G is not set
# CONFIG_VMSPLIT_1G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
CONFIG_HZ=128
# CONFIG_THUMB2_KERNEL is not set
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_HAVE_ARCH_PFN_VALID=y
# CONFIG_HIGHMEM is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_HAVE_MEMBLOCK=y
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_COMPACTION is not set
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
# CONFIG_KSM is not set
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_NEED_PER_CPU_KM=y
# CONFIG_CLEANCACHE is not set
CONFIG_FORCE_MAX_ZONEORDER=13
CONFIG_ALIGNMENT_TRAP=y
# CONFIG_UACCESS_WITH_MEMCPY is not set
# CONFIG_SECCOMP is not set
# CONFIG_CC_STACKPROTECTOR is not set
# CONFIG_DEPRECATED_PARAM_STRUCT is not set

#
# Boot options
#
CONFIG_USE_OF=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="console=tty0 console=ttySC0,115200 earlyprintk=sh-sci.0,115200 ignore_loglevel memblock=debug memchunk.veu0=4m no_console_suspend"
# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
# CONFIG_CMDLINE_EXTEND is not set
CONFIG_CMDLINE_FORCE=y
# CONFIG_XIP_KERNEL is not set
CONFIG_KEXEC=y
CONFIG_ATAGS_PROC=y
# CONFIG_CRASH_DUMP is not set
# CONFIG_AUTO_ZRELADDR is not set

#
# CPU Power Management
#
# CONFIG_CPU_IDLE is not set

#
# Floating point emulation
#

#
# At least one emulation must be selected
#
# CONFIG_VFP is not set

#
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set

#
# Power management options
#
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
CONFIG_PM_SLEEP=y
CONFIG_PM_RUNTIME=y
CONFIG_PM=y
CONFIG_PM_DEBUG=y
CONFIG_PM_ADVANCED_DEBUG=y
# CONFIG_PM_TEST_SUSPEND is not set
CONFIG_CAN_PM_TRACE=y
# CONFIG_APM_EMULATION is not set
CONFIG_PM_RUNTIME_CLK=y
CONFIG_PM_GENERIC_DOMAINS=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y

#
# Networking options
#
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM=y
# CONFIG_XFRM_USER is not set
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE_DEMUX is not set
# CONFIG_IP_MROUTE is not set
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_INET_XFRM_MODE_BEET=y
CONFIG_INET_LRO=y
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_RDS is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_L2TP is not set
# CONFIG_BRIDGE is not set
# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
# CONFIG_BATMAN_ADV is not set

#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
# CONFIG_WIRELESS is not set
# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
# CONFIG_CAIF is not set
# CONFIG_CEPH_LIB is not set

#
# Device Drivers
#

#
# Generic Driver Options
#
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
# CONFIG_DEVTMPFS is not set
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_REDBOOT_PARTS is not set
# CONFIG_MTD_CMDLINE_PARTS is not set
# CONFIG_MTD_AFS_PARTS is not set
CONFIG_MTD_OF_PARTS=y
# CONFIG_MTD_AR7_PARTS is not set

#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
# CONFIG_RFD_FTL is not set
# CONFIG_SSFDC is not set
# CONFIG_SM_FTL is not set
# CONFIG_MTD_OOPS is not set
# CONFIG_MTD_SWAP is not set

#
# RAM/ROM/Flash chip drivers
#
CONFIG_MTD_CFI=y
# CONFIG_MTD_JEDECPROBE is not set
CONFIG_MTD_GEN_PROBE=y
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
CONFIG_MTD_MAP_BANK_WIDTH_1=y
CONFIG_MTD_MAP_BANK_WIDTH_2=y
CONFIG_MTD_MAP_BANK_WIDTH_4=y
# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=y
# CONFIG_MTD_CFI_AMDSTD is not set
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set

#
# Mapping drivers for chip access
#
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_PHYSMAP=y
# CONFIG_MTD_PHYSMAP_COMPAT is not set
CONFIG_MTD_PHYSMAP_OF=y
# CONFIG_MTD_ARM_INTEGRATOR is not set
# CONFIG_MTD_PLATRAM is not set

#
# Self-contained MTD device drivers
#
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
# CONFIG_MTD_BLOCK2MTD is not set

#
# Disk-On-Chip Device Drivers
#
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
CONFIG_MTD_NAND_ECC=y
# CONFIG_MTD_NAND_ECC_SMC is not set
CONFIG_MTD_NAND=y
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
# CONFIG_MTD_NAND_ECC_BCH is not set
# CONFIG_MTD_SM_COMMON is not set
# CONFIG_MTD_NAND_MUSEUM_IDS is not set
# CONFIG_MTD_NAND_GPIO is not set
CONFIG_MTD_NAND_IDS=y
# CONFIG_MTD_NAND_DISKONCHIP is not set
# CONFIG_MTD_NAND_NANDSIM is not set
# CONFIG_MTD_NAND_PLATFORM is not set
# CONFIG_MTD_ALAUDA is not set
CONFIG_MTD_NAND_SH_FLCTL=y
# CONFIG_MTD_ONENAND is not set

#
# LPDDR flash memory drivers
#
# CONFIG_MTD_LPDDR is not set
# CONFIG_MTD_UBI is not set
CONFIG_DTC=y
CONFIG_OF=y

#
# Device Tree and Open Firmware support
#
CONFIG_PROC_DEVICETREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_IRQ=y
CONFIG_OF_DEVICE=y
CONFIG_OF_GPIO=y
CONFIG_OF_I2C=y
CONFIG_OF_NET=y
CONFIG_OF_MDIO=y
# CONFIG_PARPORT is not set
# CONFIG_BLK_DEV is not set
# CONFIG_SENSORS_LIS3LV02D is not set
# CONFIG_MISC_DEVICES is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set

#
# SCSI device support
#
CONFIG_SCSI_MOD=y
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
# CONFIG_SCSI_DMA is not set
# CONFIG_SCSI_NETLINK is not set
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_VETH is not set
CONFIG_MII=y
CONFIG_PHYLIB=y

#
# MII PHY device drivers
#
# CONFIG_MARVELL_PHY is not set
# CONFIG_DAVICOM_PHY is not set
# CONFIG_QSEMI_PHY is not set
# CONFIG_LXT_PHY is not set
# CONFIG_CICADA_PHY is not set
# CONFIG_VITESSE_PHY is not set
# CONFIG_SMSC_PHY is not set
# CONFIG_BROADCOM_PHY is not set
# CONFIG_BCM63XX_PHY is not set
# CONFIG_ICPLUS_PHY is not set
# CONFIG_REALTEK_PHY is not set
# CONFIG_NATIONAL_PHY is not set
# CONFIG_STE10XP is not set
# CONFIG_LSI_ET1011C_PHY is not set
# CONFIG_MICREL_PHY is not set
# CONFIG_FIXED_PHY is not set
# CONFIG_MDIO_BITBANG is not set
CONFIG_NET_ETHERNET=y
# CONFIG_AX88796 is not set
# CONFIG_SMC91X is not set
# CONFIG_DM9000 is not set
# CONFIG_ETHOC is not set
# CONFIG_SMC911X is not set
CONFIG_SMSC911X=y
# CONFIG_SMSC911X_ARCH_HOOKS is not set
# CONFIG_DNET is not set
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
# CONFIG_KS8851_MLL is not set
# CONFIG_FTMAC100 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
# CONFIG_WLAN is not set

#
# Enable WiMAX (Networking options) to see the WiMAX drivers
#

#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
CONFIG_USB_USBNET=y
# CONFIG_USB_NET_AX8817X is not set
# CONFIG_USB_NET_CDCETHER is not set
# CONFIG_USB_NET_CDC_EEM is not set
CONFIG_USB_NET_CDC_NCM=y
CONFIG_USB_NET_DM9601=y
# CONFIG_USB_NET_SMSC75XX is not set
# CONFIG_USB_NET_SMSC95XX is not set
# CONFIG_USB_NET_GL620A is not set
# CONFIG_USB_NET_NET1080 is not set
# CONFIG_USB_NET_PLUSB is not set
# CONFIG_USB_NET_MCS7830 is not set
# CONFIG_USB_NET_RNDIS_HOST is not set
# CONFIG_USB_NET_CDC_SUBSET is not set
# CONFIG_USB_NET_ZAURUS is not set
# CONFIG_USB_NET_CX82310_ETH is not set
# CONFIG_USB_NET_INT51X1 is not set
# CONFIG_USB_IPHETH is not set
# CONFIG_USB_SIERRA_NET is not set
# CONFIG_WAN is not set

#
# CAIF transport drivers
#
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
# CONFIG_ISDN is not set
# CONFIG_PHONE is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set
# CONFIG_INPUT_POLLDEV is not set
# CONFIG_INPUT_SPARSEKMAP is not set

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5588 is not set
# CONFIG_KEYBOARD_ADP5589 is not set
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_KEYBOARD_TCA6416 is not set
# CONFIG_KEYBOARD_MATRIX is not set
CONFIG_KEYBOARD_LM8323=y
# CONFIG_KEYBOARD_MAX7359 is not set
# CONFIG_KEYBOARD_MCS is not set
# CONFIG_KEYBOARD_MPR121 is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_OPENCORES is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
CONFIG_KEYBOARD_SH_KEYSC=y
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
# CONFIG_TOUCHSCREEN_BU21013 is not set
# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
# CONFIG_TOUCHSCREEN_DYNAPRO is not set
# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
# CONFIG_TOUCHSCREEN_EETI is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
# CONFIG_TOUCHSCREEN_MAX11801 is not set
# CONFIG_TOUCHSCREEN_MCS5000 is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
CONFIG_TOUCHSCREEN_TSC2007=y
# CONFIG_TOUCHSCREEN_W90X900 is not set
# CONFIG_TOUCHSCREEN_ST1232 is not set
# CONFIG_TOUCHSCREEN_TPS6507X is not set
# CONFIG_INPUT_MISC is not set

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_RAW is not set
# CONFIG_SERIO_ALTERA_PS2 is not set
# CONFIG_SERIO_PS2MULT is not set
# CONFIG_GAMEPORT is not set

#
# Character devices
#
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_VT_HW_CONSOLE_BINDING is not set
CONFIG_UNIX98_PTYS=y
# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_N_GSM is not set
# CONFIG_TRACE_SINK is not set
CONFIG_DEVKMEM=y

#
# Serial drivers
#
# CONFIG_SERIAL_8250 is not set

#
# Non-8250 serial port support
#
CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_SH_SCI_NR_UARTS=8
CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_TIMBERDALE is not set
# CONFIG_SERIAL_ALTERA_JTAGUART is not set
# CONFIG_SERIAL_ALTERA_UART is not set
# CONFIG_SERIAL_XILINX_PS_UART is not set
# CONFIG_HVC_DCC is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_RAMOOPS is not set
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
# CONFIG_I2C_CHARDEV is not set
# CONFIG_I2C_MUX is not set
CONFIG_I2C_HELPER_AUTO=y

#
# I2C Hardware Bus support
#

#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_GPIO is not set
# CONFIG_I2C_OCORES is not set
# CONFIG_I2C_PCA_PLATFORM is not set
# CONFIG_I2C_PXA_PCI is not set
CONFIG_I2C_SH_MOBILE=y
# CONFIG_I2C_SIMTEC is not set
# CONFIG_I2C_XILINX is not set

#
# External I2C/SMBus adapter drivers
#
CONFIG_I2C_DIOLAN_U2C=y
# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_I2C_TAOS_EVM is not set
# CONFIG_I2C_TINY_USB is not set

#
# Other I2C/SMBus bus drivers
#
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_SPI is not set

#
# PPS support
#
# CONFIG_PPS is not set

#
# PPS generators support
#

#
# PTP clock support
#

#
# Enable Device Drivers -> PPS to see the PTP clock options.
#
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
CONFIG_ARCH_REQUIRE_GPIOLIB=y
CONFIG_GPIOLIB=y
# CONFIG_DEBUG_GPIO is not set
# CONFIG_GPIO_SYSFS is not set

#
# Memory mapped GPIO drivers:
#
# CONFIG_GPIO_BASIC_MMIO is not set
# CONFIG_GPIO_IT8761E is not set

#
# I2C GPIO expanders:
#
# CONFIG_GPIO_MAX7300 is not set
# CONFIG_GPIO_MAX732X is not set
# CONFIG_GPIO_PCF857X is not set
# CONFIG_GPIO_SX150X is not set
# CONFIG_GPIO_ADP5588 is not set

#
# PCI GPIO expanders:
#

#
# SPI GPIO expanders:
#

#
# AC97 GPIO expanders:
#

#
# MODULbus GPIO expanders:
#
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
# CONFIG_THERMAL is not set
# CONFIG_WATCHDOG is not set
CONFIG_SSB_POSSIBLE=y

#
# Sonics Silicon Backplane
#
# CONFIG_SSB is not set
CONFIG_BCMA_POSSIBLE=y

#
# Broadcom specific AMBA
#
# CONFIG_BCMA is not set
CONFIG_MFD_SUPPORT=y
# CONFIG_MFD_CORE is not set
# CONFIG_MFD_88PM860X is not set
# CONFIG_MFD_SM501 is not set
# CONFIG_MFD_ASIC3 is not set
# CONFIG_HTC_EGPIO is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_HTC_I2CPLD is not set
# CONFIG_TPS6105X is not set
# CONFIG_TPS65010 is not set
# CONFIG_TPS6507X is not set
# CONFIG_MFD_TPS6586X is not set
# CONFIG_TWL4030_CORE is not set
# CONFIG_MFD_STMPE is not set
# CONFIG_MFD_TC3589X is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_MFD_T7L66XB is not set
# CONFIG_MFD_TC6387XB is not set
# CONFIG_MFD_TC6393XB is not set
# CONFIG_PMIC_DA903X is not set
# CONFIG_PMIC_ADP5520 is not set
# CONFIG_MFD_MAX8925 is not set
# CONFIG_MFD_MAX8997 is not set
# CONFIG_MFD_MAX8998 is not set
# CONFIG_MFD_WM8400 is not set
# CONFIG_MFD_WM831X_I2C is not set
# CONFIG_MFD_WM8350_I2C is not set
# CONFIG_MFD_WM8994 is not set
# CONFIG_MFD_PCF50633 is not set
# CONFIG_ABX500_CORE is not set
# CONFIG_MFD_WL1273_CORE is not set
# CONFIG_MFD_TPS65910 is not set
# CONFIG_REGULATOR is not set
CONFIG_MEDIA_SUPPORT=y

#
# Multimedia core support
#
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_COMMON=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_DVB_CORE is not set
CONFIG_VIDEO_MEDIA=y

#
# Multimedia drivers
#
CONFIG_RC_CORE=y
CONFIG_LIRC=y
CONFIG_RC_MAP=y
CONFIG_IR_NEC_DECODER=y
CONFIG_IR_RC5_DECODER=y
CONFIG_IR_RC6_DECODER=y
CONFIG_IR_JVC_DECODER=y
CONFIG_IR_SONY_DECODER=y
CONFIG_IR_RC5_SZ_DECODER=y
CONFIG_IR_LIRC_CODEC=y
# CONFIG_IR_IMON is not set
# CONFIG_IR_MCEUSB is not set
# CONFIG_IR_REDRAT3 is not set
# CONFIG_IR_STREAMZAP is not set
# CONFIG_RC_LOOPBACK is not set
CONFIG_MEDIA_TUNER=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
CONFIG_MEDIA_TUNER_SIMPLE=y
CONFIG_MEDIA_TUNER_TDA8290=y
CONFIG_MEDIA_TUNER_TDA827X=y
CONFIG_MEDIA_TUNER_TDA18271=y
CONFIG_MEDIA_TUNER_TDA9887=y
CONFIG_MEDIA_TUNER_TEA5761=y
CONFIG_MEDIA_TUNER_TEA5767=y
CONFIG_MEDIA_TUNER_MT20XX=y
CONFIG_MEDIA_TUNER_XC2028=y
CONFIG_MEDIA_TUNER_XC5000=y
CONFIG_MEDIA_TUNER_MC44S803=y
CONFIG_VIDEO_V4L2=y
CONFIG_VIDEOBUF_GEN=y
CONFIG_VIDEOBUF2_CORE=y
CONFIG_VIDEOBUF2_MEMOPS=y
CONFIG_VIDEOBUF2_DMA_CONTIG=y
CONFIG_VIDEO_CAPTURE_DRIVERS=y
# CONFIG_VIDEO_ADV_DEBUG is not set
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_VIDEO_IR_I2C=y

#
# Audio decoders, processors and mixers
#

#
# RDS decoders
#

#
# Video decoders
#

#
# Video and audio decoders
#

#
# MPEG video encoders
#

#
# Video encoders
#

#
# Camera sensor devices
#

#
# Video improvement chips
#

#
# Miscelaneous helper chips
#
# CONFIG_VIDEO_SH_VOU is not set
# CONFIG_VIDEO_VIVI is not set
# CONFIG_VIDEO_CPIA2 is not set
# CONFIG_VIDEO_TIMBERDALE is not set
# CONFIG_VIDEO_SR030PC30 is not set
CONFIG_VIDEO_NOON010PC30=y
# CONFIG_VIDEO_M5MOLS is not set
CONFIG_SOC_CAMERA=y
CONFIG_SOC_CAMERA_IMX074=y
# CONFIG_SOC_CAMERA_MT9M001 is not set
# CONFIG_SOC_CAMERA_MT9M111 is not set
# CONFIG_SOC_CAMERA_MT9T031 is not set
# CONFIG_SOC_CAMERA_MT9T112 is not set
# CONFIG_SOC_CAMERA_MT9V022 is not set
# CONFIG_SOC_CAMERA_RJ54N1 is not set
# CONFIG_SOC_CAMERA_TW9910 is not set
CONFIG_SOC_CAMERA_PLATFORM=y
# CONFIG_SOC_CAMERA_OV2640 is not set
# CONFIG_SOC_CAMERA_OV6650 is not set
# CONFIG_SOC_CAMERA_OV772X is not set
# CONFIG_SOC_CAMERA_OV9640 is not set
CONFIG_SOC_CAMERA_OV9740=y
CONFIG_VIDEO_SH_MOBILE_CSI2=y
CONFIG_VIDEO_SH_MOBILE_CEU=y
# CONFIG_V4L_USB_DRIVERS is not set
# CONFIG_V4L_MEM2MEM_DRIVERS is not set
# CONFIG_RADIO_ADAPTERS is not set

#
# Graphics support
#
# CONFIG_DRM is not set
# CONFIG_VGASTATE is not set
# CONFIG_VIDEO_OUTPUT_CONTROL is not set
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
# CONFIG_FB_BOOT_VESA_SUPPORT is not set
# CONFIG_FB_CFB_FILLRECT is not set
# CONFIG_FB_CFB_COPYAREA is not set
# CONFIG_FB_CFB_IMAGEBLIT is not set
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_IMAGEBLIT=y
# CONFIG_FB_FOREIGN_ENDIAN is not set
CONFIG_FB_SYS_FOPS=y
# CONFIG_FB_WMT_GE_ROPS is not set
CONFIG_FB_DEFERRED_IO=y
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
CONFIG_FB_BACKLIGHT=y
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set

#
# Frame buffer hardware drivers
#
# CONFIG_FB_S1D13XXX is not set
CONFIG_FB_SH_MOBILE_LCDC=y
CONFIG_FB_SH_MOBILE_HDMI=y
CONFIG_FB_SH_MOBILE_MERAM=y
# CONFIG_FB_UDL is not set
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FB_METRONOME is not set
# CONFIG_FB_BROADSHEET is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_GENERIC=y
CONFIG_BACKLIGHT_ADP8860=y

#
# Display device support
#
# CONFIG_DISPLAY_SUPPORT is not set

#
# Console display driver support
#
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_6x11 is not set
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_SUN8x16 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_10x18 is not set
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
CONFIG_LOGO_LINUX_VGA16=y
# CONFIG_LOGO_LINUX_CLUT224 is not set
CONFIG_SOUND=y
CONFIG_SOUND_OSS_CORE=y
CONFIG_SOUND_OSS_CORE_PRECLAIM=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_JACK=y
# CONFIG_SND_SEQUENCER is not set
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_SND_PCM_OSS_PLUGINS=y
# CONFIG_SND_DYNAMIC_MINORS is not set
CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
# CONFIG_SND_RAWMIDI_SEQ is not set
# CONFIG_SND_OPL3_LIB_SEQ is not set
# CONFIG_SND_OPL4_LIB_SEQ is not set
# CONFIG_SND_SBAWE_SEQ is not set
# CONFIG_SND_EMU10K1_SEQ is not set
# CONFIG_SND_DRIVERS is not set
# CONFIG_SND_ARM is not set
# CONFIG_SND_USB is not set
CONFIG_SND_SOC=y
# CONFIG_SND_SOC_CACHE_LZO is not set

#
# SoC Audio support for SuperH
#
CONFIG_SND_SOC_SH4_FSI=y
CONFIG_SND_FSI_AK4642=y
# CONFIG_SND_FSI_DA7210 is not set
# CONFIG_SND_FSI_HDMI is not set
CONFIG_SND_SOC_I2C_AND_SPI=y
# CONFIG_SND_SOC_ALL_CODECS is not set
CONFIG_SND_SOC_AK4642=y
# CONFIG_SOUND_PRIME is not set
# CONFIG_HID_SUPPORT is not set
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
# CONFIG_USB_ARCH_HAS_OHCI is not set
# CONFIG_USB_ARCH_HAS_EHCI is not set
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set
# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set

#
# Miscellaneous USB options
#
# CONFIG_USB_DEVICEFS is not set
CONFIG_USB_DEVICE_CLASS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_MON is not set
# CONFIG_USB_WUSB is not set
# CONFIG_USB_WUSB_CBAF is not set

#
# USB Host Controller Drivers
#
# CONFIG_USB_C67X00_HCD is not set
# CONFIG_USB_OXU210HP_HCD is not set
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1760_HCD is not set
# CONFIG_USB_ISP1362_HCD is not set
# CONFIG_USB_SL811_HCD is not set
CONFIG_USB_R8A66597_HCD=y
# CONFIG_USB_HWA_HCD is not set
# CONFIG_USB_MUSB_HDRC is not set
CONFIG_USB_RENESAS_USBHS=y

#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
# CONFIG_USB_PRINTER is not set
# CONFIG_USB_WDM is not set
# CONFIG_USB_TMC is not set

#
# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
#

#
# also be needed; see USB_STORAGE Help for more info
#
# CONFIG_USB_LIBUSUAL is not set

#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set

#
# USB port drivers
#
# CONFIG_USB_SERIAL is not set

#
# USB Miscellaneous drivers
#
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_ADUTUX is not set
# CONFIG_USB_SEVSEG is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
# CONFIG_USB_LED is not set
# CONFIG_USB_CYPRESS_CY7C63 is not set
# CONFIG_USB_CYTHERM is not set
# CONFIG_USB_IDMOUSE is not set
# CONFIG_USB_FTDI_ELAN is not set
# CONFIG_USB_APPLEDISPLAY is not set
# CONFIG_USB_LD is not set
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_IOWARRIOR is not set
# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
# CONFIG_USB_YUREX is not set
# CONFIG_USB_GADGET is not set

#
# OTG and related infrastructure
#
# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_USB_ULPI is not set
# CONFIG_NOP_USB_XCEIV is not set
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
# CONFIG_MMC_CLKGATE is not set

#
# MMC/SD/SDIO Card Drivers
#
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_MINORS=8
CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_SDIO_UART is not set
# CONFIG_MMC_TEST is not set

#
# MMC/SD/SDIO Host Controller Drivers
#
# CONFIG_MMC_SDHCI is not set
CONFIG_MMC_TMIO_CORE=y
CONFIG_MMC_SDHI=y
# CONFIG_MMC_DW is not set
CONFIG_MMC_SH_MMCIF=y
CONFIG_MMC_VUB300=y
# CONFIG_MMC_USHC is not set
# CONFIG_MEMSTICK is not set
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y

#
# LED drivers
#
CONFIG_LEDS_LM3530=y
CONFIG_LEDS_PCA9532=y
# CONFIG_LEDS_PCA9532_GPIO is not set
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_GPIO_PLATFORM=y
CONFIG_LEDS_GPIO_OF=y
CONFIG_LEDS_LP3944=y
CONFIG_LEDS_LP5521=y
CONFIG_LEDS_LP5523=y
CONFIG_LEDS_PCA955X=y
CONFIG_LEDS_BD2802=y
CONFIG_LEDS_LT3593=y
CONFIG_LEDS_TRIGGERS=y

#
# LED Triggers
#
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y

#
# iptables trigger is under Netfilter config (LED target)
#
# CONFIG_NFC_DEVICES is not set
# CONFIG_ACCESSIBILITY is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# CONFIG_RTC_DEBUG is not set

#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
CONFIG_RTC_INTF_DEV=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
# CONFIG_RTC_DRV_TEST is not set

#
# I2C RTC drivers
#
# CONFIG_RTC_DRV_DS1307 is not set
# CONFIG_RTC_DRV_DS1374 is not set
# CONFIG_RTC_DRV_DS1672 is not set
# CONFIG_RTC_DRV_DS3232 is not set
# CONFIG_RTC_DRV_MAX6900 is not set
CONFIG_RTC_DRV_RS5C372=y
# CONFIG_RTC_DRV_ISL1208 is not set
# CONFIG_RTC_DRV_ISL12022 is not set
# CONFIG_RTC_DRV_X1205 is not set
# CONFIG_RTC_DRV_PCF8563 is not set
# CONFIG_RTC_DRV_PCF8583 is not set
# CONFIG_RTC_DRV_M41T80 is not set
# CONFIG_RTC_DRV_BQ32K is not set
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
# CONFIG_RTC_DRV_RX8581 is not set
# CONFIG_RTC_DRV_RX8025 is not set
# CONFIG_RTC_DRV_EM3027 is not set
# CONFIG_RTC_DRV_RV3029C2 is not set

#
# SPI RTC drivers
#

#
# Platform RTC drivers
#
# CONFIG_RTC_DRV_CMOS is not set
# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
# CONFIG_RTC_DRV_MSM6242 is not set
# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_RP5C01 is not set
# CONFIG_RTC_DRV_V3020 is not set

#
# on-CPU RTC drivers
#
CONFIG_DMADEVICES=y
# CONFIG_DMADEVICES_DEBUG is not set

#
# DMA Devices
#
# CONFIG_DW_DMAC is not set
# CONFIG_SH_DMAE is not set
# CONFIG_TIMB_DMA is not set
# CONFIG_AUXDISPLAY is not set
CONFIG_UIO=y
# CONFIG_UIO_PDRV is not set
CONFIG_UIO_PDRV_GENIRQ=y
# CONFIG_STAGING is not set
CONFIG_CLKDEV_LOOKUP=y

#
# File systems
#
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_NILFS2_FS is not set
# CONFIG_FS_POSIX_ACL is not set
CONFIG_FILE_LOCKING=y
CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
CONFIG_INOTIFY_USER=y
# CONFIG_FANOTIFY is not set
# CONFIG_QUOTA is not set
# CONFIG_QUOTACTL is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set

#
# Caches
#
# CONFIG_FSCACHE is not set

#
# CD-ROM/DVD Filesystems
#
# CONFIG_ISO9660_FS is not set
# CONFIG_UDF_FS is not set

#
# DOS/FAT/NT Filesystems
#
# CONFIG_MSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_NTFS_FS is not set

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_TMPFS_XATTR is not set
# CONFIG_HUGETLB_PAGE is not set
# CONFIG_CONFIGFS_FS is not set
CONFIG_MISC_FILESYSTEMS=y
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_JFFS2_FS is not set
# CONFIG_LOGFS is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
# CONFIG_PSTORE is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
CONFIG_NETWORK_FILESYSTEMS=y
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_CEPH_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set

#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
# CONFIG_NLS_ASCII is not set
# CONFIG_NLS_ISO8859_1 is not set
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set

#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
CONFIG_ENABLE_WARN_DEPRECATED=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_FRAME_WARN=1024
CONFIG_MAGIC_SYSRQ=y
# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_DEBUG_FS=y
# CONFIG_HEADERS_CHECK is not set
# CONFIG_DEBUG_SECTION_MISMATCH is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_LOCKUP_DETECTOR=y
# CONFIG_HARDLOCKUP_DETECTOR is not set
# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
# CONFIG_DETECT_HUNG_TASK is not set
# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
# CONFIG_TIMER_STATS is not set
# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_KMEMLEAK is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_PROVE_LOCKING is not set
# CONFIG_SPARSE_RCU_POINTER is not set
# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_WRITECOUNT is not set
CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_LIST is not set
# CONFIG_TEST_LIST_SORT is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_DEBUG_CREDENTIALS is not set
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
# CONFIG_SYSCTL_SYSCALL_CHECK is not set
# CONFIG_DEBUG_PAGEALLOC is not set
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_TRACING_SUPPORT=y
# CONFIG_FTRACE is not set
CONFIG_DYNAMIC_DEBUG=y
# CONFIG_DMA_API_DEBUG is not set
# CONFIG_ATOMIC64_SELFTEST is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
# CONFIG_TEST_KSTRTOX is not set
# CONFIG_STRICT_DEVMEM is not set
CONFIG_ARM_UNWIND=y
# CONFIG_DEBUG_USER is not set
# CONFIG_DEBUG_LL is not set
# CONFIG_OC_ETM is not set

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SECURITY is not set
# CONFIG_SECURITYFS is not set
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_DEFAULT_SECURITY=""
CONFIG_CRYPTO=y

#
# Crypto core or helper
#
# CONFIG_CRYPTO_FIPS is not set
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
# CONFIG_CRYPTO_MANAGER is not set
# CONFIG_CRYPTO_MANAGER2 is not set
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_CRYPTD is not set
# CONFIG_CRYPTO_AUTHENC is not set

#
# Authenticated Encryption with Associated Data
#
# CONFIG_CRYPTO_CCM is not set
# CONFIG_CRYPTO_GCM is not set
# CONFIG_CRYPTO_SEQIV is not set

#
# Block modes
#
# CONFIG_CRYPTO_CBC is not set
# CONFIG_CRYPTO_CTR is not set
# CONFIG_CRYPTO_CTS is not set
# CONFIG_CRYPTO_ECB is not set
# CONFIG_CRYPTO_LRW is not set
# CONFIG_CRYPTO_PCBC is not set
# CONFIG_CRYPTO_XTS is not set

#
# Hash modes
#
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_XCBC is not set
# CONFIG_CRYPTO_VMAC is not set

#
# Digest
#
# CONFIG_CRYPTO_CRC32C is not set
# CONFIG_CRYPTO_GHASH is not set
# CONFIG_CRYPTO_MD4 is not set
# CONFIG_CRYPTO_MD5 is not set
# CONFIG_CRYPTO_MICHAEL_MIC is not set
# CONFIG_CRYPTO_RMD128 is not set
# CONFIG_CRYPTO_RMD160 is not set
# CONFIG_CRYPTO_RMD256 is not set
# CONFIG_CRYPTO_RMD320 is not set
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_SHA512 is not set
# CONFIG_CRYPTO_TGR192 is not set
# CONFIG_CRYPTO_WP512 is not set

#
# Ciphers
#
CONFIG_CRYPTO_AES=y
# CONFIG_CRYPTO_ANUBIS is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_CAMELLIA is not set
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST6 is not set
# CONFIG_CRYPTO_DES is not set
# CONFIG_CRYPTO_FCRYPT is not set
# CONFIG_CRYPTO_KHAZAD is not set
# CONFIG_CRYPTO_SALSA20 is not set
# CONFIG_CRYPTO_SEED is not set
# CONFIG_CRYPTO_SERPENT is not set
# CONFIG_CRYPTO_TEA is not set
# CONFIG_CRYPTO_TWOFISH is not set

#
# Compression
#
# CONFIG_CRYPTO_DEFLATE is not set
# CONFIG_CRYPTO_ZLIB is not set
# CONFIG_CRYPTO_LZO is not set

#
# Random Number Generation
#
CONFIG_CRYPTO_ANSI_CPRNG=y
# CONFIG_CRYPTO_USER_API_HASH is not set
# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
CONFIG_CRYPTO_HW=y
# CONFIG_BINARY_PRINTF is not set

#
# Library routines
#
CONFIG_BITREVERSE=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_XZ_DEC=y
CONFIG_XZ_DEC_X86=y
CONFIG_XZ_DEC_POWERPC=y
CONFIG_XZ_DEC_IA64=y
CONFIG_XZ_DEC_ARM=y
CONFIG_XZ_DEC_ARMTHUMB=y
CONFIG_XZ_DEC_SPARC=y
CONFIG_XZ_DEC_BCJ=y
# CONFIG_XZ_DEC_TEST is not set
CONFIG_DECOMPRESS_GZIP=y
CONFIG_DECOMPRESS_BZIP2=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_DECOMPRESS_XZ=y
CONFIG_DECOMPRESS_LZO=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_DMA=y
CONFIG_NLATTR=y
# CONFIG_AVERAGE is not set

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-14 13:12     ` Magnus Damm
  (?)
@ 2011-06-14 21:16     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-14 21:16 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

[-- Attachment #1: Type: Text/Plain, Size: 4290 bytes --]

On Tuesday, June 14, 2011, Magnus Damm wrote:
> Hi Rafael,

Hi,

> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Use the generic power domains support introduced by the previous
> > patch to implement support for power domains on SH7372.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > ---
> 
> Thanks for your work on this. I just tried this on my Mackerel board,
> but I can't seem to get the pd_power_up() and pd_power_down()
> callbacks to be executed. It is probably a misconfiguration from my
> side.

They trigger for me e.g. after doing

# echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank

Attached is the .config I've been using.

> Here's some feedback on the sh7372-specific code:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> > @@ -19,6 +19,7 @@ config ARCH_SH7372
> >        select CPU_V7
> >        select SH_CLK_CPG
> >        select ARCH_WANT_OPTIONAL_GPIOLIB
> > +       select PM_GENERIC_DOMAINS
> 
> We want to support a single ARM binary for multiple boards,

Surely CONFIG_ARCH_SH7372 will be set in that binary?

> so this should be enabled for all SoCs in mach-shmobile as a whole.

OK, where exactly do you want me to move it?

> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> > @@ -15,16 +15,97 @@
> >  #include <linux/list.h>
> >  #include <linux/err.h>
> >  #include <linux/slab.h>
> > +#include <linux/pm_runtime.h>
> > +#include <linux/platform_device.h>
> >  #include <asm/system.h>
> >  #include <asm/io.h>
> >  #include <asm/tlbflush.h>
> >  #include <mach/common.h>
> > +#include <mach/sh7372.h>
> >
> >  #define SMFRAM 0xe6a70000
> >  #define SYSTBCR 0xe6150024
> >  #define SBAR 0xe6180020
> >  #define APARMBAREA 0xe6f10020
> >
> > +#define SPDCR 0xe6180008
> > +#define SWUCR 0xe6180014
> > +#define PSTR 0xe6180080
> > +
> > +struct sh7372_domain_data {
> > +       unsigned int bit_shift;
> > +};
> 
> Is it possible to make struct sh7372_domain_data include struct
> generic_pm_domain?

It should be possible to do that.

Do I understand it correctly that you want one structure definition per
power domain instead of the two?

> I suspect so since these two data types seem to be linked together.
> I guess container_of() can be used for conversion between the types?

I don't think that will be necessary. :-)

> > +
> > +static int pd_power_down(struct dev_pm_domain *domain)
> > +{
> > +       struct sh7372_domain_data *dd = domain->platform_data;
> > +       unsigned int mask = 1 << dd->bit_shift;
> > +
> > +       if (__raw_readl(PSTR) & mask) {
> > +               __raw_writel(mask, SPDCR);
> > +
> > +               while (__raw_readl(SPDCR) & mask) {}
> 
> This probably wants a cpu_relax() in the polling loop. I realize that
> my prototype hack lacked that...

OK

> > +               pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > +                        mask, __raw_readl(PSTR));
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +static int pd_power_up(struct dev_pm_domain *domain)
> > +{
> > +       struct sh7372_domain_data *dd = domain->platform_data;
> > +       unsigned int mask = 1 << dd->bit_shift;
> > +
> > +       if (!(__raw_readl(PSTR) & mask)) {
> > +               __raw_writel(mask, SWUCR);
> > +
> > +               while (__raw_readl(SWUCR) & mask) {}
> 
> Same cpu_relax() here.

OK

> > +static int __init sh7372_power_domains_init(void)
> > +{
> > +       sh7372_init_domain(&sh7372_a4lc_domain, &sh7372_a4lc_domain_data);
> > +       return 0;
> > +}
> > +core_initcall(sh7372_power_domains_init);
> 
> This initcall is going to be executed regardless which SoC we're
> running on. We only want it called for sh7372 though.

OK

> If you look at other SoC-specific code then you will notice that
> initcalls are only used for functions in mach-shmobile/ that are
> common for all SoCs implemented under mach-shmobile.
> 
> You most likely want to initialize from sh7372_pm_init(), but for that
> to work you probably have to reorder your code inside mackerel_init().

OK, I'll figure out how to do the initialization correctly.

Thanks,
Rafael

[-- Attachment #2: mackerel-config --]
[-- Type: text/plain, Size: 44458 bytes --]

#
# Automatically generated make config: don't edit
# Linux/arm 3.0.0-rc2 Kernel Configuration
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
CONFIG_GENERIC_GPIO=y
# CONFIG_ARCH_USES_GETTIMEOFFSET is not set
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_KTIME_SCALAR=y
CONFIG_HAVE_PROC_CPU=y
CONFIG_NO_IOPORT=y
CONFIG_STACKTRACE_SUPPORT=y
CONFIG_HAVE_LATENCYTOP_SUPPORT=y
CONFIG_LOCKDEP_SUPPORT=y
CONFIG_TRACE_IRQFLAGS_SUPPORT=y
CONFIG_HARDIRQS_SW_RESEND=y
CONFIG_GENERIC_IRQ_PROBE=y
CONFIG_RWSEM_GENERIC_SPINLOCK=y
CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y
CONFIG_GENERIC_HWEIGHT=y
CONFIG_GENERIC_CALIBRATE_DELAY=y
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_VECTORS_BASE=0xffff0000
# CONFIG_ARM_PATCH_PHYS_VIRT is not set
CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
CONFIG_CONSTRUCTORS=y
CONFIG_HAVE_IRQ_WORK=y

#
# General setup
#
CONFIG_EXPERIMENTAL=y
CONFIG_BROKEN_ON_SMP=y
CONFIG_INIT_ENV_ARG_LIMIT=32
CONFIG_CROSS_COMPILE=""
CONFIG_LOCALVERSION=""
CONFIG_LOCALVERSION_AUTO=y
CONFIG_HAVE_KERNEL_GZIP=y
CONFIG_HAVE_KERNEL_LZMA=y
CONFIG_HAVE_KERNEL_LZO=y
CONFIG_KERNEL_GZIP=y
# CONFIG_KERNEL_LZMA is not set
# CONFIG_KERNEL_LZO is not set
CONFIG_SWAP=y
CONFIG_SYSVIPC=y
CONFIG_SYSVIPC_SYSCTL=y
# CONFIG_POSIX_MQUEUE is not set
# CONFIG_BSD_PROCESS_ACCT is not set
# CONFIG_FHANDLE is not set
# CONFIG_TASKSTATS is not set
# CONFIG_AUDIT is not set
CONFIG_HAVE_GENERIC_HARDIRQS=y

#
# IRQ subsystem
#
CONFIG_GENERIC_HARDIRQS=y
CONFIG_HAVE_SPARSE_IRQ=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_SPARSE_IRQ=y

#
# RCU Subsystem
#
CONFIG_TINY_RCU=y
# CONFIG_PREEMPT_RCU is not set
# CONFIG_RCU_TRACE is not set
# CONFIG_TREE_RCU_TRACE is not set
CONFIG_IKCONFIG=y
CONFIG_IKCONFIG_PROC=y
CONFIG_LOG_BUF_SHIFT=16
# CONFIG_CGROUPS is not set
CONFIG_NAMESPACES=y
# CONFIG_UTS_NS is not set
# CONFIG_IPC_NS is not set
# CONFIG_USER_NS is not set
# CONFIG_PID_NS is not set
# CONFIG_NET_NS is not set
# CONFIG_SCHED_AUTOGROUP is not set
CONFIG_SYSFS_DEPRECATED=y
CONFIG_SYSFS_DEPRECATED_V2=y
# CONFIG_RELAY is not set
CONFIG_BLK_DEV_INITRD=y
CONFIG_INITRAMFS_SOURCE="/home/rafael/src/arm/initramfs_data.cpio"
CONFIG_INITRAMFS_ROOT_UID=0
CONFIG_INITRAMFS_ROOT_GID=0
CONFIG_RD_GZIP=y
CONFIG_RD_BZIP2=y
CONFIG_RD_LZMA=y
CONFIG_RD_XZ=y
CONFIG_RD_LZO=y
CONFIG_INITRAMFS_COMPRESSION_NONE=y
# CONFIG_INITRAMFS_COMPRESSION_GZIP is not set
# CONFIG_INITRAMFS_COMPRESSION_BZIP2 is not set
# CONFIG_INITRAMFS_COMPRESSION_LZMA is not set
# CONFIG_INITRAMFS_COMPRESSION_XZ is not set
# CONFIG_INITRAMFS_COMPRESSION_LZO is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
CONFIG_SYSCTL=y
CONFIG_ANON_INODES=y
# CONFIG_EXPERT is not set
CONFIG_UID16=y
CONFIG_SYSCTL_SYSCALL=y
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
CONFIG_HOTPLUG=y
CONFIG_PRINTK=y
CONFIG_BUG=y
CONFIG_ELF_CORE=y
CONFIG_BASE_FULL=y
CONFIG_FUTEX=y
CONFIG_EPOLL=y
CONFIG_SIGNALFD=y
CONFIG_TIMERFD=y
CONFIG_EVENTFD=y
CONFIG_SHMEM=y
CONFIG_AIO=y
# CONFIG_EMBEDDED is not set
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_PERF_USE_VMALLOC=y

#
# Kernel Performance Events And Counters
#
# CONFIG_PERF_EVENTS is not set
# CONFIG_PERF_COUNTERS is not set
CONFIG_VM_EVENT_COUNTERS=y
CONFIG_COMPAT_BRK=y
CONFIG_SLAB=y
# CONFIG_SLUB is not set
# CONFIG_PROFILING is not set
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_KPROBES=y
CONFIG_HAVE_KRETPROBES=y
CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_DMA_API_DEBUG=y

#
# GCOV-based kernel profiling
#
# CONFIG_GCOV_KERNEL is not set
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_SLABINFO=y
CONFIG_RT_MUTEXES=y
CONFIG_BASE_SMALL=0
# CONFIG_MODULES is not set
CONFIG_BLOCK=y
CONFIG_LBDAF=y
# CONFIG_BLK_DEV_BSG is not set
# CONFIG_BLK_DEV_INTEGRITY is not set

#
# IO Schedulers
#
CONFIG_IOSCHED_NOOP=y
# CONFIG_IOSCHED_DEADLINE is not set
# CONFIG_IOSCHED_CFQ is not set
CONFIG_DEFAULT_NOOP=y
CONFIG_DEFAULT_IOSCHED="noop"
# CONFIG_INLINE_SPIN_TRYLOCK is not set
# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set
# CONFIG_INLINE_SPIN_LOCK is not set
# CONFIG_INLINE_SPIN_LOCK_BH is not set
# CONFIG_INLINE_SPIN_LOCK_IRQ is not set
# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set
CONFIG_INLINE_SPIN_UNLOCK=y
# CONFIG_INLINE_SPIN_UNLOCK_BH is not set
CONFIG_INLINE_SPIN_UNLOCK_IRQ=y
# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set
# CONFIG_INLINE_READ_TRYLOCK is not set
# CONFIG_INLINE_READ_LOCK is not set
# CONFIG_INLINE_READ_LOCK_BH is not set
# CONFIG_INLINE_READ_LOCK_IRQ is not set
# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set
CONFIG_INLINE_READ_UNLOCK=y
# CONFIG_INLINE_READ_UNLOCK_BH is not set
CONFIG_INLINE_READ_UNLOCK_IRQ=y
# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set
# CONFIG_INLINE_WRITE_TRYLOCK is not set
# CONFIG_INLINE_WRITE_LOCK is not set
# CONFIG_INLINE_WRITE_LOCK_BH is not set
# CONFIG_INLINE_WRITE_LOCK_IRQ is not set
# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set
CONFIG_INLINE_WRITE_UNLOCK=y
# CONFIG_INLINE_WRITE_UNLOCK_BH is not set
CONFIG_INLINE_WRITE_UNLOCK_IRQ=y
# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set
# CONFIG_MUTEX_SPIN_ON_OWNER is not set
CONFIG_FREEZER=y

#
# System Type
#
CONFIG_MMU=y
# CONFIG_ARCH_INTEGRATOR is not set
# CONFIG_ARCH_REALVIEW is not set
# CONFIG_ARCH_VERSATILE is not set
# CONFIG_ARCH_VEXPRESS is not set
# CONFIG_ARCH_AT91 is not set
# CONFIG_ARCH_BCMRING is not set
# CONFIG_ARCH_CLPS711X is not set
# CONFIG_ARCH_CNS3XXX is not set
# CONFIG_ARCH_GEMINI is not set
# CONFIG_ARCH_EBSA110 is not set
# CONFIG_ARCH_EP93XX is not set
# CONFIG_ARCH_FOOTBRIDGE is not set
# CONFIG_ARCH_MXC is not set
# CONFIG_ARCH_MXS is not set
# CONFIG_ARCH_NETX is not set
# CONFIG_ARCH_H720X is not set
# CONFIG_ARCH_IOP13XX is not set
# CONFIG_ARCH_IOP32X is not set
# CONFIG_ARCH_IOP33X is not set
# CONFIG_ARCH_IXP23XX is not set
# CONFIG_ARCH_IXP2000 is not set
# CONFIG_ARCH_IXP4XX is not set
# CONFIG_ARCH_DOVE is not set
# CONFIG_ARCH_KIRKWOOD is not set
# CONFIG_ARCH_LOKI is not set
# CONFIG_ARCH_LPC32XX is not set
# CONFIG_ARCH_MV78XX0 is not set
# CONFIG_ARCH_ORION5X is not set
# CONFIG_ARCH_MMP is not set
# CONFIG_ARCH_KS8695 is not set
# CONFIG_ARCH_W90X900 is not set
# CONFIG_ARCH_NUC93X is not set
# CONFIG_ARCH_TEGRA is not set
# CONFIG_ARCH_PNX4008 is not set
# CONFIG_ARCH_PXA is not set
# CONFIG_ARCH_MSM is not set
CONFIG_ARCH_SHMOBILE=y
# CONFIG_ARCH_RPC is not set
# CONFIG_ARCH_SA1100 is not set
# CONFIG_ARCH_S3C2410 is not set
# CONFIG_ARCH_S3C64XX is not set
# CONFIG_ARCH_S5P64X0 is not set
# CONFIG_ARCH_S5PC100 is not set
# CONFIG_ARCH_S5PV210 is not set
# CONFIG_ARCH_EXYNOS4 is not set
# CONFIG_ARCH_SHARK is not set
# CONFIG_ARCH_TCC_926 is not set
# CONFIG_ARCH_U300 is not set
# CONFIG_ARCH_U8500 is not set
# CONFIG_ARCH_NOMADIK is not set
# CONFIG_ARCH_DAVINCI is not set
# CONFIG_ARCH_OMAP is not set
# CONFIG_PLAT_SPEAR is not set
# CONFIG_ARCH_VT8500 is not set
# CONFIG_GPIO_PCA953X is not set
# CONFIG_KEYBOARD_GPIO_POLLED is not set

#
# System MMU
#

#
# SH-Mobile System Type
#
# CONFIG_ARCH_SH7367 is not set
# CONFIG_ARCH_SH7377 is not set
CONFIG_ARCH_SH7372=y
# CONFIG_ARCH_SH73A0 is not set

#
# SH-Mobile Board Type
#
# CONFIG_MACH_AP4EVB is not set
CONFIG_MACH_MACKEREL=y

#
# SH-Mobile System Configuration
#

#
# Memory configuration
#
CONFIG_MEMORY_START=0x40000000
CONFIG_MEMORY_SIZE=0x10000000

#
# Timer and clock configuration
#
CONFIG_SHMOBILE_TIMER_HZ=128
CONFIG_SH_TIMER_CMT=y
# CONFIG_SH_TIMER_TMU is not set
CONFIG_SH_CLK_CPG=y

#
# SuperH / SH-Mobile Driver Options
#

#
# Interrupt controller options
#
# CONFIG_INTC_USERIMASK is not set
# CONFIG_INTC_MAPPING_DEBUG is not set

#
# Processor Type
#
CONFIG_CPU_V7=y
CONFIG_CPU_32v6K=y
CONFIG_CPU_32v7=y
CONFIG_CPU_ABRT_EV7=y
CONFIG_CPU_PABRT_V7=y
CONFIG_CPU_CACHE_V7=y
CONFIG_CPU_CACHE_VIPT=y
CONFIG_CPU_COPY_V6=y
CONFIG_CPU_TLB_V7=y
CONFIG_CPU_HAS_ASID=y
CONFIG_CPU_CP15=y
CONFIG_CPU_CP15_MMU=y

#
# Processor Features
#
CONFIG_ARM_THUMB=y
# CONFIG_ARM_THUMBEE is not set
# CONFIG_SWP_EMULATE is not set
# CONFIG_CPU_ICACHE_DISABLE is not set
# CONFIG_CPU_DCACHE_DISABLE is not set
CONFIG_CPU_BPREDICT_DISABLE=y
CONFIG_OUTER_CACHE=y
CONFIG_OUTER_CACHE_SYNC=y
CONFIG_CACHE_L2X0=y
CONFIG_CACHE_PL310=y
CONFIG_ARM_L1_CACHE_SHIFT=5
CONFIG_ARM_DMA_MEM_BUFFERABLE=y
CONFIG_CPU_HAS_PMU=y
CONFIG_MULTI_IRQ_HANDLER=y
CONFIG_ARM_ERRATA_430973=y
CONFIG_ARM_ERRATA_458693=y
# CONFIG_ARM_ERRATA_460075 is not set
# CONFIG_PL310_ERRATA_588369 is not set
# CONFIG_PL310_ERRATA_727915 is not set
# CONFIG_ARM_ERRATA_743622 is not set
CONFIG_ARM_ERRATA_753970=y
# CONFIG_ARM_ERRATA_754322 is not set

#
# Bus support
#
# CONFIG_PCI_SYSCALL is not set
# CONFIG_ARCH_SUPPORTS_MSI is not set
# CONFIG_PCCARD is not set

#
# Kernel Features
#
# CONFIG_NO_HZ is not set
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
# CONFIG_SMP is not set
CONFIG_VMSPLIT_3G=y
# CONFIG_VMSPLIT_2G is not set
# CONFIG_VMSPLIT_1G is not set
CONFIG_PAGE_OFFSET=0xC0000000
CONFIG_PREEMPT_NONE=y
# CONFIG_PREEMPT_VOLUNTARY is not set
# CONFIG_PREEMPT is not set
CONFIG_HZ=128
# CONFIG_THUMB2_KERNEL is not set
CONFIG_AEABI=y
# CONFIG_OABI_COMPAT is not set
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
CONFIG_HAVE_ARCH_PFN_VALID=y
# CONFIG_HIGHMEM is not set
CONFIG_SELECT_MEMORY_MODEL=y
CONFIG_FLATMEM_MANUAL=y
CONFIG_FLATMEM=y
CONFIG_FLAT_NODE_MEM_MAP=y
CONFIG_HAVE_MEMBLOCK=y
CONFIG_PAGEFLAGS_EXTENDED=y
CONFIG_SPLIT_PTLOCK_CPUS=4
# CONFIG_COMPACTION is not set
# CONFIG_PHYS_ADDR_T_64BIT is not set
CONFIG_ZONE_DMA_FLAG=0
CONFIG_VIRT_TO_BUS=y
# CONFIG_KSM is not set
CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
CONFIG_NEED_PER_CPU_KM=y
# CONFIG_CLEANCACHE is not set
CONFIG_FORCE_MAX_ZONEORDER=13
CONFIG_ALIGNMENT_TRAP=y
# CONFIG_UACCESS_WITH_MEMCPY is not set
# CONFIG_SECCOMP is not set
# CONFIG_CC_STACKPROTECTOR is not set
# CONFIG_DEPRECATED_PARAM_STRUCT is not set

#
# Boot options
#
CONFIG_USE_OF=y
CONFIG_ZBOOT_ROM_TEXT=0x0
CONFIG_ZBOOT_ROM_BSS=0x0
CONFIG_CMDLINE="console=tty0 console=ttySC0,115200 earlyprintk=sh-sci.0,115200 ignore_loglevel memblock=debug memchunk.veu0=4m no_console_suspend"
# CONFIG_CMDLINE_FROM_BOOTLOADER is not set
# CONFIG_CMDLINE_EXTEND is not set
CONFIG_CMDLINE_FORCE=y
# CONFIG_XIP_KERNEL is not set
CONFIG_KEXEC=y
CONFIG_ATAGS_PROC=y
# CONFIG_CRASH_DUMP is not set
# CONFIG_AUTO_ZRELADDR is not set

#
# CPU Power Management
#
# CONFIG_CPU_IDLE is not set

#
# Floating point emulation
#

#
# At least one emulation must be selected
#
# CONFIG_VFP is not set

#
# Userspace binary formats
#
CONFIG_BINFMT_ELF=y
# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
CONFIG_HAVE_AOUT=y
# CONFIG_BINFMT_AOUT is not set
# CONFIG_BINFMT_MISC is not set

#
# Power management options
#
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
CONFIG_PM_SLEEP=y
CONFIG_PM_RUNTIME=y
CONFIG_PM=y
CONFIG_PM_DEBUG=y
CONFIG_PM_ADVANCED_DEBUG=y
# CONFIG_PM_TEST_SUSPEND is not set
CONFIG_CAN_PM_TRACE=y
# CONFIG_APM_EMULATION is not set
CONFIG_PM_RUNTIME_CLK=y
CONFIG_PM_GENERIC_DOMAINS=y
CONFIG_ARCH_SUSPEND_POSSIBLE=y
CONFIG_NET=y

#
# Networking options
#
CONFIG_PACKET=y
CONFIG_UNIX=y
CONFIG_XFRM=y
# CONFIG_XFRM_USER is not set
# CONFIG_XFRM_SUB_POLICY is not set
# CONFIG_XFRM_MIGRATE is not set
# CONFIG_XFRM_STATISTICS is not set
# CONFIG_NET_KEY is not set
CONFIG_INET=y
CONFIG_IP_MULTICAST=y
# CONFIG_IP_ADVANCED_ROUTER is not set
# CONFIG_IP_PNP is not set
# CONFIG_NET_IPIP is not set
# CONFIG_NET_IPGRE_DEMUX is not set
# CONFIG_IP_MROUTE is not set
# CONFIG_ARPD is not set
# CONFIG_SYN_COOKIES is not set
# CONFIG_INET_AH is not set
# CONFIG_INET_ESP is not set
# CONFIG_INET_IPCOMP is not set
# CONFIG_INET_XFRM_TUNNEL is not set
# CONFIG_INET_TUNNEL is not set
CONFIG_INET_XFRM_MODE_TRANSPORT=y
CONFIG_INET_XFRM_MODE_TUNNEL=y
CONFIG_INET_XFRM_MODE_BEET=y
CONFIG_INET_LRO=y
CONFIG_INET_DIAG=y
CONFIG_INET_TCP_DIAG=y
# CONFIG_TCP_CONG_ADVANCED is not set
CONFIG_TCP_CONG_CUBIC=y
CONFIG_DEFAULT_TCP_CONG="cubic"
# CONFIG_TCP_MD5SIG is not set
# CONFIG_IPV6 is not set
# CONFIG_NETWORK_SECMARK is not set
# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
# CONFIG_NETFILTER is not set
# CONFIG_IP_DCCP is not set
# CONFIG_IP_SCTP is not set
# CONFIG_RDS is not set
# CONFIG_TIPC is not set
# CONFIG_ATM is not set
# CONFIG_L2TP is not set
# CONFIG_BRIDGE is not set
# CONFIG_NET_DSA is not set
# CONFIG_VLAN_8021Q is not set
# CONFIG_DECNET is not set
# CONFIG_LLC2 is not set
# CONFIG_IPX is not set
# CONFIG_ATALK is not set
# CONFIG_X25 is not set
# CONFIG_LAPB is not set
# CONFIG_ECONET is not set
# CONFIG_WAN_ROUTER is not set
# CONFIG_PHONET is not set
# CONFIG_IEEE802154 is not set
# CONFIG_NET_SCHED is not set
# CONFIG_DCB is not set
# CONFIG_BATMAN_ADV is not set

#
# Network testing
#
# CONFIG_NET_PKTGEN is not set
# CONFIG_HAMRADIO is not set
# CONFIG_CAN is not set
# CONFIG_IRDA is not set
# CONFIG_BT is not set
# CONFIG_AF_RXRPC is not set
# CONFIG_WIRELESS is not set
# CONFIG_WIMAX is not set
# CONFIG_RFKILL is not set
# CONFIG_NET_9P is not set
# CONFIG_CAIF is not set
# CONFIG_CEPH_LIB is not set

#
# Device Drivers
#

#
# Generic Driver Options
#
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
# CONFIG_DEVTMPFS is not set
CONFIG_STANDALONE=y
CONFIG_PREVENT_FIRMWARE_BUILD=y
CONFIG_FW_LOADER=y
CONFIG_FIRMWARE_IN_KERNEL=y
CONFIG_EXTRA_FIRMWARE=""
# CONFIG_DEBUG_DRIVER is not set
# CONFIG_DEBUG_DEVRES is not set
# CONFIG_SYS_HYPERVISOR is not set
# CONFIG_CONNECTOR is not set
CONFIG_MTD=y
# CONFIG_MTD_DEBUG is not set
# CONFIG_MTD_REDBOOT_PARTS is not set
# CONFIG_MTD_CMDLINE_PARTS is not set
# CONFIG_MTD_AFS_PARTS is not set
CONFIG_MTD_OF_PARTS=y
# CONFIG_MTD_AR7_PARTS is not set

#
# User Modules And Translation Layers
#
CONFIG_MTD_CHAR=y
CONFIG_MTD_BLKDEVS=y
CONFIG_MTD_BLOCK=y
# CONFIG_FTL is not set
# CONFIG_NFTL is not set
# CONFIG_INFTL is not set
# CONFIG_RFD_FTL is not set
# CONFIG_SSFDC is not set
# CONFIG_SM_FTL is not set
# CONFIG_MTD_OOPS is not set
# CONFIG_MTD_SWAP is not set

#
# RAM/ROM/Flash chip drivers
#
CONFIG_MTD_CFI=y
# CONFIG_MTD_JEDECPROBE is not set
CONFIG_MTD_GEN_PROBE=y
# CONFIG_MTD_CFI_ADV_OPTIONS is not set
CONFIG_MTD_MAP_BANK_WIDTH_1=y
CONFIG_MTD_MAP_BANK_WIDTH_2=y
CONFIG_MTD_MAP_BANK_WIDTH_4=y
# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
CONFIG_MTD_CFI_I1=y
CONFIG_MTD_CFI_I2=y
# CONFIG_MTD_CFI_I4 is not set
# CONFIG_MTD_CFI_I8 is not set
CONFIG_MTD_CFI_INTELEXT=y
# CONFIG_MTD_CFI_AMDSTD is not set
# CONFIG_MTD_CFI_STAA is not set
CONFIG_MTD_CFI_UTIL=y
# CONFIG_MTD_RAM is not set
# CONFIG_MTD_ROM is not set
# CONFIG_MTD_ABSENT is not set

#
# Mapping drivers for chip access
#
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_PHYSMAP=y
# CONFIG_MTD_PHYSMAP_COMPAT is not set
CONFIG_MTD_PHYSMAP_OF=y
# CONFIG_MTD_ARM_INTEGRATOR is not set
# CONFIG_MTD_PLATRAM is not set

#
# Self-contained MTD device drivers
#
# CONFIG_MTD_SLRAM is not set
# CONFIG_MTD_PHRAM is not set
# CONFIG_MTD_MTDRAM is not set
# CONFIG_MTD_BLOCK2MTD is not set

#
# Disk-On-Chip Device Drivers
#
# CONFIG_MTD_DOC2000 is not set
# CONFIG_MTD_DOC2001 is not set
# CONFIG_MTD_DOC2001PLUS is not set
CONFIG_MTD_NAND_ECC=y
# CONFIG_MTD_NAND_ECC_SMC is not set
CONFIG_MTD_NAND=y
# CONFIG_MTD_NAND_VERIFY_WRITE is not set
# CONFIG_MTD_NAND_ECC_BCH is not set
# CONFIG_MTD_SM_COMMON is not set
# CONFIG_MTD_NAND_MUSEUM_IDS is not set
# CONFIG_MTD_NAND_GPIO is not set
CONFIG_MTD_NAND_IDS=y
# CONFIG_MTD_NAND_DISKONCHIP is not set
# CONFIG_MTD_NAND_NANDSIM is not set
# CONFIG_MTD_NAND_PLATFORM is not set
# CONFIG_MTD_ALAUDA is not set
CONFIG_MTD_NAND_SH_FLCTL=y
# CONFIG_MTD_ONENAND is not set

#
# LPDDR flash memory drivers
#
# CONFIG_MTD_LPDDR is not set
# CONFIG_MTD_UBI is not set
CONFIG_DTC=y
CONFIG_OF=y

#
# Device Tree and Open Firmware support
#
CONFIG_PROC_DEVICETREE=y
CONFIG_OF_FLATTREE=y
CONFIG_OF_EARLY_FLATTREE=y
CONFIG_OF_ADDRESS=y
CONFIG_OF_IRQ=y
CONFIG_OF_DEVICE=y
CONFIG_OF_GPIO=y
CONFIG_OF_I2C=y
CONFIG_OF_NET=y
CONFIG_OF_MDIO=y
# CONFIG_PARPORT is not set
# CONFIG_BLK_DEV is not set
# CONFIG_SENSORS_LIS3LV02D is not set
# CONFIG_MISC_DEVICES is not set
CONFIG_HAVE_IDE=y
# CONFIG_IDE is not set

#
# SCSI device support
#
CONFIG_SCSI_MOD=y
# CONFIG_RAID_ATTRS is not set
# CONFIG_SCSI is not set
# CONFIG_SCSI_DMA is not set
# CONFIG_SCSI_NETLINK is not set
# CONFIG_ATA is not set
# CONFIG_MD is not set
CONFIG_NETDEVICES=y
# CONFIG_DUMMY is not set
# CONFIG_BONDING is not set
# CONFIG_MACVLAN is not set
# CONFIG_EQUALIZER is not set
# CONFIG_TUN is not set
# CONFIG_VETH is not set
CONFIG_MII=y
CONFIG_PHYLIB=y

#
# MII PHY device drivers
#
# CONFIG_MARVELL_PHY is not set
# CONFIG_DAVICOM_PHY is not set
# CONFIG_QSEMI_PHY is not set
# CONFIG_LXT_PHY is not set
# CONFIG_CICADA_PHY is not set
# CONFIG_VITESSE_PHY is not set
# CONFIG_SMSC_PHY is not set
# CONFIG_BROADCOM_PHY is not set
# CONFIG_BCM63XX_PHY is not set
# CONFIG_ICPLUS_PHY is not set
# CONFIG_REALTEK_PHY is not set
# CONFIG_NATIONAL_PHY is not set
# CONFIG_STE10XP is not set
# CONFIG_LSI_ET1011C_PHY is not set
# CONFIG_MICREL_PHY is not set
# CONFIG_FIXED_PHY is not set
# CONFIG_MDIO_BITBANG is not set
CONFIG_NET_ETHERNET=y
# CONFIG_AX88796 is not set
# CONFIG_SMC91X is not set
# CONFIG_DM9000 is not set
# CONFIG_ETHOC is not set
# CONFIG_SMC911X is not set
CONFIG_SMSC911X=y
# CONFIG_SMSC911X_ARCH_HOOKS is not set
# CONFIG_DNET is not set
# CONFIG_IBM_NEW_EMAC_ZMII is not set
# CONFIG_IBM_NEW_EMAC_RGMII is not set
# CONFIG_IBM_NEW_EMAC_TAH is not set
# CONFIG_IBM_NEW_EMAC_EMAC4 is not set
# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set
# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set
# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set
# CONFIG_B44 is not set
# CONFIG_KS8851_MLL is not set
# CONFIG_FTMAC100 is not set
# CONFIG_NETDEV_1000 is not set
# CONFIG_NETDEV_10000 is not set
# CONFIG_WLAN is not set

#
# Enable WiMAX (Networking options) to see the WiMAX drivers
#

#
# USB Network Adapters
#
# CONFIG_USB_CATC is not set
# CONFIG_USB_KAWETH is not set
# CONFIG_USB_PEGASUS is not set
# CONFIG_USB_RTL8150 is not set
CONFIG_USB_USBNET=y
# CONFIG_USB_NET_AX8817X is not set
# CONFIG_USB_NET_CDCETHER is not set
# CONFIG_USB_NET_CDC_EEM is not set
CONFIG_USB_NET_CDC_NCM=y
CONFIG_USB_NET_DM9601=y
# CONFIG_USB_NET_SMSC75XX is not set
# CONFIG_USB_NET_SMSC95XX is not set
# CONFIG_USB_NET_GL620A is not set
# CONFIG_USB_NET_NET1080 is not set
# CONFIG_USB_NET_PLUSB is not set
# CONFIG_USB_NET_MCS7830 is not set
# CONFIG_USB_NET_RNDIS_HOST is not set
# CONFIG_USB_NET_CDC_SUBSET is not set
# CONFIG_USB_NET_ZAURUS is not set
# CONFIG_USB_NET_CX82310_ETH is not set
# CONFIG_USB_NET_INT51X1 is not set
# CONFIG_USB_IPHETH is not set
# CONFIG_USB_SIERRA_NET is not set
# CONFIG_WAN is not set

#
# CAIF transport drivers
#
# CONFIG_PPP is not set
# CONFIG_SLIP is not set
# CONFIG_NETCONSOLE is not set
# CONFIG_NETPOLL is not set
# CONFIG_NET_POLL_CONTROLLER is not set
# CONFIG_ISDN is not set
# CONFIG_PHONE is not set

#
# Input device support
#
CONFIG_INPUT=y
# CONFIG_INPUT_FF_MEMLESS is not set
# CONFIG_INPUT_POLLDEV is not set
# CONFIG_INPUT_SPARSEKMAP is not set

#
# Userland interfaces
#
CONFIG_INPUT_MOUSEDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_JOYDEV is not set
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_EVBUG is not set

#
# Input Device Drivers
#
CONFIG_INPUT_KEYBOARD=y
# CONFIG_KEYBOARD_ADP5588 is not set
# CONFIG_KEYBOARD_ADP5589 is not set
# CONFIG_KEYBOARD_ATKBD is not set
# CONFIG_KEYBOARD_QT1070 is not set
# CONFIG_KEYBOARD_QT2160 is not set
# CONFIG_KEYBOARD_LKKBD is not set
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_KEYBOARD_TCA6416 is not set
# CONFIG_KEYBOARD_MATRIX is not set
CONFIG_KEYBOARD_LM8323=y
# CONFIG_KEYBOARD_MAX7359 is not set
# CONFIG_KEYBOARD_MCS is not set
# CONFIG_KEYBOARD_MPR121 is not set
# CONFIG_KEYBOARD_NEWTON is not set
# CONFIG_KEYBOARD_OPENCORES is not set
# CONFIG_KEYBOARD_STOWAWAY is not set
# CONFIG_KEYBOARD_SUNKBD is not set
CONFIG_KEYBOARD_SH_KEYSC=y
# CONFIG_KEYBOARD_XTKBD is not set
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
CONFIG_INPUT_TOUCHSCREEN=y
# CONFIG_TOUCHSCREEN_AD7879 is not set
# CONFIG_TOUCHSCREEN_ATMEL_MXT is not set
# CONFIG_TOUCHSCREEN_BU21013 is not set
# CONFIG_TOUCHSCREEN_CY8CTMG110 is not set
# CONFIG_TOUCHSCREEN_DYNAPRO is not set
# CONFIG_TOUCHSCREEN_HAMPSHIRE is not set
# CONFIG_TOUCHSCREEN_EETI is not set
# CONFIG_TOUCHSCREEN_FUJITSU is not set
# CONFIG_TOUCHSCREEN_GUNZE is not set
# CONFIG_TOUCHSCREEN_ELO is not set
# CONFIG_TOUCHSCREEN_WACOM_W8001 is not set
# CONFIG_TOUCHSCREEN_MAX11801 is not set
# CONFIG_TOUCHSCREEN_MCS5000 is not set
# CONFIG_TOUCHSCREEN_MTOUCH is not set
# CONFIG_TOUCHSCREEN_INEXIO is not set
# CONFIG_TOUCHSCREEN_MK712 is not set
# CONFIG_TOUCHSCREEN_PENMOUNT is not set
# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set
# CONFIG_TOUCHSCREEN_TOUCHWIN is not set
# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set
# CONFIG_TOUCHSCREEN_TOUCHIT213 is not set
CONFIG_TOUCHSCREEN_TSC2007=y
# CONFIG_TOUCHSCREEN_W90X900 is not set
# CONFIG_TOUCHSCREEN_ST1232 is not set
# CONFIG_TOUCHSCREEN_TPS6507X is not set
# CONFIG_INPUT_MISC is not set

#
# Hardware I/O ports
#
CONFIG_SERIO=y
CONFIG_SERIO_SERPORT=y
# CONFIG_SERIO_RAW is not set
# CONFIG_SERIO_ALTERA_PS2 is not set
# CONFIG_SERIO_PS2MULT is not set
# CONFIG_GAMEPORT is not set

#
# Character devices
#
CONFIG_VT=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_VT_CONSOLE=y
CONFIG_HW_CONSOLE=y
# CONFIG_VT_HW_CONSOLE_BINDING is not set
CONFIG_UNIX98_PTYS=y
# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
# CONFIG_LEGACY_PTYS is not set
# CONFIG_SERIAL_NONSTANDARD is not set
# CONFIG_N_GSM is not set
# CONFIG_TRACE_SINK is not set
CONFIG_DEVKMEM=y

#
# Serial drivers
#
# CONFIG_SERIAL_8250 is not set

#
# Non-8250 serial port support
#
CONFIG_SERIAL_SH_SCI=y
CONFIG_SERIAL_SH_SCI_NR_UARTS=8
CONFIG_SERIAL_SH_SCI_CONSOLE=y
CONFIG_SERIAL_CORE=y
CONFIG_SERIAL_CORE_CONSOLE=y
# CONFIG_SERIAL_TIMBERDALE is not set
# CONFIG_SERIAL_ALTERA_JTAGUART is not set
# CONFIG_SERIAL_ALTERA_UART is not set
# CONFIG_SERIAL_XILINX_PS_UART is not set
# CONFIG_HVC_DCC is not set
# CONFIG_IPMI_HANDLER is not set
# CONFIG_HW_RANDOM is not set
# CONFIG_R3964 is not set
# CONFIG_RAW_DRIVER is not set
# CONFIG_TCG_TPM is not set
# CONFIG_RAMOOPS is not set
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
CONFIG_I2C_COMPAT=y
# CONFIG_I2C_CHARDEV is not set
# CONFIG_I2C_MUX is not set
CONFIG_I2C_HELPER_AUTO=y

#
# I2C Hardware Bus support
#

#
# I2C system bus drivers (mostly embedded / system-on-chip)
#
# CONFIG_I2C_DESIGNWARE is not set
# CONFIG_I2C_GPIO is not set
# CONFIG_I2C_OCORES is not set
# CONFIG_I2C_PCA_PLATFORM is not set
# CONFIG_I2C_PXA_PCI is not set
CONFIG_I2C_SH_MOBILE=y
# CONFIG_I2C_SIMTEC is not set
# CONFIG_I2C_XILINX is not set

#
# External I2C/SMBus adapter drivers
#
CONFIG_I2C_DIOLAN_U2C=y
# CONFIG_I2C_PARPORT_LIGHT is not set
# CONFIG_I2C_TAOS_EVM is not set
# CONFIG_I2C_TINY_USB is not set

#
# Other I2C/SMBus bus drivers
#
# CONFIG_I2C_DEBUG_CORE is not set
# CONFIG_I2C_DEBUG_ALGO is not set
# CONFIG_I2C_DEBUG_BUS is not set
# CONFIG_SPI is not set

#
# PPS support
#
# CONFIG_PPS is not set

#
# PPS generators support
#

#
# PTP clock support
#

#
# Enable Device Drivers -> PPS to see the PTP clock options.
#
CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
CONFIG_ARCH_REQUIRE_GPIOLIB=y
CONFIG_GPIOLIB=y
# CONFIG_DEBUG_GPIO is not set
# CONFIG_GPIO_SYSFS is not set

#
# Memory mapped GPIO drivers:
#
# CONFIG_GPIO_BASIC_MMIO is not set
# CONFIG_GPIO_IT8761E is not set

#
# I2C GPIO expanders:
#
# CONFIG_GPIO_MAX7300 is not set
# CONFIG_GPIO_MAX732X is not set
# CONFIG_GPIO_PCF857X is not set
# CONFIG_GPIO_SX150X is not set
# CONFIG_GPIO_ADP5588 is not set

#
# PCI GPIO expanders:
#

#
# SPI GPIO expanders:
#

#
# AC97 GPIO expanders:
#

#
# MODULbus GPIO expanders:
#
# CONFIG_W1 is not set
# CONFIG_POWER_SUPPLY is not set
# CONFIG_HWMON is not set
# CONFIG_THERMAL is not set
# CONFIG_WATCHDOG is not set
CONFIG_SSB_POSSIBLE=y

#
# Sonics Silicon Backplane
#
# CONFIG_SSB is not set
CONFIG_BCMA_POSSIBLE=y

#
# Broadcom specific AMBA
#
# CONFIG_BCMA is not set
CONFIG_MFD_SUPPORT=y
# CONFIG_MFD_CORE is not set
# CONFIG_MFD_88PM860X is not set
# CONFIG_MFD_SM501 is not set
# CONFIG_MFD_ASIC3 is not set
# CONFIG_HTC_EGPIO is not set
# CONFIG_HTC_PASIC3 is not set
# CONFIG_HTC_I2CPLD is not set
# CONFIG_TPS6105X is not set
# CONFIG_TPS65010 is not set
# CONFIG_TPS6507X is not set
# CONFIG_MFD_TPS6586X is not set
# CONFIG_TWL4030_CORE is not set
# CONFIG_MFD_STMPE is not set
# CONFIG_MFD_TC3589X is not set
# CONFIG_MFD_TMIO is not set
# CONFIG_MFD_T7L66XB is not set
# CONFIG_MFD_TC6387XB is not set
# CONFIG_MFD_TC6393XB is not set
# CONFIG_PMIC_DA903X is not set
# CONFIG_PMIC_ADP5520 is not set
# CONFIG_MFD_MAX8925 is not set
# CONFIG_MFD_MAX8997 is not set
# CONFIG_MFD_MAX8998 is not set
# CONFIG_MFD_WM8400 is not set
# CONFIG_MFD_WM831X_I2C is not set
# CONFIG_MFD_WM8350_I2C is not set
# CONFIG_MFD_WM8994 is not set
# CONFIG_MFD_PCF50633 is not set
# CONFIG_ABX500_CORE is not set
# CONFIG_MFD_WL1273_CORE is not set
# CONFIG_MFD_TPS65910 is not set
# CONFIG_REGULATOR is not set
CONFIG_MEDIA_SUPPORT=y

#
# Multimedia core support
#
CONFIG_MEDIA_CONTROLLER=y
CONFIG_VIDEO_DEV=y
CONFIG_VIDEO_V4L2_COMMON=y
CONFIG_VIDEO_V4L2_SUBDEV_API=y
# CONFIG_DVB_CORE is not set
CONFIG_VIDEO_MEDIA=y

#
# Multimedia drivers
#
CONFIG_RC_CORE=y
CONFIG_LIRC=y
CONFIG_RC_MAP=y
CONFIG_IR_NEC_DECODER=y
CONFIG_IR_RC5_DECODER=y
CONFIG_IR_RC6_DECODER=y
CONFIG_IR_JVC_DECODER=y
CONFIG_IR_SONY_DECODER=y
CONFIG_IR_RC5_SZ_DECODER=y
CONFIG_IR_LIRC_CODEC=y
# CONFIG_IR_IMON is not set
# CONFIG_IR_MCEUSB is not set
# CONFIG_IR_REDRAT3 is not set
# CONFIG_IR_STREAMZAP is not set
# CONFIG_RC_LOOPBACK is not set
CONFIG_MEDIA_TUNER=y
# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
CONFIG_MEDIA_TUNER_SIMPLE=y
CONFIG_MEDIA_TUNER_TDA8290=y
CONFIG_MEDIA_TUNER_TDA827X=y
CONFIG_MEDIA_TUNER_TDA18271=y
CONFIG_MEDIA_TUNER_TDA9887=y
CONFIG_MEDIA_TUNER_TEA5761=y
CONFIG_MEDIA_TUNER_TEA5767=y
CONFIG_MEDIA_TUNER_MT20XX=y
CONFIG_MEDIA_TUNER_XC2028=y
CONFIG_MEDIA_TUNER_XC5000=y
CONFIG_MEDIA_TUNER_MC44S803=y
CONFIG_VIDEO_V4L2=y
CONFIG_VIDEOBUF_GEN=y
CONFIG_VIDEOBUF2_CORE=y
CONFIG_VIDEOBUF2_MEMOPS=y
CONFIG_VIDEOBUF2_DMA_CONTIG=y
CONFIG_VIDEO_CAPTURE_DRIVERS=y
# CONFIG_VIDEO_ADV_DEBUG is not set
CONFIG_VIDEO_FIXED_MINOR_RANGES=y
CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
CONFIG_VIDEO_IR_I2C=y

#
# Audio decoders, processors and mixers
#

#
# RDS decoders
#

#
# Video decoders
#

#
# Video and audio decoders
#

#
# MPEG video encoders
#

#
# Video encoders
#

#
# Camera sensor devices
#

#
# Video improvement chips
#

#
# Miscelaneous helper chips
#
# CONFIG_VIDEO_SH_VOU is not set
# CONFIG_VIDEO_VIVI is not set
# CONFIG_VIDEO_CPIA2 is not set
# CONFIG_VIDEO_TIMBERDALE is not set
# CONFIG_VIDEO_SR030PC30 is not set
CONFIG_VIDEO_NOON010PC30=y
# CONFIG_VIDEO_M5MOLS is not set
CONFIG_SOC_CAMERA=y
CONFIG_SOC_CAMERA_IMX074=y
# CONFIG_SOC_CAMERA_MT9M001 is not set
# CONFIG_SOC_CAMERA_MT9M111 is not set
# CONFIG_SOC_CAMERA_MT9T031 is not set
# CONFIG_SOC_CAMERA_MT9T112 is not set
# CONFIG_SOC_CAMERA_MT9V022 is not set
# CONFIG_SOC_CAMERA_RJ54N1 is not set
# CONFIG_SOC_CAMERA_TW9910 is not set
CONFIG_SOC_CAMERA_PLATFORM=y
# CONFIG_SOC_CAMERA_OV2640 is not set
# CONFIG_SOC_CAMERA_OV6650 is not set
# CONFIG_SOC_CAMERA_OV772X is not set
# CONFIG_SOC_CAMERA_OV9640 is not set
CONFIG_SOC_CAMERA_OV9740=y
CONFIG_VIDEO_SH_MOBILE_CSI2=y
CONFIG_VIDEO_SH_MOBILE_CEU=y
# CONFIG_V4L_USB_DRIVERS is not set
# CONFIG_V4L_MEM2MEM_DRIVERS is not set
# CONFIG_RADIO_ADAPTERS is not set

#
# Graphics support
#
# CONFIG_DRM is not set
# CONFIG_VGASTATE is not set
# CONFIG_VIDEO_OUTPUT_CONTROL is not set
CONFIG_FB=y
# CONFIG_FIRMWARE_EDID is not set
# CONFIG_FB_DDC is not set
# CONFIG_FB_BOOT_VESA_SUPPORT is not set
# CONFIG_FB_CFB_FILLRECT is not set
# CONFIG_FB_CFB_COPYAREA is not set
# CONFIG_FB_CFB_IMAGEBLIT is not set
# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_IMAGEBLIT=y
# CONFIG_FB_FOREIGN_ENDIAN is not set
CONFIG_FB_SYS_FOPS=y
# CONFIG_FB_WMT_GE_ROPS is not set
CONFIG_FB_DEFERRED_IO=y
# CONFIG_FB_SVGALIB is not set
# CONFIG_FB_MACMODES is not set
CONFIG_FB_BACKLIGHT=y
CONFIG_FB_MODE_HELPERS=y
# CONFIG_FB_TILEBLITTING is not set

#
# Frame buffer hardware drivers
#
# CONFIG_FB_S1D13XXX is not set
CONFIG_FB_SH_MOBILE_LCDC=y
CONFIG_FB_SH_MOBILE_HDMI=y
CONFIG_FB_SH_MOBILE_MERAM=y
# CONFIG_FB_UDL is not set
# CONFIG_FB_VIRTUAL is not set
# CONFIG_FB_METRONOME is not set
# CONFIG_FB_BROADSHEET is not set
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_PLATFORM=y
CONFIG_BACKLIGHT_CLASS_DEVICE=y
CONFIG_BACKLIGHT_GENERIC=y
CONFIG_BACKLIGHT_ADP8860=y

#
# Display device support
#
# CONFIG_DISPLAY_SUPPORT is not set

#
# Console display driver support
#
CONFIG_DUMMY_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FONTS=y
CONFIG_FONT_8x8=y
# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_6x11 is not set
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_SUN8x16 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_10x18 is not set
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
CONFIG_LOGO_LINUX_VGA16=y
# CONFIG_LOGO_LINUX_CLUT224 is not set
CONFIG_SOUND=y
CONFIG_SOUND_OSS_CORE=y
CONFIG_SOUND_OSS_CORE_PRECLAIM=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_JACK=y
# CONFIG_SND_SEQUENCER is not set
CONFIG_SND_OSSEMUL=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
CONFIG_SND_PCM_OSS_PLUGINS=y
# CONFIG_SND_DYNAMIC_MINORS is not set
CONFIG_SND_SUPPORT_OLD_API=y
CONFIG_SND_VERBOSE_PROCFS=y
# CONFIG_SND_VERBOSE_PRINTK is not set
# CONFIG_SND_DEBUG is not set
# CONFIG_SND_RAWMIDI_SEQ is not set
# CONFIG_SND_OPL3_LIB_SEQ is not set
# CONFIG_SND_OPL4_LIB_SEQ is not set
# CONFIG_SND_SBAWE_SEQ is not set
# CONFIG_SND_EMU10K1_SEQ is not set
# CONFIG_SND_DRIVERS is not set
# CONFIG_SND_ARM is not set
# CONFIG_SND_USB is not set
CONFIG_SND_SOC=y
# CONFIG_SND_SOC_CACHE_LZO is not set

#
# SoC Audio support for SuperH
#
CONFIG_SND_SOC_SH4_FSI=y
CONFIG_SND_FSI_AK4642=y
# CONFIG_SND_FSI_DA7210 is not set
# CONFIG_SND_FSI_HDMI is not set
CONFIG_SND_SOC_I2C_AND_SPI=y
# CONFIG_SND_SOC_ALL_CODECS is not set
CONFIG_SND_SOC_AK4642=y
# CONFIG_SOUND_PRIME is not set
# CONFIG_HID_SUPPORT is not set
CONFIG_USB_SUPPORT=y
CONFIG_USB_ARCH_HAS_HCD=y
# CONFIG_USB_ARCH_HAS_OHCI is not set
# CONFIG_USB_ARCH_HAS_EHCI is not set
CONFIG_USB=y
# CONFIG_USB_DEBUG is not set
# CONFIG_USB_ANNOUNCE_NEW_DEVICES is not set

#
# Miscellaneous USB options
#
# CONFIG_USB_DEVICEFS is not set
CONFIG_USB_DEVICE_CLASS=y
# CONFIG_USB_DYNAMIC_MINORS is not set
# CONFIG_USB_SUSPEND is not set
# CONFIG_USB_MON is not set
# CONFIG_USB_WUSB is not set
# CONFIG_USB_WUSB_CBAF is not set

#
# USB Host Controller Drivers
#
# CONFIG_USB_C67X00_HCD is not set
# CONFIG_USB_OXU210HP_HCD is not set
# CONFIG_USB_ISP116X_HCD is not set
# CONFIG_USB_ISP1760_HCD is not set
# CONFIG_USB_ISP1362_HCD is not set
# CONFIG_USB_SL811_HCD is not set
CONFIG_USB_R8A66597_HCD=y
# CONFIG_USB_HWA_HCD is not set
# CONFIG_USB_MUSB_HDRC is not set
CONFIG_USB_RENESAS_USBHS=y

#
# USB Device Class drivers
#
# CONFIG_USB_ACM is not set
# CONFIG_USB_PRINTER is not set
# CONFIG_USB_WDM is not set
# CONFIG_USB_TMC is not set

#
# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may
#

#
# also be needed; see USB_STORAGE Help for more info
#
# CONFIG_USB_LIBUSUAL is not set

#
# USB Imaging devices
#
# CONFIG_USB_MDC800 is not set

#
# USB port drivers
#
# CONFIG_USB_SERIAL is not set

#
# USB Miscellaneous drivers
#
# CONFIG_USB_EMI62 is not set
# CONFIG_USB_EMI26 is not set
# CONFIG_USB_ADUTUX is not set
# CONFIG_USB_SEVSEG is not set
# CONFIG_USB_RIO500 is not set
# CONFIG_USB_LEGOTOWER is not set
# CONFIG_USB_LCD is not set
# CONFIG_USB_LED is not set
# CONFIG_USB_CYPRESS_CY7C63 is not set
# CONFIG_USB_CYTHERM is not set
# CONFIG_USB_IDMOUSE is not set
# CONFIG_USB_FTDI_ELAN is not set
# CONFIG_USB_APPLEDISPLAY is not set
# CONFIG_USB_LD is not set
# CONFIG_USB_TRANCEVIBRATOR is not set
# CONFIG_USB_IOWARRIOR is not set
# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
# CONFIG_USB_YUREX is not set
# CONFIG_USB_GADGET is not set

#
# OTG and related infrastructure
#
# CONFIG_USB_GPIO_VBUS is not set
# CONFIG_USB_ULPI is not set
# CONFIG_NOP_USB_XCEIV is not set
CONFIG_MMC=y
# CONFIG_MMC_DEBUG is not set
# CONFIG_MMC_UNSAFE_RESUME is not set
# CONFIG_MMC_CLKGATE is not set

#
# MMC/SD/SDIO Card Drivers
#
CONFIG_MMC_BLOCK=y
CONFIG_MMC_BLOCK_MINORS=8
CONFIG_MMC_BLOCK_BOUNCE=y
# CONFIG_SDIO_UART is not set
# CONFIG_MMC_TEST is not set

#
# MMC/SD/SDIO Host Controller Drivers
#
# CONFIG_MMC_SDHCI is not set
CONFIG_MMC_TMIO_CORE=y
CONFIG_MMC_SDHI=y
# CONFIG_MMC_DW is not set
CONFIG_MMC_SH_MMCIF=y
CONFIG_MMC_VUB300=y
# CONFIG_MMC_USHC is not set
# CONFIG_MEMSTICK is not set
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y

#
# LED drivers
#
CONFIG_LEDS_LM3530=y
CONFIG_LEDS_PCA9532=y
# CONFIG_LEDS_PCA9532_GPIO is not set
CONFIG_LEDS_GPIO=y
CONFIG_LEDS_GPIO_PLATFORM=y
CONFIG_LEDS_GPIO_OF=y
CONFIG_LEDS_LP3944=y
CONFIG_LEDS_LP5521=y
CONFIG_LEDS_LP5523=y
CONFIG_LEDS_PCA955X=y
CONFIG_LEDS_BD2802=y
CONFIG_LEDS_LT3593=y
CONFIG_LEDS_TRIGGERS=y

#
# LED Triggers
#
CONFIG_LEDS_TRIGGER_TIMER=y
CONFIG_LEDS_TRIGGER_HEARTBEAT=y
CONFIG_LEDS_TRIGGER_BACKLIGHT=y
CONFIG_LEDS_TRIGGER_GPIO=y
CONFIG_LEDS_TRIGGER_DEFAULT_ON=y

#
# iptables trigger is under Netfilter config (LED target)
#
# CONFIG_NFC_DEVICES is not set
# CONFIG_ACCESSIBILITY is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# CONFIG_RTC_DEBUG is not set

#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
CONFIG_RTC_INTF_DEV=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
# CONFIG_RTC_DRV_TEST is not set

#
# I2C RTC drivers
#
# CONFIG_RTC_DRV_DS1307 is not set
# CONFIG_RTC_DRV_DS1374 is not set
# CONFIG_RTC_DRV_DS1672 is not set
# CONFIG_RTC_DRV_DS3232 is not set
# CONFIG_RTC_DRV_MAX6900 is not set
CONFIG_RTC_DRV_RS5C372=y
# CONFIG_RTC_DRV_ISL1208 is not set
# CONFIG_RTC_DRV_ISL12022 is not set
# CONFIG_RTC_DRV_X1205 is not set
# CONFIG_RTC_DRV_PCF8563 is not set
# CONFIG_RTC_DRV_PCF8583 is not set
# CONFIG_RTC_DRV_M41T80 is not set
# CONFIG_RTC_DRV_BQ32K is not set
# CONFIG_RTC_DRV_S35390A is not set
# CONFIG_RTC_DRV_FM3130 is not set
# CONFIG_RTC_DRV_RX8581 is not set
# CONFIG_RTC_DRV_RX8025 is not set
# CONFIG_RTC_DRV_EM3027 is not set
# CONFIG_RTC_DRV_RV3029C2 is not set

#
# SPI RTC drivers
#

#
# Platform RTC drivers
#
# CONFIG_RTC_DRV_CMOS is not set
# CONFIG_RTC_DRV_DS1286 is not set
# CONFIG_RTC_DRV_DS1511 is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_STK17TA8 is not set
# CONFIG_RTC_DRV_M48T86 is not set
# CONFIG_RTC_DRV_M48T35 is not set
# CONFIG_RTC_DRV_M48T59 is not set
# CONFIG_RTC_DRV_MSM6242 is not set
# CONFIG_RTC_DRV_BQ4802 is not set
# CONFIG_RTC_DRV_RP5C01 is not set
# CONFIG_RTC_DRV_V3020 is not set

#
# on-CPU RTC drivers
#
CONFIG_DMADEVICES=y
# CONFIG_DMADEVICES_DEBUG is not set

#
# DMA Devices
#
# CONFIG_DW_DMAC is not set
# CONFIG_SH_DMAE is not set
# CONFIG_TIMB_DMA is not set
# CONFIG_AUXDISPLAY is not set
CONFIG_UIO=y
# CONFIG_UIO_PDRV is not set
CONFIG_UIO_PDRV_GENIRQ=y
# CONFIG_STAGING is not set
CONFIG_CLKDEV_LOOKUP=y

#
# File systems
#
# CONFIG_EXT2_FS is not set
# CONFIG_EXT3_FS is not set
# CONFIG_EXT4_FS is not set
# CONFIG_REISERFS_FS is not set
# CONFIG_JFS_FS is not set
# CONFIG_XFS_FS is not set
# CONFIG_GFS2_FS is not set
# CONFIG_BTRFS_FS is not set
# CONFIG_NILFS2_FS is not set
# CONFIG_FS_POSIX_ACL is not set
CONFIG_FILE_LOCKING=y
CONFIG_FSNOTIFY=y
# CONFIG_DNOTIFY is not set
CONFIG_INOTIFY_USER=y
# CONFIG_FANOTIFY is not set
# CONFIG_QUOTA is not set
# CONFIG_QUOTACTL is not set
# CONFIG_AUTOFS4_FS is not set
# CONFIG_FUSE_FS is not set

#
# Caches
#
# CONFIG_FSCACHE is not set

#
# CD-ROM/DVD Filesystems
#
# CONFIG_ISO9660_FS is not set
# CONFIG_UDF_FS is not set

#
# DOS/FAT/NT Filesystems
#
# CONFIG_MSDOS_FS is not set
# CONFIG_VFAT_FS is not set
# CONFIG_NTFS_FS is not set

#
# Pseudo filesystems
#
CONFIG_PROC_FS=y
CONFIG_PROC_SYSCTL=y
CONFIG_PROC_PAGE_MONITOR=y
CONFIG_SYSFS=y
CONFIG_TMPFS=y
# CONFIG_TMPFS_POSIX_ACL is not set
# CONFIG_TMPFS_XATTR is not set
# CONFIG_HUGETLB_PAGE is not set
# CONFIG_CONFIGFS_FS is not set
CONFIG_MISC_FILESYSTEMS=y
# CONFIG_ADFS_FS is not set
# CONFIG_AFFS_FS is not set
# CONFIG_HFS_FS is not set
# CONFIG_HFSPLUS_FS is not set
# CONFIG_BEFS_FS is not set
# CONFIG_BFS_FS is not set
# CONFIG_EFS_FS is not set
# CONFIG_JFFS2_FS is not set
# CONFIG_LOGFS is not set
# CONFIG_CRAMFS is not set
# CONFIG_SQUASHFS is not set
# CONFIG_VXFS_FS is not set
# CONFIG_MINIX_FS is not set
# CONFIG_OMFS_FS is not set
# CONFIG_HPFS_FS is not set
# CONFIG_QNX4FS_FS is not set
# CONFIG_ROMFS_FS is not set
# CONFIG_PSTORE is not set
# CONFIG_SYSV_FS is not set
# CONFIG_UFS_FS is not set
CONFIG_NETWORK_FILESYSTEMS=y
# CONFIG_NFS_FS is not set
# CONFIG_NFSD is not set
# CONFIG_CEPH_FS is not set
# CONFIG_CIFS is not set
# CONFIG_NCP_FS is not set
# CONFIG_CODA_FS is not set
# CONFIG_AFS_FS is not set

#
# Partition Types
#
# CONFIG_PARTITION_ADVANCED is not set
CONFIG_MSDOS_PARTITION=y
CONFIG_NLS=y
CONFIG_NLS_DEFAULT="iso8859-1"
# CONFIG_NLS_CODEPAGE_437 is not set
# CONFIG_NLS_CODEPAGE_737 is not set
# CONFIG_NLS_CODEPAGE_775 is not set
# CONFIG_NLS_CODEPAGE_850 is not set
# CONFIG_NLS_CODEPAGE_852 is not set
# CONFIG_NLS_CODEPAGE_855 is not set
# CONFIG_NLS_CODEPAGE_857 is not set
# CONFIG_NLS_CODEPAGE_860 is not set
# CONFIG_NLS_CODEPAGE_861 is not set
# CONFIG_NLS_CODEPAGE_862 is not set
# CONFIG_NLS_CODEPAGE_863 is not set
# CONFIG_NLS_CODEPAGE_864 is not set
# CONFIG_NLS_CODEPAGE_865 is not set
# CONFIG_NLS_CODEPAGE_866 is not set
# CONFIG_NLS_CODEPAGE_869 is not set
# CONFIG_NLS_CODEPAGE_936 is not set
# CONFIG_NLS_CODEPAGE_950 is not set
# CONFIG_NLS_CODEPAGE_932 is not set
# CONFIG_NLS_CODEPAGE_949 is not set
# CONFIG_NLS_CODEPAGE_874 is not set
# CONFIG_NLS_ISO8859_8 is not set
# CONFIG_NLS_CODEPAGE_1250 is not set
# CONFIG_NLS_CODEPAGE_1251 is not set
# CONFIG_NLS_ASCII is not set
# CONFIG_NLS_ISO8859_1 is not set
# CONFIG_NLS_ISO8859_2 is not set
# CONFIG_NLS_ISO8859_3 is not set
# CONFIG_NLS_ISO8859_4 is not set
# CONFIG_NLS_ISO8859_5 is not set
# CONFIG_NLS_ISO8859_6 is not set
# CONFIG_NLS_ISO8859_7 is not set
# CONFIG_NLS_ISO8859_9 is not set
# CONFIG_NLS_ISO8859_13 is not set
# CONFIG_NLS_ISO8859_14 is not set
# CONFIG_NLS_ISO8859_15 is not set
# CONFIG_NLS_KOI8_R is not set
# CONFIG_NLS_KOI8_U is not set
# CONFIG_NLS_UTF8 is not set

#
# Kernel hacking
#
# CONFIG_PRINTK_TIME is not set
CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
CONFIG_ENABLE_WARN_DEPRECATED=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_FRAME_WARN=1024
CONFIG_MAGIC_SYSRQ=y
# CONFIG_STRIP_ASM_SYMS is not set
# CONFIG_UNUSED_SYMBOLS is not set
CONFIG_DEBUG_FS=y
# CONFIG_HEADERS_CHECK is not set
# CONFIG_DEBUG_SECTION_MISMATCH is not set
CONFIG_DEBUG_KERNEL=y
# CONFIG_DEBUG_SHIRQ is not set
CONFIG_LOCKUP_DETECTOR=y
# CONFIG_HARDLOCKUP_DETECTOR is not set
# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC=y
CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=1
# CONFIG_DETECT_HUNG_TASK is not set
# CONFIG_SCHED_DEBUG is not set
# CONFIG_SCHEDSTATS is not set
# CONFIG_TIMER_STATS is not set
# CONFIG_DEBUG_OBJECTS is not set
# CONFIG_DEBUG_SLAB is not set
# CONFIG_DEBUG_KMEMLEAK is not set
# CONFIG_DEBUG_RT_MUTEXES is not set
# CONFIG_RT_MUTEX_TESTER is not set
# CONFIG_DEBUG_SPINLOCK is not set
# CONFIG_DEBUG_MUTEXES is not set
# CONFIG_DEBUG_LOCK_ALLOC is not set
# CONFIG_PROVE_LOCKING is not set
# CONFIG_SPARSE_RCU_POINTER is not set
# CONFIG_LOCK_STAT is not set
# CONFIG_DEBUG_SPINLOCK_SLEEP is not set
# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
# CONFIG_DEBUG_STACK_USAGE is not set
# CONFIG_DEBUG_KOBJECT is not set
CONFIG_DEBUG_BUGVERBOSE=y
# CONFIG_DEBUG_INFO is not set
# CONFIG_DEBUG_VM is not set
# CONFIG_DEBUG_WRITECOUNT is not set
CONFIG_DEBUG_MEMORY_INIT=y
# CONFIG_DEBUG_LIST is not set
# CONFIG_TEST_LIST_SORT is not set
# CONFIG_DEBUG_SG is not set
# CONFIG_DEBUG_NOTIFIERS is not set
# CONFIG_DEBUG_CREDENTIALS is not set
# CONFIG_BOOT_PRINTK_DELAY is not set
# CONFIG_RCU_TORTURE_TEST is not set
# CONFIG_BACKTRACE_SELF_TEST is not set
# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
# CONFIG_LKDTM is not set
# CONFIG_FAULT_INJECTION is not set
# CONFIG_LATENCYTOP is not set
# CONFIG_SYSCTL_SYSCALL_CHECK is not set
# CONFIG_DEBUG_PAGEALLOC is not set
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_TRACING_SUPPORT=y
# CONFIG_FTRACE is not set
CONFIG_DYNAMIC_DEBUG=y
# CONFIG_DMA_API_DEBUG is not set
# CONFIG_ATOMIC64_SELFTEST is not set
# CONFIG_SAMPLES is not set
CONFIG_HAVE_ARCH_KGDB=y
# CONFIG_KGDB is not set
# CONFIG_TEST_KSTRTOX is not set
# CONFIG_STRICT_DEVMEM is not set
CONFIG_ARM_UNWIND=y
# CONFIG_DEBUG_USER is not set
# CONFIG_DEBUG_LL is not set
# CONFIG_OC_ETM is not set

#
# Security options
#
# CONFIG_KEYS is not set
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SECURITY is not set
# CONFIG_SECURITYFS is not set
CONFIG_DEFAULT_SECURITY_DAC=y
CONFIG_DEFAULT_SECURITY=""
CONFIG_CRYPTO=y

#
# Crypto core or helper
#
# CONFIG_CRYPTO_FIPS is not set
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_RNG=y
CONFIG_CRYPTO_RNG2=y
# CONFIG_CRYPTO_MANAGER is not set
# CONFIG_CRYPTO_MANAGER2 is not set
# CONFIG_CRYPTO_GF128MUL is not set
# CONFIG_CRYPTO_NULL is not set
# CONFIG_CRYPTO_CRYPTD is not set
# CONFIG_CRYPTO_AUTHENC is not set

#
# Authenticated Encryption with Associated Data
#
# CONFIG_CRYPTO_CCM is not set
# CONFIG_CRYPTO_GCM is not set
# CONFIG_CRYPTO_SEQIV is not set

#
# Block modes
#
# CONFIG_CRYPTO_CBC is not set
# CONFIG_CRYPTO_CTR is not set
# CONFIG_CRYPTO_CTS is not set
# CONFIG_CRYPTO_ECB is not set
# CONFIG_CRYPTO_LRW is not set
# CONFIG_CRYPTO_PCBC is not set
# CONFIG_CRYPTO_XTS is not set

#
# Hash modes
#
# CONFIG_CRYPTO_HMAC is not set
# CONFIG_CRYPTO_XCBC is not set
# CONFIG_CRYPTO_VMAC is not set

#
# Digest
#
# CONFIG_CRYPTO_CRC32C is not set
# CONFIG_CRYPTO_GHASH is not set
# CONFIG_CRYPTO_MD4 is not set
# CONFIG_CRYPTO_MD5 is not set
# CONFIG_CRYPTO_MICHAEL_MIC is not set
# CONFIG_CRYPTO_RMD128 is not set
# CONFIG_CRYPTO_RMD160 is not set
# CONFIG_CRYPTO_RMD256 is not set
# CONFIG_CRYPTO_RMD320 is not set
# CONFIG_CRYPTO_SHA1 is not set
# CONFIG_CRYPTO_SHA256 is not set
# CONFIG_CRYPTO_SHA512 is not set
# CONFIG_CRYPTO_TGR192 is not set
# CONFIG_CRYPTO_WP512 is not set

#
# Ciphers
#
CONFIG_CRYPTO_AES=y
# CONFIG_CRYPTO_ANUBIS is not set
# CONFIG_CRYPTO_ARC4 is not set
# CONFIG_CRYPTO_BLOWFISH is not set
# CONFIG_CRYPTO_CAMELLIA is not set
# CONFIG_CRYPTO_CAST5 is not set
# CONFIG_CRYPTO_CAST6 is not set
# CONFIG_CRYPTO_DES is not set
# CONFIG_CRYPTO_FCRYPT is not set
# CONFIG_CRYPTO_KHAZAD is not set
# CONFIG_CRYPTO_SALSA20 is not set
# CONFIG_CRYPTO_SEED is not set
# CONFIG_CRYPTO_SERPENT is not set
# CONFIG_CRYPTO_TEA is not set
# CONFIG_CRYPTO_TWOFISH is not set

#
# Compression
#
# CONFIG_CRYPTO_DEFLATE is not set
# CONFIG_CRYPTO_ZLIB is not set
# CONFIG_CRYPTO_LZO is not set

#
# Random Number Generation
#
CONFIG_CRYPTO_ANSI_CPRNG=y
# CONFIG_CRYPTO_USER_API_HASH is not set
# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
CONFIG_CRYPTO_HW=y
# CONFIG_BINARY_PRINTF is not set

#
# Library routines
#
CONFIG_BITREVERSE=y
# CONFIG_CRC_CCITT is not set
# CONFIG_CRC16 is not set
# CONFIG_CRC_T10DIF is not set
# CONFIG_CRC_ITU_T is not set
CONFIG_CRC32=y
# CONFIG_CRC7 is not set
# CONFIG_LIBCRC32C is not set
CONFIG_ZLIB_INFLATE=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_XZ_DEC=y
CONFIG_XZ_DEC_X86=y
CONFIG_XZ_DEC_POWERPC=y
CONFIG_XZ_DEC_IA64=y
CONFIG_XZ_DEC_ARM=y
CONFIG_XZ_DEC_ARMTHUMB=y
CONFIG_XZ_DEC_SPARC=y
CONFIG_XZ_DEC_BCJ=y
# CONFIG_XZ_DEC_TEST is not set
CONFIG_DECOMPRESS_GZIP=y
CONFIG_DECOMPRESS_BZIP2=y
CONFIG_DECOMPRESS_LZMA=y
CONFIG_DECOMPRESS_XZ=y
CONFIG_DECOMPRESS_LZO=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_DMA=y
CONFIG_NLATTR=y
# CONFIG_AVERAGE is not set

[-- Attachment #3: Type: text/plain, Size: 0 bytes --]



^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-14 21:16       ` Rafael J. Wysocki
@ 2011-06-15 14:17         ` Magnus Damm
  -1 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-15 14:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Tuesday, June 14, 2011, Magnus Damm wrote:
>> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Use the generic power domains support introduced by the previous
>> > patch to implement support for power domains on SH7372.
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > ---
>>
>> Thanks for your work on this. I just tried this on my Mackerel board,
>> but I can't seem to get the pd_power_up() and pd_power_down()
>> callbacks to be executed. It is probably a misconfiguration from my
>> side.
>
> They trigger for me e.g. after doing
>
> # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
>
> Attached is the .config I've been using.

Thanks, I can trigger using sysfs and your kernel configuration.

However, I assumed it also would work when the sceen saver kicked in.
I recall it being fbcon that controls the screen save, perhaps
something else. So just wait a bit and see if you also can reproduce
it. The console gets black but the power is still on...

Also forcing to go back to powered-on state (see below) doesn't work that well:
# echo 0 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank

It looks like we loose the panning information somehow. Most likely a
LCDC driver bug. Unless the driver callbacks are not being invoked as
expected.

Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
as a LCD cache. It sits in the same hardware power domain as the
LCDCs. I don't think the MERAM software supports power down
unfortunately. Disabling MERAM support removes the garbage on the
screen.

>> Here's some feedback on the sh7372-specific code:
>>
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
>> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
>> > @@ -19,6 +19,7 @@ config ARCH_SH7372
>> >        select CPU_V7
>> >        select SH_CLK_CPG
>> >        select ARCH_WANT_OPTIONAL_GPIOLIB
>> > +       select PM_GENERIC_DOMAINS
>>
>> We want to support a single ARM binary for multiple boards,
>
> Surely CONFIG_ARCH_SH7372 will be set in that binary?
>
>> so this should be enabled for all SoCs in mach-shmobile as a whole.
>
> OK, where exactly do you want me to move it?

Ideally to ARCH_SHMOBILE in arch/arm/Kconfig.

>> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
>> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
>> > @@ -15,16 +15,97 @@
>> >  #include <linux/list.h>
>> >  #include <linux/err.h>
>> >  #include <linux/slab.h>
>> > +#include <linux/pm_runtime.h>
>> > +#include <linux/platform_device.h>
>> >  #include <asm/system.h>
>> >  #include <asm/io.h>
>> >  #include <asm/tlbflush.h>
>> >  #include <mach/common.h>
>> > +#include <mach/sh7372.h>
>> >
>> >  #define SMFRAM 0xe6a70000
>> >  #define SYSTBCR 0xe6150024
>> >  #define SBAR 0xe6180020
>> >  #define APARMBAREA 0xe6f10020
>> >
>> > +#define SPDCR 0xe6180008
>> > +#define SWUCR 0xe6180014
>> > +#define PSTR 0xe6180080
>> > +
>> > +struct sh7372_domain_data {
>> > +       unsigned int bit_shift;
>> > +};
>>
>> Is it possible to make struct sh7372_domain_data include struct
>> generic_pm_domain?
>
> It should be possible to do that.
>
> Do I understand it correctly that you want one structure definition per
> power domain instead of the two?

Yes, at least that's what I would do to keep the data together. I
don't care that much though, so feel free to implement it however
you'd like.

>> > +core_initcall(sh7372_power_domains_init);
>>
>> This initcall is going to be executed regardless which SoC we're
>> running on. We only want it called for sh7372 though.
>
> OK
>
>> If you look at other SoC-specific code then you will notice that
>> initcalls are only used for functions in mach-shmobile/ that are
>> common for all SoCs implemented under mach-shmobile.
>>
>> You most likely want to initialize from sh7372_pm_init(), but for that
>> to work you probably have to reorder your code inside mackerel_init().
>
> OK, I'll figure out how to do the initialization correctly.

Please do!

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
@ 2011-06-15 14:17         ` Magnus Damm
  0 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-15 14:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Tuesday, June 14, 2011, Magnus Damm wrote:
>> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Use the generic power domains support introduced by the previous
>> > patch to implement support for power domains on SH7372.
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > ---
>>
>> Thanks for your work on this. I just tried this on my Mackerel board,
>> but I can't seem to get the pd_power_up() and pd_power_down()
>> callbacks to be executed. It is probably a misconfiguration from my
>> side.
>
> They trigger for me e.g. after doing
>
> # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
>
> Attached is the .config I've been using.

Thanks, I can trigger using sysfs and your kernel configuration.

However, I assumed it also would work when the sceen saver kicked in.
I recall it being fbcon that controls the screen save, perhaps
something else. So just wait a bit and see if you also can reproduce
it. The console gets black but the power is still on...

Also forcing to go back to powered-on state (see below) doesn't work that well:
# echo 0 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank

It looks like we loose the panning information somehow. Most likely a
LCDC driver bug. Unless the driver callbacks are not being invoked as
expected.

Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
as a LCD cache. It sits in the same hardware power domain as the
LCDCs. I don't think the MERAM software supports power down
unfortunately. Disabling MERAM support removes the garbage on the
screen.

>> Here's some feedback on the sh7372-specific code:
>>
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
>> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
>> > @@ -19,6 +19,7 @@ config ARCH_SH7372
>> >        select CPU_V7
>> >        select SH_CLK_CPG
>> >        select ARCH_WANT_OPTIONAL_GPIOLIB
>> > +       select PM_GENERIC_DOMAINS
>>
>> We want to support a single ARM binary for multiple boards,
>
> Surely CONFIG_ARCH_SH7372 will be set in that binary?
>
>> so this should be enabled for all SoCs in mach-shmobile as a whole.
>
> OK, where exactly do you want me to move it?

Ideally to ARCH_SHMOBILE in arch/arm/Kconfig.

>> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
>> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
>> > @@ -15,16 +15,97 @@
>> >  #include <linux/list.h>
>> >  #include <linux/err.h>
>> >  #include <linux/slab.h>
>> > +#include <linux/pm_runtime.h>
>> > +#include <linux/platform_device.h>
>> >  #include <asm/system.h>
>> >  #include <asm/io.h>
>> >  #include <asm/tlbflush.h>
>> >  #include <mach/common.h>
>> > +#include <mach/sh7372.h>
>> >
>> >  #define SMFRAM 0xe6a70000
>> >  #define SYSTBCR 0xe6150024
>> >  #define SBAR 0xe6180020
>> >  #define APARMBAREA 0xe6f10020
>> >
>> > +#define SPDCR 0xe6180008
>> > +#define SWUCR 0xe6180014
>> > +#define PSTR 0xe6180080
>> > +
>> > +struct sh7372_domain_data {
>> > +       unsigned int bit_shift;
>> > +};
>>
>> Is it possible to make struct sh7372_domain_data include struct
>> generic_pm_domain?
>
> It should be possible to do that.
>
> Do I understand it correctly that you want one structure definition per
> power domain instead of the two?

Yes, at least that's what I would do to keep the data together. I
don't care that much though, so feel free to implement it however
you'd like.

>> > +core_initcall(sh7372_power_domains_init);
>>
>> This initcall is going to be executed regardless which SoC we're
>> running on. We only want it called for sh7372 though.
>
> OK
>
>> If you look at other SoC-specific code then you will notice that
>> initcalls are only used for functions in mach-shmobile/ that are
>> common for all SoCs implemented under mach-shmobile.
>>
>> You most likely want to initialize from sh7372_pm_init(), but for that
>> to work you probably have to reorder your code inside mackerel_init().
>
> OK, I'll figure out how to do the initialization correctly.

Please do!

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-14 21:16       ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-15 14:17       ` Magnus Damm
  -1 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-15 14:17 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Tuesday, June 14, 2011, Magnus Damm wrote:
>> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Use the generic power domains support introduced by the previous
>> > patch to implement support for power domains on SH7372.
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > ---
>>
>> Thanks for your work on this. I just tried this on my Mackerel board,
>> but I can't seem to get the pd_power_up() and pd_power_down()
>> callbacks to be executed. It is probably a misconfiguration from my
>> side.
>
> They trigger for me e.g. after doing
>
> # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
>
> Attached is the .config I've been using.

Thanks, I can trigger using sysfs and your kernel configuration.

However, I assumed it also would work when the sceen saver kicked in.
I recall it being fbcon that controls the screen save, perhaps
something else. So just wait a bit and see if you also can reproduce
it. The console gets black but the power is still on...

Also forcing to go back to powered-on state (see below) doesn't work that well:
# echo 0 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank

It looks like we loose the panning information somehow. Most likely a
LCDC driver bug. Unless the driver callbacks are not being invoked as
expected.

Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
as a LCD cache. It sits in the same hardware power domain as the
LCDCs. I don't think the MERAM software supports power down
unfortunately. Disabling MERAM support removes the garbage on the
screen.

>> Here's some feedback on the sh7372-specific code:
>>
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
>> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
>> > @@ -19,6 +19,7 @@ config ARCH_SH7372
>> >        select CPU_V7
>> >        select SH_CLK_CPG
>> >        select ARCH_WANT_OPTIONAL_GPIOLIB
>> > +       select PM_GENERIC_DOMAINS
>>
>> We want to support a single ARM binary for multiple boards,
>
> Surely CONFIG_ARCH_SH7372 will be set in that binary?
>
>> so this should be enabled for all SoCs in mach-shmobile as a whole.
>
> OK, where exactly do you want me to move it?

Ideally to ARCH_SHMOBILE in arch/arm/Kconfig.

>> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
>> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
>> > @@ -15,16 +15,97 @@
>> >  #include <linux/list.h>
>> >  #include <linux/err.h>
>> >  #include <linux/slab.h>
>> > +#include <linux/pm_runtime.h>
>> > +#include <linux/platform_device.h>
>> >  #include <asm/system.h>
>> >  #include <asm/io.h>
>> >  #include <asm/tlbflush.h>
>> >  #include <mach/common.h>
>> > +#include <mach/sh7372.h>
>> >
>> >  #define SMFRAM 0xe6a70000
>> >  #define SYSTBCR 0xe6150024
>> >  #define SBAR 0xe6180020
>> >  #define APARMBAREA 0xe6f10020
>> >
>> > +#define SPDCR 0xe6180008
>> > +#define SWUCR 0xe6180014
>> > +#define PSTR 0xe6180080
>> > +
>> > +struct sh7372_domain_data {
>> > +       unsigned int bit_shift;
>> > +};
>>
>> Is it possible to make struct sh7372_domain_data include struct
>> generic_pm_domain?
>
> It should be possible to do that.
>
> Do I understand it correctly that you want one structure definition per
> power domain instead of the two?

Yes, at least that's what I would do to keep the data together. I
don't care that much though, so feel free to implement it however
you'd like.

>> > +core_initcall(sh7372_power_domains_init);
>>
>> This initcall is going to be executed regardless which SoC we're
>> running on. We only want it called for sh7372 though.
>
> OK
>
>> If you look at other SoC-specific code then you will notice that
>> initcalls are only used for functions in mach-shmobile/ that are
>> common for all SoCs implemented under mach-shmobile.
>>
>> You most likely want to initialize from sh7372_pm_init(), but for that
>> to work you probably have to reorder your code inside mackerel_init().
>
> OK, I'll figure out how to do the initialization correctly.

Please do!

Thanks,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-15 14:17         ` Magnus Damm
@ 2011-06-15 23:06           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-15 23:06 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

On Wednesday, June 15, 2011, Magnus Damm wrote:
> On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Tuesday, June 14, 2011, Magnus Damm wrote:
> >> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >>
> >> Thanks for your work on this. I just tried this on my Mackerel board,
> >> but I can't seem to get the pd_power_up() and pd_power_down()
> >> callbacks to be executed. It is probably a misconfiguration from my
> >> side.
> >
> > They trigger for me e.g. after doing
> >
> > # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> >
> > Attached is the .config I've been using.
> 
> Thanks, I can trigger using sysfs and your kernel configuration.

Good.

> However, I assumed it also would work when the sceen saver kicked in.
> I recall it being fbcon that controls the screen save, perhaps
> something else. So just wait a bit and see if you also can reproduce
> it. The console gets black but the power is still on...

I noticed that, but I think it simply means pm_runtime_put() isn't
called in that code path.

> Also forcing to go back to powered-on state (see below) doesn't work that well:
> # echo 0 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> 
> It looks like we loose the panning information somehow. Most likely a
> LCDC driver bug. Unless the driver callbacks are not being invoked as
> expected.

That should be easy to verify, I'll do that.

> Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
> enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
> as a LCD cache. It sits in the same hardware power domain as the
> LCDCs. I don't think the MERAM software supports power down
> unfortunately. Disabling MERAM support removes the garbage on the
> screen.

Well, I can't really comment here.

> >> Here's some feedback on the sh7372-specific code:
> >>
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> >> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> >> > @@ -19,6 +19,7 @@ config ARCH_SH7372
> >> >        select CPU_V7
> >> >        select SH_CLK_CPG
> >> >        select ARCH_WANT_OPTIONAL_GPIOLIB
> >> > +       select PM_GENERIC_DOMAINS
> >>
> >> We want to support a single ARM binary for multiple boards,
> >
> > Surely CONFIG_ARCH_SH7372 will be set in that binary?
> >
> >> so this should be enabled for all SoCs in mach-shmobile as a whole.
> >
> > OK, where exactly do you want me to move it?
> 
> Ideally to ARCH_SHMOBILE in arch/arm/Kconfig.

OK

> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> >> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> >> > @@ -15,16 +15,97 @@
> >> >  #include <linux/list.h>
> >> >  #include <linux/err.h>
> >> >  #include <linux/slab.h>
> >> > +#include <linux/pm_runtime.h>
> >> > +#include <linux/platform_device.h>
> >> >  #include <asm/system.h>
> >> >  #include <asm/io.h>
> >> >  #include <asm/tlbflush.h>
> >> >  #include <mach/common.h>
> >> > +#include <mach/sh7372.h>
> >> >
> >> >  #define SMFRAM 0xe6a70000
> >> >  #define SYSTBCR 0xe6150024
> >> >  #define SBAR 0xe6180020
> >> >  #define APARMBAREA 0xe6f10020
> >> >
> >> > +#define SPDCR 0xe6180008
> >> > +#define SWUCR 0xe6180014
> >> > +#define PSTR 0xe6180080
> >> > +
> >> > +struct sh7372_domain_data {
> >> > +       unsigned int bit_shift;
> >> > +};
> >>
> >> Is it possible to make struct sh7372_domain_data include struct
> >> generic_pm_domain?
> >
> > It should be possible to do that.
> >
> > Do I understand it correctly that you want one structure definition per
> > power domain instead of the two?
> 
> Yes, at least that's what I would do to keep the data together. I
> don't care that much though, so feel free to implement it however
> you'd like.

OK, I think I can merge them.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
@ 2011-06-15 23:06           ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-15 23:06 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

On Wednesday, June 15, 2011, Magnus Damm wrote:
> On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Tuesday, June 14, 2011, Magnus Damm wrote:
> >> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >>
> >> Thanks for your work on this. I just tried this on my Mackerel board,
> >> but I can't seem to get the pd_power_up() and pd_power_down()
> >> callbacks to be executed. It is probably a misconfiguration from my
> >> side.
> >
> > They trigger for me e.g. after doing
> >
> > # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> >
> > Attached is the .config I've been using.
> 
> Thanks, I can trigger using sysfs and your kernel configuration.

Good.

> However, I assumed it also would work when the sceen saver kicked in.
> I recall it being fbcon that controls the screen save, perhaps
> something else. So just wait a bit and see if you also can reproduce
> it. The console gets black but the power is still on...

I noticed that, but I think it simply means pm_runtime_put() isn't
called in that code path.

> Also forcing to go back to powered-on state (see below) doesn't work that well:
> # echo 0 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> 
> It looks like we loose the panning information somehow. Most likely a
> LCDC driver bug. Unless the driver callbacks are not being invoked as
> expected.

That should be easy to verify, I'll do that.

> Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
> enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
> as a LCD cache. It sits in the same hardware power domain as the
> LCDCs. I don't think the MERAM software supports power down
> unfortunately. Disabling MERAM support removes the garbage on the
> screen.

Well, I can't really comment here.

> >> Here's some feedback on the sh7372-specific code:
> >>
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> >> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> >> > @@ -19,6 +19,7 @@ config ARCH_SH7372
> >> >        select CPU_V7
> >> >        select SH_CLK_CPG
> >> >        select ARCH_WANT_OPTIONAL_GPIOLIB
> >> > +       select PM_GENERIC_DOMAINS
> >>
> >> We want to support a single ARM binary for multiple boards,
> >
> > Surely CONFIG_ARCH_SH7372 will be set in that binary?
> >
> >> so this should be enabled for all SoCs in mach-shmobile as a whole.
> >
> > OK, where exactly do you want me to move it?
> 
> Ideally to ARCH_SHMOBILE in arch/arm/Kconfig.

OK

> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> >> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> >> > @@ -15,16 +15,97 @@
> >> >  #include <linux/list.h>
> >> >  #include <linux/err.h>
> >> >  #include <linux/slab.h>
> >> > +#include <linux/pm_runtime.h>
> >> > +#include <linux/platform_device.h>
> >> >  #include <asm/system.h>
> >> >  #include <asm/io.h>
> >> >  #include <asm/tlbflush.h>
> >> >  #include <mach/common.h>
> >> > +#include <mach/sh7372.h>
> >> >
> >> >  #define SMFRAM 0xe6a70000
> >> >  #define SYSTBCR 0xe6150024
> >> >  #define SBAR 0xe6180020
> >> >  #define APARMBAREA 0xe6f10020
> >> >
> >> > +#define SPDCR 0xe6180008
> >> > +#define SWUCR 0xe6180014
> >> > +#define PSTR 0xe6180080
> >> > +
> >> > +struct sh7372_domain_data {
> >> > +       unsigned int bit_shift;
> >> > +};
> >>
> >> Is it possible to make struct sh7372_domain_data include struct
> >> generic_pm_domain?
> >
> > It should be possible to do that.
> >
> > Do I understand it correctly that you want one structure definition per
> > power domain instead of the two?
> 
> Yes, at least that's what I would do to keep the data together. I
> don't care that much though, so feel free to implement it however
> you'd like.

OK, I think I can merge them.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-15 14:17         ` Magnus Damm
  (?)
  (?)
@ 2011-06-15 23:06         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-15 23:06 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Wednesday, June 15, 2011, Magnus Damm wrote:
> On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Tuesday, June 14, 2011, Magnus Damm wrote:
> >> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >>
> >> Thanks for your work on this. I just tried this on my Mackerel board,
> >> but I can't seem to get the pd_power_up() and pd_power_down()
> >> callbacks to be executed. It is probably a misconfiguration from my
> >> side.
> >
> > They trigger for me e.g. after doing
> >
> > # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> >
> > Attached is the .config I've been using.
> 
> Thanks, I can trigger using sysfs and your kernel configuration.

Good.

> However, I assumed it also would work when the sceen saver kicked in.
> I recall it being fbcon that controls the screen save, perhaps
> something else. So just wait a bit and see if you also can reproduce
> it. The console gets black but the power is still on...

I noticed that, but I think it simply means pm_runtime_put() isn't
called in that code path.

> Also forcing to go back to powered-on state (see below) doesn't work that well:
> # echo 0 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> 
> It looks like we loose the panning information somehow. Most likely a
> LCDC driver bug. Unless the driver callbacks are not being invoked as
> expected.

That should be easy to verify, I'll do that.

> Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
> enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
> as a LCD cache. It sits in the same hardware power domain as the
> LCDCs. I don't think the MERAM software supports power down
> unfortunately. Disabling MERAM support removes the garbage on the
> screen.

Well, I can't really comment here.

> >> Here's some feedback on the sh7372-specific code:
> >>
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/Kconfig
> >> > +++ linux-2.6/arch/arm/mach-shmobile/Kconfig
> >> > @@ -19,6 +19,7 @@ config ARCH_SH7372
> >> >        select CPU_V7
> >> >        select SH_CLK_CPG
> >> >        select ARCH_WANT_OPTIONAL_GPIOLIB
> >> > +       select PM_GENERIC_DOMAINS
> >>
> >> We want to support a single ARM binary for multiple boards,
> >
> > Surely CONFIG_ARCH_SH7372 will be set in that binary?
> >
> >> so this should be enabled for all SoCs in mach-shmobile as a whole.
> >
> > OK, where exactly do you want me to move it?
> 
> Ideally to ARCH_SHMOBILE in arch/arm/Kconfig.

OK

> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
> >> > +++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
> >> > @@ -15,16 +15,97 @@
> >> >  #include <linux/list.h>
> >> >  #include <linux/err.h>
> >> >  #include <linux/slab.h>
> >> > +#include <linux/pm_runtime.h>
> >> > +#include <linux/platform_device.h>
> >> >  #include <asm/system.h>
> >> >  #include <asm/io.h>
> >> >  #include <asm/tlbflush.h>
> >> >  #include <mach/common.h>
> >> > +#include <mach/sh7372.h>
> >> >
> >> >  #define SMFRAM 0xe6a70000
> >> >  #define SYSTBCR 0xe6150024
> >> >  #define SBAR 0xe6180020
> >> >  #define APARMBAREA 0xe6f10020
> >> >
> >> > +#define SPDCR 0xe6180008
> >> > +#define SWUCR 0xe6180014
> >> > +#define PSTR 0xe6180080
> >> > +
> >> > +struct sh7372_domain_data {
> >> > +       unsigned int bit_shift;
> >> > +};
> >>
> >> Is it possible to make struct sh7372_domain_data include struct
> >> generic_pm_domain?
> >
> > It should be possible to do that.
> >
> > Do I understand it correctly that you want one structure definition per
> > power domain instead of the two?
> 
> Yes, at least that's what I would do to keep the data together. I
> don't care that much though, so feel free to implement it however
> you'd like.

OK, I think I can merge them.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-11 20:31   ` Rafael J. Wysocki
@ 2011-06-19 22:02     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:02 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---

Hi,

I this version of the patch I made the following modifications:

* Removed the change adding platform_data to struct dev_pm_domain,
  because that field is not going to be necessary in the near future.

* Moved the code calling drv->pm->runtime_suspend(dev) to a separate
  function that returns immediately if dle->need_restore is set for the
  given device (meaning that drv->pm->runtime_suspend(dev) has already
  been called for it and the corresponding ->runtime_resume() hasn't).
  This fixes a bug where drv->pm->runtime_suspend() could be called for
  a device whose state hasn't been restored after power cycling its
  PM domain.

* Made pm_genpd_add_device() return error code on an attempt to add a device
  do a PM domain whose power_is_off is set (that complemets the previous
  modification).

* Makde the .power_on() and .power_off() generic PM domain callbacks take
  (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).

Thanks,
Rafael

---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  490 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 +++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 573 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
=================================--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
=================================--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,490 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count = 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_suspend)
+		ret = drv->pm->runtime_suspend(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_resume)
+		drv->pm->runtime_resume(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count = 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev = dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain = new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
=================================--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
@ 2011-06-19 22:02     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:02 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---

Hi,

I this version of the patch I made the following modifications:

* Removed the change adding platform_data to struct dev_pm_domain,
  because that field is not going to be necessary in the near future.

* Moved the code calling drv->pm->runtime_suspend(dev) to a separate
  function that returns immediately if dle->need_restore is set for the
  given device (meaning that drv->pm->runtime_suspend(dev) has already
  been called for it and the corresponding ->runtime_resume() hasn't).
  This fixes a bug where drv->pm->runtime_suspend() could be called for
  a device whose state hasn't been restored after power cycling its
  PM domain.

* Made pm_genpd_add_device() return error code on an attempt to add a device
  do a PM domain whose power_is_off is set (that complemets the previous
  modification).

* Makde the .power_on() and .power_off() generic PM domain callbacks take
  (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).

Thanks,
Rafael

---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  490 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 +++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 573 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,490 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_suspend)
+		ret = drv->pm->runtime_suspend(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_resume)
+		drv->pm->runtime_resume(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-11 20:31   ` Rafael J. Wysocki
  (?)
@ 2011-06-19 22:02   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:02 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---

Hi,

I this version of the patch I made the following modifications:

* Removed the change adding platform_data to struct dev_pm_domain,
  because that field is not going to be necessary in the near future.

* Moved the code calling drv->pm->runtime_suspend(dev) to a separate
  function that returns immediately if dle->need_restore is set for the
  given device (meaning that drv->pm->runtime_suspend(dev) has already
  been called for it and the corresponding ->runtime_resume() hasn't).
  This fixes a bug where drv->pm->runtime_suspend() could be called for
  a device whose state hasn't been restored after power cycling its
  PM domain.

* Made pm_genpd_add_device() return error code on an attempt to add a device
  do a PM domain whose power_is_off is set (that complemets the previous
  modification).

* Makde the .power_on() and .power_off() generic PM domain callbacks take
  (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).

Thanks,
Rafael

---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  490 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 +++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 573 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,490 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_suspend)
+		ret = drv->pm->runtime_suspend(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	if (drv && drv->pm && drv->pm->runtime_resume)
+		drv->pm->runtime_resume(dev);
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-11 20:39   ` Rafael J. Wysocki
@ 2011-06-19 22:06     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:06 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

In this version of the patch I made some changes following from the
modifications of [4/8] and I introduced pd_to_genpd() as a wrapper
around container_of(pm_domain, struct generic_pm_domain, domain) to
convert between struct dev_pm_domain and struct generic_pm_domain objects.

Thanks,
Rafael

---
 drivers/base/power/domain.c |  539 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 538 insertions(+), 13 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 /**
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -153,7 +154,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -258,6 +259,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -268,7 +290,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -282,28 +303,491 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev = dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ = 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
 	}
 
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count = 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -327,6 +811,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev = dev) {
 			ret = -EINVAL;
@@ -342,6 +831,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -371,6 +861,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -379,6 +874,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -386,6 +882,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -494,7 +991,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
=================================--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-19 22:06     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:06 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

In this version of the patch I made some changes following from the
modifications of [4/8] and I introduced pd_to_genpd() as a wrapper
around container_of(pm_domain, struct generic_pm_domain, domain) to
convert between struct dev_pm_domain and struct generic_pm_domain objects.

Thanks,
Rafael

---
 drivers/base/power/domain.c |  539 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 538 insertions(+), 13 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 /**
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -153,7 +154,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -258,6 +259,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -268,7 +290,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -282,28 +303,491 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
 	}
 
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -327,6 +811,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -342,6 +831,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -371,6 +861,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -379,6 +874,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -386,6 +882,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -494,7 +991,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-11 20:39   ` Rafael J. Wysocki
                     ` (3 preceding siblings ...)
  (?)
@ 2011-06-19 22:06   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:06 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

In this version of the patch I made some changes following from the
modifications of [4/8] and I introduced pd_to_genpd() as a wrapper
around container_of(pm_domain, struct generic_pm_domain, domain) to
convert between struct dev_pm_domain and struct generic_pm_domain objects.

Thanks,
Rafael

---
 drivers/base/power/domain.c |  539 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 538 insertions(+), 13 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 /**
@@ -40,7 +40,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -153,7 +154,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -258,6 +259,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -268,7 +290,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -282,28 +303,491 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
+	__pm_genpd_runtime_resume(dev, genpd);
+	mutex_unlock(&genpd->lock);
 
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
+	return 0;
+}
+
+#else
+
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
 	}
 
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
 	if (genpd->start_device)
 		genpd->start_device(dev);
 
-	mutex_unlock(&genpd->lock);
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
 
 	return 0;
 }
 
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
 #else
 
-#define pm_genpd_runtime_suspend	NULL
-#define pm_genpd_runtime_resume		NULL
+#define pm_genpd_prepare	NULL
+#define pm_genpd_suspend	NULL
+#define pm_genpd_suspend_noirq	NULL
+#define pm_genpd_resume_noirq	NULL
+#define pm_genpd_resume		NULL
+#define pm_genpd_freeze		NULL
+#define pm_genpd_freeze_noirq	NULL
+#define pm_genpd_thaw_noirq	NULL
+#define pm_genpd_thaw		NULL
+#define pm_genpd_complete	NULL
 
-#endif /* CONFIG_PM_RUNTIME */
+#endif /* CONFIG_PM_SLEEP */
 
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
@@ -327,6 +811,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -342,6 +831,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -371,6 +861,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -379,6 +874,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -386,6 +882,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -494,7 +991,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-15 23:06           ` Rafael J. Wysocki
@ 2011-06-19 22:07             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:07 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

I think that all of the previous comments have been addressed in this
version of the patch.

Thanks,
Rafael

---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 ++++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   71 +++++++++++++++++++++++++++
 5 files changed, 108 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,87 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		__raw_writel(mask, SPDCR);
+
+		while (__raw_readl(SPDCR) & mask)
+			cpu_relax();
+
+		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		__raw_writel(mask, SWUCR);
+
+		while (__raw_readl(SWUCR) & mask)
+			cpu_relax();
+
+		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
=================================--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
@ 2011-06-19 22:07             ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:07 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

I think that all of the previous comments have been addressed in this
version of the patch.

Thanks,
Rafael

---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 ++++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   71 +++++++++++++++++++++++++++
 5 files changed, 108 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,87 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		__raw_writel(mask, SPDCR);
+
+		while (__raw_readl(SPDCR) & mask)
+			cpu_relax();
+
+		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		__raw_writel(mask, SWUCR);
+
+		while (__raw_readl(SWUCR) & mask)
+			cpu_relax();
+
+		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-15 23:06           ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-19 22:07           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-19 22:07 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

I think that all of the previous comments have been addressed in this
version of the patch.

Thanks,
Rafael

---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 ++++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   71 +++++++++++++++++++++++++++
 5 files changed, 108 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,87 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		__raw_writel(mask, SPDCR);
+
+		while (__raw_readl(SPDCR) & mask)
+			cpu_relax();
+
+		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		__raw_writel(mask, SWUCR);
+
+		while (__raw_readl(SWUCR) & mask)
+			cpu_relax();
+
+		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+			 mask, __raw_readl(PSTR));
+	}
+
+	return 0;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-19 22:07             ` Rafael J. Wysocki
@ 2011-06-20  2:01               ` Paul Mundt
  -1 siblings, 0 replies; 261+ messages in thread
From: Paul Mundt @ 2011-06-20  2:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> +static int pd_power_down(struct generic_pm_domain *genpd)
> +{
> +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> +
> +	if (__raw_readl(PSTR) & mask) {
> +		__raw_writel(mask, SPDCR);
> +
> +		while (__raw_readl(SPDCR) & mask)
> +			cpu_relax();
> +
> +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> +			 mask, __raw_readl(PSTR));
> +	}
> +
> +	return 0;
> +}
> +
> +static int pd_power_up(struct generic_pm_domain *genpd)
> +{
> +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> +
> +	if (!(__raw_readl(PSTR) & mask)) {
> +		__raw_writel(mask, SWUCR);
> +
> +		while (__raw_readl(SWUCR) & mask)
> +			cpu_relax();
> +
> +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> +			 mask, __raw_readl(PSTR));
> +	}
> +
> +	return 0;
> +}
> +
Given that these functions can return errors, it's probably more prudent
to implement some timeout logic on top of the busy loop. Hardware does
get stuck, after all.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
@ 2011-06-20  2:01               ` Paul Mundt
  0 siblings, 0 replies; 261+ messages in thread
From: Paul Mundt @ 2011-06-20  2:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> +static int pd_power_down(struct generic_pm_domain *genpd)
> +{
> +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> +
> +	if (__raw_readl(PSTR) & mask) {
> +		__raw_writel(mask, SPDCR);
> +
> +		while (__raw_readl(SPDCR) & mask)
> +			cpu_relax();
> +
> +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> +			 mask, __raw_readl(PSTR));
> +	}
> +
> +	return 0;
> +}
> +
> +static int pd_power_up(struct generic_pm_domain *genpd)
> +{
> +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> +
> +	if (!(__raw_readl(PSTR) & mask)) {
> +		__raw_writel(mask, SWUCR);
> +
> +		while (__raw_readl(SWUCR) & mask)
> +			cpu_relax();
> +
> +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> +			 mask, __raw_readl(PSTR));
> +	}
> +
> +	return 0;
> +}
> +
Given that these functions can return errors, it's probably more prudent
to implement some timeout logic on top of the busy loop. Hardware does
get stuck, after all.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-19 22:07             ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-20  2:01             ` Paul Mundt
  -1 siblings, 0 replies; 261+ messages in thread
From: Paul Mundt @ 2011-06-20  2:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> +static int pd_power_down(struct generic_pm_domain *genpd)
> +{
> +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> +
> +	if (__raw_readl(PSTR) & mask) {
> +		__raw_writel(mask, SPDCR);
> +
> +		while (__raw_readl(SPDCR) & mask)
> +			cpu_relax();
> +
> +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> +			 mask, __raw_readl(PSTR));
> +	}
> +
> +	return 0;
> +}
> +
> +static int pd_power_up(struct generic_pm_domain *genpd)
> +{
> +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> +
> +	if (!(__raw_readl(PSTR) & mask)) {
> +		__raw_writel(mask, SWUCR);
> +
> +		while (__raw_readl(SWUCR) & mask)
> +			cpu_relax();
> +
> +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> +			 mask, __raw_readl(PSTR));
> +	}
> +
> +	return 0;
> +}
> +
Given that these functions can return errors, it's probably more prudent
to implement some timeout logic on top of the busy loop. Hardware does
get stuck, after all.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-20  2:01               ` Paul Mundt
@ 2011-06-20 22:30                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-20 22:30 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Monday, June 20, 2011, Paul Mundt wrote:
> On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> > +static int pd_power_down(struct generic_pm_domain *genpd)
> > +{
> > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > +
> > +	if (__raw_readl(PSTR) & mask) {
> > +		__raw_writel(mask, SPDCR);
> > +
> > +		while (__raw_readl(SPDCR) & mask)
> > +			cpu_relax();
> > +
> > +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > +			 mask, __raw_readl(PSTR));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int pd_power_up(struct generic_pm_domain *genpd)
> > +{
> > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > +
> > +	if (!(__raw_readl(PSTR) & mask)) {
> > +		__raw_writel(mask, SWUCR);
> > +
> > +		while (__raw_readl(SWUCR) & mask)
> > +			cpu_relax();
> > +
> > +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> > +			 mask, __raw_readl(PSTR));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> Given that these functions can return errors, it's probably more prudent
> to implement some timeout logic on top of the busy loop. Hardware does
> get stuck, after all.

Yes, it does, but we don't really know what timeout value to use in there.

In fact, failures of pd_power_down() don't really matter (at worst the power
domain won't be powered off really) and pd_power_up() should only return error
code if the error is known to be unrecoverable.

So, what about this: repeat certain number of times (I verified that it took
several iterations of the loop until the new value settled on my hardware)
and then return from pd_power_down() or go to sleep for a short time and repeat
in pd_power_up()?

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v7)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   84 +++++++++++++++++++++++++++
 5 files changed, 121 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,100 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_MS 10
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count = PSTR_RETRIES;
+
+		__raw_writel(mask, SPDCR);
+		while ((__raw_readl(SPDCR) & mask) && retry_count--)
+			cpu_relax();
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		do {
+			unsigned int retry_count = PSTR_RETRIES;
+
+			__raw_writel(mask, SWUCR);
+			while ((__raw_readl(SWUCR) & mask) && retry_count--)
+				cpu_relax();
+
+			if (!(__raw_readl(SWUCR) & mask))
+				break;
+
+			msleep(PSTR_DELAY_MS);
+		} while (true);
+	}
+
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
=================================--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
@ 2011-06-20 22:30                 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-20 22:30 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Monday, June 20, 2011, Paul Mundt wrote:
> On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> > +static int pd_power_down(struct generic_pm_domain *genpd)
> > +{
> > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > +
> > +	if (__raw_readl(PSTR) & mask) {
> > +		__raw_writel(mask, SPDCR);
> > +
> > +		while (__raw_readl(SPDCR) & mask)
> > +			cpu_relax();
> > +
> > +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > +			 mask, __raw_readl(PSTR));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int pd_power_up(struct generic_pm_domain *genpd)
> > +{
> > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > +
> > +	if (!(__raw_readl(PSTR) & mask)) {
> > +		__raw_writel(mask, SWUCR);
> > +
> > +		while (__raw_readl(SWUCR) & mask)
> > +			cpu_relax();
> > +
> > +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> > +			 mask, __raw_readl(PSTR));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> Given that these functions can return errors, it's probably more prudent
> to implement some timeout logic on top of the busy loop. Hardware does
> get stuck, after all.

Yes, it does, but we don't really know what timeout value to use in there.

In fact, failures of pd_power_down() don't really matter (at worst the power
domain won't be powered off really) and pd_power_up() should only return error
code if the error is known to be unrecoverable.

So, what about this: repeat certain number of times (I verified that it took
several iterations of the loop until the new value settled on my hardware)
and then return from pd_power_down() or go to sleep for a short time and repeat
in pd_power_up()?

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v7)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   84 +++++++++++++++++++++++++++
 5 files changed, 121 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,100 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_MS 10
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count = PSTR_RETRIES;
+
+		__raw_writel(mask, SPDCR);
+		while ((__raw_readl(SPDCR) & mask) && retry_count--)
+			cpu_relax();
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		do {
+			unsigned int retry_count = PSTR_RETRIES;
+
+			__raw_writel(mask, SWUCR);
+			while ((__raw_readl(SWUCR) & mask) && retry_count--)
+				cpu_relax();
+
+			if (!(__raw_readl(SWUCR) & mask))
+				break;
+
+			msleep(PSTR_DELAY_MS);
+		} while (true);
+	}
+
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-20  2:01               ` Paul Mundt
  (?)
  (?)
@ 2011-06-20 22:30               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-20 22:30 UTC (permalink / raw)
  To: Paul Mundt; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Monday, June 20, 2011, Paul Mundt wrote:
> On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> > +static int pd_power_down(struct generic_pm_domain *genpd)
> > +{
> > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > +
> > +	if (__raw_readl(PSTR) & mask) {
> > +		__raw_writel(mask, SPDCR);
> > +
> > +		while (__raw_readl(SPDCR) & mask)
> > +			cpu_relax();
> > +
> > +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > +			 mask, __raw_readl(PSTR));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> > +static int pd_power_up(struct generic_pm_domain *genpd)
> > +{
> > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > +
> > +	if (!(__raw_readl(PSTR) & mask)) {
> > +		__raw_writel(mask, SWUCR);
> > +
> > +		while (__raw_readl(SWUCR) & mask)
> > +			cpu_relax();
> > +
> > +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> > +			 mask, __raw_readl(PSTR));
> > +	}
> > +
> > +	return 0;
> > +}
> > +
> Given that these functions can return errors, it's probably more prudent
> to implement some timeout logic on top of the busy loop. Hardware does
> get stuck, after all.

Yes, it does, but we don't really know what timeout value to use in there.

In fact, failures of pd_power_down() don't really matter (at worst the power
domain won't be powered off really) and pd_power_up() should only return error
code if the error is known to be unrecoverable.

So, what about this: repeat certain number of times (I verified that it took
several iterations of the loop until the new value settled on my hardware)
and then return from pd_power_down() or go to sleep for a short time and repeat
in pd_power_up()?

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v7)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   84 +++++++++++++++++++++++++++
 5 files changed, 121 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,100 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_MS 10
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count = PSTR_RETRIES;
+
+		__raw_writel(mask, SPDCR);
+		while ((__raw_readl(SPDCR) & mask) && retry_count--)
+			cpu_relax();
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (!(__raw_readl(PSTR) & mask)) {
+		do {
+			unsigned int retry_count = PSTR_RETRIES;
+
+			__raw_writel(mask, SWUCR);
+			while ((__raw_readl(SWUCR) & mask) && retry_count--)
+				cpu_relax();
+
+			if (!(__raw_readl(SWUCR) & mask))
+				break;
+
+			msleep(PSTR_DELAY_MS);
+		} while (true);
+	}
+
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-19 22:06     ` Rafael J. Wysocki
@ 2011-06-20 23:05       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-20 23:05 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

On Monday, June 20, 2011, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
> 
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  For the
> other devices the action carried out depends on the type of the
> transition.  During system suspend the power domain .suspend()
> callback executes pm_generic_suspend() for the device, while the
> PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
> for it, stops it and eventually removes power from the PM domain it
> belongs to (after all devices in the domain have been stopped and its
> subdomains have been powered off).
> 
> During system resume the PM domain .resume_noirq() callback
> restores power to the PM domain (when executed for it first time),
> starts the device and executes pm_generic_resume_noirq() for it,
> while the .resume() callback executes pm_generic_resume() for the
> device.  Finally, the .complete() callback executes pm_runtime_idle()
> for the device which should put it back into the suspended state if
> its runtime PM usage count is equal to zero at that time.
> 
> The actions carried out during hibernation and resume from it are
> analogous to the ones described above.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

The patch below adds wakeup devices support on top of this one.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions

Devices that are set up to wake up the system from sleep states
should not be stopped and power should not be removed from them
when the system goes into a sleep state.  Make the generic PM domain
code respect that limitation.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -446,6 +446,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -666,6 +669,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-20 23:05       ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-20 23:05 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh

On Monday, June 20, 2011, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
> 
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  For the
> other devices the action carried out depends on the type of the
> transition.  During system suspend the power domain .suspend()
> callback executes pm_generic_suspend() for the device, while the
> PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
> for it, stops it and eventually removes power from the PM domain it
> belongs to (after all devices in the domain have been stopped and its
> subdomains have been powered off).
> 
> During system resume the PM domain .resume_noirq() callback
> restores power to the PM domain (when executed for it first time),
> starts the device and executes pm_generic_resume_noirq() for it,
> while the .resume() callback executes pm_generic_resume() for the
> device.  Finally, the .complete() callback executes pm_runtime_idle()
> for the device which should put it back into the suspended state if
> its runtime PM usage count is equal to zero at that time.
> 
> The actions carried out during hibernation and resume from it are
> analogous to the ones described above.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

The patch below adds wakeup devices support on top of this one.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions

Devices that are set up to wake up the system from sleep states
should not be stopped and power should not be removed from them
when the system goes into a sleep state.  Make the generic PM domain
code respect that limitation.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -446,6 +446,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -666,6 +669,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-19 22:06     ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-20 23:05     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-20 23:05 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

On Monday, June 20, 2011, Rafael J. Wysocki wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
> 
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  For the
> other devices the action carried out depends on the type of the
> transition.  During system suspend the power domain .suspend()
> callback executes pm_generic_suspend() for the device, while the
> PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
> for it, stops it and eventually removes power from the PM domain it
> belongs to (after all devices in the domain have been stopped and its
> subdomains have been powered off).
> 
> During system resume the PM domain .resume_noirq() callback
> restores power to the PM domain (when executed for it first time),
> starts the device and executes pm_generic_resume_noirq() for it,
> while the .resume() callback executes pm_generic_resume() for the
> device.  Finally, the .complete() callback executes pm_runtime_idle()
> for the device which should put it back into the suspended state if
> its runtime PM usage count is equal to zero at that time.
> 
> The actions carried out during hibernation and resume from it are
> analogous to the ones described above.
> 
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> ---

The patch below adds wakeup devices support on top of this one.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions

Devices that are set up to wake up the system from sleep states
should not be stopped and power should not be removed from them
when the system goes into a sleep state.  Make the generic PM domain
code respect that limitation.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -446,6 +446,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -666,6 +669,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
  2011-06-11 20:26   ` Rafael J. Wysocki
@ 2011-06-20 23:37     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-20 23:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
> (PM: Add support for device power domains), which introduced the
> struct dev_power_domain type for representing device power domains,
> evidently confuses some developers who tend to think that objects
> of this type must correspond to "power domains" as defined by
> hardware, which is not the case.  Namely, at the kernel level, a
> struct dev_power_domain object can represent arbitrary set of devices
> that are mutually dependent power management-wise and need not belong
> to one hardware power domain.  To avoid that confusion, rename struct
> dev_power_domain to struct dev_pm_domain and rename the related
> pointers in struct device and struct pm_clk_notifier_block from
> pwr_domain to pm_domain.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Acked-by: Kevin Hilman <khilman@ti.com>

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
@ 2011-06-20 23:37     ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-20 23:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
> (PM: Add support for device power domains), which introduced the
> struct dev_power_domain type for representing device power domains,
> evidently confuses some developers who tend to think that objects
> of this type must correspond to "power domains" as defined by
> hardware, which is not the case.  Namely, at the kernel level, a
> struct dev_power_domain object can represent arbitrary set of devices
> that are mutually dependent power management-wise and need not belong
> to one hardware power domain.  To avoid that confusion, rename struct
> dev_power_domain to struct dev_pm_domain and rename the related
> pointers in struct device and struct pm_clk_notifier_block from
> pwr_domain to pm_domain.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Acked-by: Kevin Hilman <khilman@ti.com>

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
  2011-06-11 20:26   ` Rafael J. Wysocki
  (?)
@ 2011-06-20 23:37   ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-20 23:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
> (PM: Add support for device power domains), which introduced the
> struct dev_power_domain type for representing device power domains,
> evidently confuses some developers who tend to think that objects
> of this type must correspond to "power domains" as defined by
> hardware, which is not the case.  Namely, at the kernel level, a
> struct dev_power_domain object can represent arbitrary set of devices
> that are mutually dependent power management-wise and need not belong
> to one hardware power domain.  To avoid that confusion, rename struct
> dev_power_domain to struct dev_pm_domain and rename the related
> pointers in struct device and struct pm_clk_notifier_block from
> pwr_domain to pm_domain.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Acked-by: Kevin Hilman <khilman@ti.com>

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-21  0:02   ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21  0:02 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> Hi,
>
> This is the 4th update of the patchset adding support for generic I/O PM
> domains.  
>
> The patches have been reworked quite a bit to take feedback into
> account, but I left the Greg's ACK in [4/8] in the hope it still applies
> (Greg, please let me know in case it doesn't :-)).
>
> The model here is that a bunch of devices share a common power resource
> that can be turned on and off by software.  In addition to that, there
> are means to start and stop the activity of each device, for example
> by manipulating their clocks.  Moreover, there may be hierarchy of
> such things, for example power resource A may be necessary for devices
> a, b, c, which don't rely on any other power resources, and for devices
> x, y, z that also rely on power resource X.  In that case there one PM
> domain object representing devices a, b, c and power resource A, and 
> another PM domain object will represent devices x, y, z with power
> resource X, plus the first object will be the second one's parent.
>
> Note to Kevin: I know you'd like each PM domain to be able to go into several
> different states, but the situation will always be that in some of those
> states the devices' registers will remain intact, while in the rest of those
> states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> 1-3 preserve device registers.  Then it is not necessary to save device
> registers for "domain" states 1-3 and it only is necessary to save them
> when going to state 4.  In that case, .power_off() may map to the "go to
> state 4" operation (and analogously .power_on()), while the rest may be
> done by .stop_device() and .start_device().  IOW, .power_is_off = true
> means "the devices' registers have to be restored", so it need not map to
> any particular physical state of a (hardware) power domain.

Sure, but it's not only about register context save/restore.  It's about
the the governor hook and how you decide which state to enter.  IOW, the
governor decision is not only about whether or not you will lose
register context but also about the latency involved in entering &
exiting those states.

So from my perspective, having only 2-states at this level makes the
governor rather pointless since any decision making will have to be done
where ever the knowledge of the mulitple power states lives.

> Note to Magnus and Paul: I didn't use a global lock as suggested, because
> I think it may lead to completely unnecessary congestion in situations in
> which there are no hierarchies of PM domains.  It is quite easy to show that
> the code doesn't deadlock, because (1) no more than 2 locks are held by the
> same thread at a time (parent lock and child lock) and (2) they are always
> acquired in the same order (parent before the child).
>
> Overall, I think I've taken all of the important dependencies into
> consideration, but if you spot something suspicious, please let me know. :-)
> Wakeup is not covered at this point, because it's not necessary for the
> SH7372's A4LC power domain that's the first user of the new code, but it
> is quite clear how add the support for it.  Also, for more complicated
> cases it is necessary to take QoS requirements (latencies) into account,
> which is in the works (kind of).
>
> [1/8] - Update documentation to reflect the fact that struct dev_power_domain
>         callbacks take precedence over subsystem PM callbacks.
>
> [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
>         fact that those objects need not correspond to hardware power domains
>         directly.
>
> [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
>
> [4/8] - Introduce runtime PM support for generic I/O PM domains.
>
> [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
>         (that's necessary for the next patches).
>
> [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
>
> [7/8] - Add system-wide PM support for generic I/O PM domains.
>
> [8/8] - Use the new code to represent the SH7372's A4MP power domain.
>
> The patchset has been tested on SH7372 Mackerel board and appears to work
> correctly.
>
> I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> which people may possibly use the naming that's going to change in their new
> code.  If you agree with that, please let me know, I'll need some serious
> ACKs below that patch if it's to be pushed for 3.0. ;-)

Just gave you my ack, but [2/8] will need a minor update to apply on
Linus' master branch since another fix to mach-omap1/pm_bus.c just got
merged[1] via the OMAP tree.

I don't have any other fixes touching those files queued for v3.0 so I
don't expect any other conflicts there.

Kevin

[1]
commit e9e35c5a2b2c803b5e2f25906d8ffe110670ceb6
Author: Kevin Hilman <khilman@ti.com>
Date:   Tue Jun 14 05:53:18 2011 -0700

    OMAP1: PM: register notifiers with generic clock ops even when !PM_RUNTIME
    
    When runtime PM is disabled, device clocks need to be enabled on
    device add and disabled on device remove.  This currently is not
    happening because in the !PM_RUNTIME case, no notifiers are registered
    for OMAP1 devices.
    
    Fix this by ensuring notifiers are registered, even in the !PM_RUNTIME case.
    
    Reported-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
    Tested-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
    Signed-off-by: Kevin Hilman <khilman@ti.com>
    Signed-off-by: Tony Lindgren <tony@atomide.com>


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-21  0:02   ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21  0:02 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> Hi,
>
> This is the 4th update of the patchset adding support for generic I/O PM
> domains.  
>
> The patches have been reworked quite a bit to take feedback into
> account, but I left the Greg's ACK in [4/8] in the hope it still applies
> (Greg, please let me know in case it doesn't :-)).
>
> The model here is that a bunch of devices share a common power resource
> that can be turned on and off by software.  In addition to that, there
> are means to start and stop the activity of each device, for example
> by manipulating their clocks.  Moreover, there may be hierarchy of
> such things, for example power resource A may be necessary for devices
> a, b, c, which don't rely on any other power resources, and for devices
> x, y, z that also rely on power resource X.  In that case there one PM
> domain object representing devices a, b, c and power resource A, and 
> another PM domain object will represent devices x, y, z with power
> resource X, plus the first object will be the second one's parent.
>
> Note to Kevin: I know you'd like each PM domain to be able to go into several
> different states, but the situation will always be that in some of those
> states the devices' registers will remain intact, while in the rest of those
> states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> 1-3 preserve device registers.  Then it is not necessary to save device
> registers for "domain" states 1-3 and it only is necessary to save them
> when going to state 4.  In that case, .power_off() may map to the "go to
> state 4" operation (and analogously .power_on()), while the rest may be
> done by .stop_device() and .start_device().  IOW, .power_is_off == true
> means "the devices' registers have to be restored", so it need not map to
> any particular physical state of a (hardware) power domain.

Sure, but it's not only about register context save/restore.  It's about
the the governor hook and how you decide which state to enter.  IOW, the
governor decision is not only about whether or not you will lose
register context but also about the latency involved in entering &
exiting those states.

So from my perspective, having only 2-states at this level makes the
governor rather pointless since any decision making will have to be done
where ever the knowledge of the mulitple power states lives.

> Note to Magnus and Paul: I didn't use a global lock as suggested, because
> I think it may lead to completely unnecessary congestion in situations in
> which there are no hierarchies of PM domains.  It is quite easy to show that
> the code doesn't deadlock, because (1) no more than 2 locks are held by the
> same thread at a time (parent lock and child lock) and (2) they are always
> acquired in the same order (parent before the child).
>
> Overall, I think I've taken all of the important dependencies into
> consideration, but if you spot something suspicious, please let me know. :-)
> Wakeup is not covered at this point, because it's not necessary for the
> SH7372's A4LC power domain that's the first user of the new code, but it
> is quite clear how add the support for it.  Also, for more complicated
> cases it is necessary to take QoS requirements (latencies) into account,
> which is in the works (kind of).
>
> [1/8] - Update documentation to reflect the fact that struct dev_power_domain
>         callbacks take precedence over subsystem PM callbacks.
>
> [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
>         fact that those objects need not correspond to hardware power domains
>         directly.
>
> [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
>
> [4/8] - Introduce runtime PM support for generic I/O PM domains.
>
> [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
>         (that's necessary for the next patches).
>
> [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
>
> [7/8] - Add system-wide PM support for generic I/O PM domains.
>
> [8/8] - Use the new code to represent the SH7372's A4MP power domain.
>
> The patchset has been tested on SH7372 Mackerel board and appears to work
> correctly.
>
> I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> which people may possibly use the naming that's going to change in their new
> code.  If you agree with that, please let me know, I'll need some serious
> ACKs below that patch if it's to be pushed for 3.0. ;-)

Just gave you my ack, but [2/8] will need a minor update to apply on
Linus' master branch since another fix to mach-omap1/pm_bus.c just got
merged[1] via the OMAP tree.

I don't have any other fixes touching those files queued for v3.0 so I
don't expect any other conflicts there.

Kevin

[1]
commit e9e35c5a2b2c803b5e2f25906d8ffe110670ceb6
Author: Kevin Hilman <khilman@ti.com>
Date:   Tue Jun 14 05:53:18 2011 -0700

    OMAP1: PM: register notifiers with generic clock ops even when !PM_RUNTIME
    
    When runtime PM is disabled, device clocks need to be enabled on
    device add and disabled on device remove.  This currently is not
    happening because in the !PM_RUNTIME case, no notifiers are registered
    for OMAP1 devices.
    
    Fix this by ensuring notifiers are registered, even in the !PM_RUNTIME case.
    
    Reported-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
    Tested-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
    Signed-off-by: Kevin Hilman <khilman@ti.com>
    Signed-off-by: Tony Lindgren <tony@atomide.com>


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (19 preceding siblings ...)
  (?)
@ 2011-06-21  0:02 ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21  0:02 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> Hi,
>
> This is the 4th update of the patchset adding support for generic I/O PM
> domains.  
>
> The patches have been reworked quite a bit to take feedback into
> account, but I left the Greg's ACK in [4/8] in the hope it still applies
> (Greg, please let me know in case it doesn't :-)).
>
> The model here is that a bunch of devices share a common power resource
> that can be turned on and off by software.  In addition to that, there
> are means to start and stop the activity of each device, for example
> by manipulating their clocks.  Moreover, there may be hierarchy of
> such things, for example power resource A may be necessary for devices
> a, b, c, which don't rely on any other power resources, and for devices
> x, y, z that also rely on power resource X.  In that case there one PM
> domain object representing devices a, b, c and power resource A, and 
> another PM domain object will represent devices x, y, z with power
> resource X, plus the first object will be the second one's parent.
>
> Note to Kevin: I know you'd like each PM domain to be able to go into several
> different states, but the situation will always be that in some of those
> states the devices' registers will remain intact, while in the rest of those
> states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> 1-3 preserve device registers.  Then it is not necessary to save device
> registers for "domain" states 1-3 and it only is necessary to save them
> when going to state 4.  In that case, .power_off() may map to the "go to
> state 4" operation (and analogously .power_on()), while the rest may be
> done by .stop_device() and .start_device().  IOW, .power_is_off == true
> means "the devices' registers have to be restored", so it need not map to
> any particular physical state of a (hardware) power domain.

Sure, but it's not only about register context save/restore.  It's about
the the governor hook and how you decide which state to enter.  IOW, the
governor decision is not only about whether or not you will lose
register context but also about the latency involved in entering &
exiting those states.

So from my perspective, having only 2-states at this level makes the
governor rather pointless since any decision making will have to be done
where ever the knowledge of the mulitple power states lives.

> Note to Magnus and Paul: I didn't use a global lock as suggested, because
> I think it may lead to completely unnecessary congestion in situations in
> which there are no hierarchies of PM domains.  It is quite easy to show that
> the code doesn't deadlock, because (1) no more than 2 locks are held by the
> same thread at a time (parent lock and child lock) and (2) they are always
> acquired in the same order (parent before the child).
>
> Overall, I think I've taken all of the important dependencies into
> consideration, but if you spot something suspicious, please let me know. :-)
> Wakeup is not covered at this point, because it's not necessary for the
> SH7372's A4LC power domain that's the first user of the new code, but it
> is quite clear how add the support for it.  Also, for more complicated
> cases it is necessary to take QoS requirements (latencies) into account,
> which is in the works (kind of).
>
> [1/8] - Update documentation to reflect the fact that struct dev_power_domain
>         callbacks take precedence over subsystem PM callbacks.
>
> [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
>         fact that those objects need not correspond to hardware power domains
>         directly.
>
> [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
>
> [4/8] - Introduce runtime PM support for generic I/O PM domains.
>
> [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
>         (that's necessary for the next patches).
>
> [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
>
> [7/8] - Add system-wide PM support for generic I/O PM domains.
>
> [8/8] - Use the new code to represent the SH7372's A4MP power domain.
>
> The patchset has been tested on SH7372 Mackerel board and appears to work
> correctly.
>
> I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> which people may possibly use the naming that's going to change in their new
> code.  If you agree with that, please let me know, I'll need some serious
> ACKs below that patch if it's to be pushed for 3.0. ;-)

Just gave you my ack, but [2/8] will need a minor update to apply on
Linus' master branch since another fix to mach-omap1/pm_bus.c just got
merged[1] via the OMAP tree.

I don't have any other fixes touching those files queued for v3.0 so I
don't expect any other conflicts there.

Kevin

[1]
commit e9e35c5a2b2c803b5e2f25906d8ffe110670ceb6
Author: Kevin Hilman <khilman@ti.com>
Date:   Tue Jun 14 05:53:18 2011 -0700

    OMAP1: PM: register notifiers with generic clock ops even when !PM_RUNTIME
    
    When runtime PM is disabled, device clocks need to be enabled on
    device add and disabled on device remove.  This currently is not
    happening because in the !PM_RUNTIME case, no notifiers are registered
    for OMAP1 devices.
    
    Fix this by ensuring notifiers are registered, even in the !PM_RUNTIME case.
    
    Reported-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
    Tested-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
    Signed-off-by: Kevin Hilman <khilman@ti.com>
    Signed-off-by: Tony Lindgren <tony@atomide.com>

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-21  0:02   ` Kevin Hilman
@ 2011-06-21 11:06     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-21 11:06 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

On Tuesday, June 21, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > Hi,
> >
> > This is the 4th update of the patchset adding support for generic I/O PM
> > domains.  
> >
> > The patches have been reworked quite a bit to take feedback into
> > account, but I left the Greg's ACK in [4/8] in the hope it still applies
> > (Greg, please let me know in case it doesn't :-)).
> >
> > The model here is that a bunch of devices share a common power resource
> > that can be turned on and off by software.  In addition to that, there
> > are means to start and stop the activity of each device, for example
> > by manipulating their clocks.  Moreover, there may be hierarchy of
> > such things, for example power resource A may be necessary for devices
> > a, b, c, which don't rely on any other power resources, and for devices
> > x, y, z that also rely on power resource X.  In that case there one PM
> > domain object representing devices a, b, c and power resource A, and 
> > another PM domain object will represent devices x, y, z with power
> > resource X, plus the first object will be the second one's parent.
> >
> > Note to Kevin: I know you'd like each PM domain to be able to go into several
> > different states, but the situation will always be that in some of those
> > states the devices' registers will remain intact, while in the rest of those
> > states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> > 1-3 preserve device registers.  Then it is not necessary to save device
> > registers for "domain" states 1-3 and it only is necessary to save them
> > when going to state 4.  In that case, .power_off() may map to the "go to
> > state 4" operation (and analogously .power_on()), while the rest may be
> > done by .stop_device() and .start_device().  IOW, .power_is_off = true
> > means "the devices' registers have to be restored", so it need not map to
> > any particular physical state of a (hardware) power domain.
> 
> Sure, but it's not only about register context save/restore.  It's about
> the the governor hook and how you decide which state to enter.  IOW, the
> governor decision is not only about whether or not you will lose
> register context but also about the latency involved in entering &
> exiting those states.
> 
> So from my perspective, having only 2-states at this level makes the
> governor rather pointless since any decision making will have to be done
> where ever the knowledge of the mulitple power states lives.

Well, in principle you can make the governor whatever you want, so it may
as well know of multiple states.

Anyway, if using multiple domain states turns out to be useful at the core
level, it may be added later with a separate patch.

> > Note to Magnus and Paul: I didn't use a global lock as suggested, because
> > I think it may lead to completely unnecessary congestion in situations in
> > which there are no hierarchies of PM domains.  It is quite easy to show that
> > the code doesn't deadlock, because (1) no more than 2 locks are held by the
> > same thread at a time (parent lock and child lock) and (2) they are always
> > acquired in the same order (parent before the child).
> >
> > Overall, I think I've taken all of the important dependencies into
> > consideration, but if you spot something suspicious, please let me know. :-)
> > Wakeup is not covered at this point, because it's not necessary for the
> > SH7372's A4LC power domain that's the first user of the new code, but it
> > is quite clear how add the support for it.  Also, for more complicated
> > cases it is necessary to take QoS requirements (latencies) into account,
> > which is in the works (kind of).
> >
> > [1/8] - Update documentation to reflect the fact that struct dev_power_domain
> >         callbacks take precedence over subsystem PM callbacks.
> >
> > [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
> >         fact that those objects need not correspond to hardware power domains
> >         directly.
> >
> > [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
> >
> > [4/8] - Introduce runtime PM support for generic I/O PM domains.
> >
> > [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
> >         (that's necessary for the next patches).
> >
> > [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
> >
> > [7/8] - Add system-wide PM support for generic I/O PM domains.
> >
> > [8/8] - Use the new code to represent the SH7372's A4MP power domain.
> >
> > The patchset has been tested on SH7372 Mackerel board and appears to work
> > correctly.
> >
> > I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> > that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> > which people may possibly use the naming that's going to change in their new
> > code.  If you agree with that, please let me know, I'll need some serious
> > ACKs below that patch if it's to be pushed for 3.0. ;-)
> 
> Just gave you my ack,

I thought the ACK was for [2/8] only, so do I understand correctly that it's
for the entire series? :-)

> but [2/8] will need a minor update to apply on
> Linus' master branch since another fix to mach-omap1/pm_bus.c just got
> merged[1] via the OMAP tree.

Yes, I already rebased my patches on top of 3.0-rc4.

> I don't have any other fixes touching those files queued for v3.0 so I
> don't expect any other conflicts there.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-21 11:06     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-21 11:06 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

On Tuesday, June 21, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > Hi,
> >
> > This is the 4th update of the patchset adding support for generic I/O PM
> > domains.  
> >
> > The patches have been reworked quite a bit to take feedback into
> > account, but I left the Greg's ACK in [4/8] in the hope it still applies
> > (Greg, please let me know in case it doesn't :-)).
> >
> > The model here is that a bunch of devices share a common power resource
> > that can be turned on and off by software.  In addition to that, there
> > are means to start and stop the activity of each device, for example
> > by manipulating their clocks.  Moreover, there may be hierarchy of
> > such things, for example power resource A may be necessary for devices
> > a, b, c, which don't rely on any other power resources, and for devices
> > x, y, z that also rely on power resource X.  In that case there one PM
> > domain object representing devices a, b, c and power resource A, and 
> > another PM domain object will represent devices x, y, z with power
> > resource X, plus the first object will be the second one's parent.
> >
> > Note to Kevin: I know you'd like each PM domain to be able to go into several
> > different states, but the situation will always be that in some of those
> > states the devices' registers will remain intact, while in the rest of those
> > states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> > 1-3 preserve device registers.  Then it is not necessary to save device
> > registers for "domain" states 1-3 and it only is necessary to save them
> > when going to state 4.  In that case, .power_off() may map to the "go to
> > state 4" operation (and analogously .power_on()), while the rest may be
> > done by .stop_device() and .start_device().  IOW, .power_is_off == true
> > means "the devices' registers have to be restored", so it need not map to
> > any particular physical state of a (hardware) power domain.
> 
> Sure, but it's not only about register context save/restore.  It's about
> the the governor hook and how you decide which state to enter.  IOW, the
> governor decision is not only about whether or not you will lose
> register context but also about the latency involved in entering &
> exiting those states.
> 
> So from my perspective, having only 2-states at this level makes the
> governor rather pointless since any decision making will have to be done
> where ever the knowledge of the mulitple power states lives.

Well, in principle you can make the governor whatever you want, so it may
as well know of multiple states.

Anyway, if using multiple domain states turns out to be useful at the core
level, it may be added later with a separate patch.

> > Note to Magnus and Paul: I didn't use a global lock as suggested, because
> > I think it may lead to completely unnecessary congestion in situations in
> > which there are no hierarchies of PM domains.  It is quite easy to show that
> > the code doesn't deadlock, because (1) no more than 2 locks are held by the
> > same thread at a time (parent lock and child lock) and (2) they are always
> > acquired in the same order (parent before the child).
> >
> > Overall, I think I've taken all of the important dependencies into
> > consideration, but if you spot something suspicious, please let me know. :-)
> > Wakeup is not covered at this point, because it's not necessary for the
> > SH7372's A4LC power domain that's the first user of the new code, but it
> > is quite clear how add the support for it.  Also, for more complicated
> > cases it is necessary to take QoS requirements (latencies) into account,
> > which is in the works (kind of).
> >
> > [1/8] - Update documentation to reflect the fact that struct dev_power_domain
> >         callbacks take precedence over subsystem PM callbacks.
> >
> > [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
> >         fact that those objects need not correspond to hardware power domains
> >         directly.
> >
> > [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
> >
> > [4/8] - Introduce runtime PM support for generic I/O PM domains.
> >
> > [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
> >         (that's necessary for the next patches).
> >
> > [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
> >
> > [7/8] - Add system-wide PM support for generic I/O PM domains.
> >
> > [8/8] - Use the new code to represent the SH7372's A4MP power domain.
> >
> > The patchset has been tested on SH7372 Mackerel board and appears to work
> > correctly.
> >
> > I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> > that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> > which people may possibly use the naming that's going to change in their new
> > code.  If you agree with that, please let me know, I'll need some serious
> > ACKs below that patch if it's to be pushed for 3.0. ;-)
> 
> Just gave you my ack,

I thought the ACK was for [2/8] only, so do I understand correctly that it's
for the entire series? :-)

> but [2/8] will need a minor update to apply on
> Linus' master branch since another fix to mach-omap1/pm_bus.c just got
> merged[1] via the OMAP tree.

Yes, I already rebased my patches on top of 3.0-rc4.

> I don't have any other fixes touching those files queued for v3.0 so I
> don't expect any other conflicts there.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-21  0:02   ` Kevin Hilman
  (?)
@ 2011-06-21 11:06   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-21 11:06 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tuesday, June 21, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > Hi,
> >
> > This is the 4th update of the patchset adding support for generic I/O PM
> > domains.  
> >
> > The patches have been reworked quite a bit to take feedback into
> > account, but I left the Greg's ACK in [4/8] in the hope it still applies
> > (Greg, please let me know in case it doesn't :-)).
> >
> > The model here is that a bunch of devices share a common power resource
> > that can be turned on and off by software.  In addition to that, there
> > are means to start and stop the activity of each device, for example
> > by manipulating their clocks.  Moreover, there may be hierarchy of
> > such things, for example power resource A may be necessary for devices
> > a, b, c, which don't rely on any other power resources, and for devices
> > x, y, z that also rely on power resource X.  In that case there one PM
> > domain object representing devices a, b, c and power resource A, and 
> > another PM domain object will represent devices x, y, z with power
> > resource X, plus the first object will be the second one's parent.
> >
> > Note to Kevin: I know you'd like each PM domain to be able to go into several
> > different states, but the situation will always be that in some of those
> > states the devices' registers will remain intact, while in the rest of those
> > states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> > 1-3 preserve device registers.  Then it is not necessary to save device
> > registers for "domain" states 1-3 and it only is necessary to save them
> > when going to state 4.  In that case, .power_off() may map to the "go to
> > state 4" operation (and analogously .power_on()), while the rest may be
> > done by .stop_device() and .start_device().  IOW, .power_is_off == true
> > means "the devices' registers have to be restored", so it need not map to
> > any particular physical state of a (hardware) power domain.
> 
> Sure, but it's not only about register context save/restore.  It's about
> the the governor hook and how you decide which state to enter.  IOW, the
> governor decision is not only about whether or not you will lose
> register context but also about the latency involved in entering &
> exiting those states.
> 
> So from my perspective, having only 2-states at this level makes the
> governor rather pointless since any decision making will have to be done
> where ever the knowledge of the mulitple power states lives.

Well, in principle you can make the governor whatever you want, so it may
as well know of multiple states.

Anyway, if using multiple domain states turns out to be useful at the core
level, it may be added later with a separate patch.

> > Note to Magnus and Paul: I didn't use a global lock as suggested, because
> > I think it may lead to completely unnecessary congestion in situations in
> > which there are no hierarchies of PM domains.  It is quite easy to show that
> > the code doesn't deadlock, because (1) no more than 2 locks are held by the
> > same thread at a time (parent lock and child lock) and (2) they are always
> > acquired in the same order (parent before the child).
> >
> > Overall, I think I've taken all of the important dependencies into
> > consideration, but if you spot something suspicious, please let me know. :-)
> > Wakeup is not covered at this point, because it's not necessary for the
> > SH7372's A4LC power domain that's the first user of the new code, but it
> > is quite clear how add the support for it.  Also, for more complicated
> > cases it is necessary to take QoS requirements (latencies) into account,
> > which is in the works (kind of).
> >
> > [1/8] - Update documentation to reflect the fact that struct dev_power_domain
> >         callbacks take precedence over subsystem PM callbacks.
> >
> > [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
> >         fact that those objects need not correspond to hardware power domains
> >         directly.
> >
> > [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
> >
> > [4/8] - Introduce runtime PM support for generic I/O PM domains.
> >
> > [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
> >         (that's necessary for the next patches).
> >
> > [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
> >
> > [7/8] - Add system-wide PM support for generic I/O PM domains.
> >
> > [8/8] - Use the new code to represent the SH7372's A4MP power domain.
> >
> > The patchset has been tested on SH7372 Mackerel board and appears to work
> > correctly.
> >
> > I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> > that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> > which people may possibly use the naming that's going to change in their new
> > code.  If you agree with that, please let me know, I'll need some serious
> > ACKs below that patch if it's to be pushed for 3.0. ;-)
> 
> Just gave you my ack,

I thought the ACK was for [2/8] only, so do I understand correctly that it's
for the entire series? :-)

> but [2/8] will need a minor update to apply on
> Linus' master branch since another fix to mach-omap1/pm_bus.c just got
> merged[1] via the OMAP tree.

Yes, I already rebased my patches on top of 3.0-rc4.

> I don't have any other fixes touching those files queued for v3.0 so I
> don't expect any other conflicts there.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-20 22:30                 ` Rafael J. Wysocki
@ 2011-06-21 11:57                   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-21 11:57 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Tuesday, June 21, 2011, Rafael J. Wysocki wrote:
> On Monday, June 20, 2011, Paul Mundt wrote:
> > On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> > > +static int pd_power_down(struct generic_pm_domain *genpd)
> > > +{
> > > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > > +
> > > +	if (__raw_readl(PSTR) & mask) {
> > > +		__raw_writel(mask, SPDCR);
> > > +
> > > +		while (__raw_readl(SPDCR) & mask)
> > > +			cpu_relax();
> > > +
> > > +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > > +			 mask, __raw_readl(PSTR));
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int pd_power_up(struct generic_pm_domain *genpd)
> > > +{
> > > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > > +
> > > +	if (!(__raw_readl(PSTR) & mask)) {
> > > +		__raw_writel(mask, SWUCR);
> > > +
> > > +		while (__raw_readl(SWUCR) & mask)
> > > +			cpu_relax();
> > > +
> > > +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> > > +			 mask, __raw_readl(PSTR));
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > Given that these functions can return errors, it's probably more prudent
> > to implement some timeout logic on top of the busy loop. Hardware does
> > get stuck, after all.
> 
> Yes, it does, but we don't really know what timeout value to use in there.
> 
> In fact, failures of pd_power_down() don't really matter (at worst the power
> domain won't be powered off really) and pd_power_up() should only return error
> code if the error is known to be unrecoverable.
> 
> So, what about this: repeat certain number of times (I verified that it took
> several iterations of the loop until the new value settled on my hardware)
> and then return from pd_power_down() or go to sleep for a short time and repeat
> in pd_power_up()?

Having discussed that with Magnus I changed pd_power_down() to use a timeout.
Specifically, it will bail out if the state doesn't change after approximately
1 ms.

Updated patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v7)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 ++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   92 +++++++++++++++++++++++++++
 5 files changed, 129 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,108 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
=================================--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
@ 2011-06-21 11:57                   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-21 11:57 UTC (permalink / raw)
  To: Paul Mundt
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Tuesday, June 21, 2011, Rafael J. Wysocki wrote:
> On Monday, June 20, 2011, Paul Mundt wrote:
> > On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> > > +static int pd_power_down(struct generic_pm_domain *genpd)
> > > +{
> > > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > > +
> > > +	if (__raw_readl(PSTR) & mask) {
> > > +		__raw_writel(mask, SPDCR);
> > > +
> > > +		while (__raw_readl(SPDCR) & mask)
> > > +			cpu_relax();
> > > +
> > > +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > > +			 mask, __raw_readl(PSTR));
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int pd_power_up(struct generic_pm_domain *genpd)
> > > +{
> > > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > > +
> > > +	if (!(__raw_readl(PSTR) & mask)) {
> > > +		__raw_writel(mask, SWUCR);
> > > +
> > > +		while (__raw_readl(SWUCR) & mask)
> > > +			cpu_relax();
> > > +
> > > +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> > > +			 mask, __raw_readl(PSTR));
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > Given that these functions can return errors, it's probably more prudent
> > to implement some timeout logic on top of the busy loop. Hardware does
> > get stuck, after all.
> 
> Yes, it does, but we don't really know what timeout value to use in there.
> 
> In fact, failures of pd_power_down() don't really matter (at worst the power
> domain won't be powered off really) and pd_power_up() should only return error
> code if the error is known to be unrecoverable.
> 
> So, what about this: repeat certain number of times (I verified that it took
> several iterations of the loop until the new value settled on my hardware)
> and then return from pd_power_down() or go to sleep for a short time and repeat
> in pd_power_up()?

Having discussed that with Magnus I changed pd_power_down() to use a timeout.
Specifically, it will bail out if the state doesn't change after approximately
1 ms.

Updated patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v7)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 ++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   92 +++++++++++++++++++++++++++
 5 files changed, 129 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,108 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-20 22:30                 ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-21 11:57                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-21 11:57 UTC (permalink / raw)
  To: Paul Mundt; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tuesday, June 21, 2011, Rafael J. Wysocki wrote:
> On Monday, June 20, 2011, Paul Mundt wrote:
> > On Mon, Jun 20, 2011 at 12:07:47AM +0200, Rafael J. Wysocki wrote:
> > > +static int pd_power_down(struct generic_pm_domain *genpd)
> > > +{
> > > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > > +
> > > +	if (__raw_readl(PSTR) & mask) {
> > > +		__raw_writel(mask, SPDCR);
> > > +
> > > +		while (__raw_readl(SPDCR) & mask)
> > > +			cpu_relax();
> > > +
> > > +		pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
> > > +			 mask, __raw_readl(PSTR));
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > > +static int pd_power_up(struct generic_pm_domain *genpd)
> > > +{
> > > +	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
> > > +	unsigned int mask = 1 << sh7372_pd->bit_shift;
> > > +
> > > +	if (!(__raw_readl(PSTR) & mask)) {
> > > +		__raw_writel(mask, SWUCR);
> > > +
> > > +		while (__raw_readl(SWUCR) & mask)
> > > +			cpu_relax();
> > > +
> > > +		pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
> > > +			 mask, __raw_readl(PSTR));
> > > +	}
> > > +
> > > +	return 0;
> > > +}
> > > +
> > Given that these functions can return errors, it's probably more prudent
> > to implement some timeout logic on top of the busy loop. Hardware does
> > get stuck, after all.
> 
> Yes, it does, but we don't really know what timeout value to use in there.
> 
> In fact, failures of pd_power_down() don't really matter (at worst the power
> domain won't be powered off really) and pd_power_up() should only return error
> code if the error is known to be unrecoverable.
> 
> So, what about this: repeat certain number of times (I verified that it took
> several iterations of the loop until the new value settled on my hardware)
> and then return from pd_power_down() or go to sleep for a short time and repeat
> in pd_power_up()?

Having discussed that with Magnus I changed pd_power_down() to use a timeout.
Specifically, it will bail out if the state doesn't change after approximately
1 ms.

Updated patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v7)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 ++++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   92 +++++++++++++++++++++++++++
 5 files changed, 129 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc_domain);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,108 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_runtime_clk_suspend;
+	genpd->start_device = pm_runtime_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_runtime_clk_init(dev);
+		pm_runtime_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-21 11:57                   ` Rafael J. Wysocki
@ 2011-06-21 12:47                     ` Paul Mundt
  -1 siblings, 0 replies; 261+ messages in thread
From: Paul Mundt @ 2011-06-21 12:47 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Tue, Jun 21, 2011 at 01:57:08PM +0200, Rafael J. Wysocki wrote:
> On Tuesday, June 21, 2011, Rafael J. Wysocki wrote:
> > On Monday, June 20, 2011, Paul Mundt wrote:
> > > Given that these functions can return errors, it's probably more prudent
> > > to implement some timeout logic on top of the busy loop. Hardware does
> > > get stuck, after all.
> > 
> > Yes, it does, but we don't really know what timeout value to use in there.
> > 
> > In fact, failures of pd_power_down() don't really matter (at worst the power
> > domain won't be powered off really) and pd_power_up() should only return error
> > code if the error is known to be unrecoverable.
> > 
> > So, what about this: repeat certain number of times (I verified that it took
> > several iterations of the loop until the new value settled on my hardware)
> > and then return from pd_power_down() or go to sleep for a short time and repeat
> > in pd_power_up()?
> 
> Having discussed that with Magnus I changed pd_power_down() to use a timeout.
> Specifically, it will bail out if the state doesn't change after approximately
> 1 ms.
> 
Looks good to me, thanks for fixing it up.

Acked-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
@ 2011-06-21 12:47                     ` Paul Mundt
  0 siblings, 0 replies; 261+ messages in thread
From: Paul Mundt @ 2011-06-21 12:47 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Magnus Damm, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

On Tue, Jun 21, 2011 at 01:57:08PM +0200, Rafael J. Wysocki wrote:
> On Tuesday, June 21, 2011, Rafael J. Wysocki wrote:
> > On Monday, June 20, 2011, Paul Mundt wrote:
> > > Given that these functions can return errors, it's probably more prudent
> > > to implement some timeout logic on top of the busy loop. Hardware does
> > > get stuck, after all.
> > 
> > Yes, it does, but we don't really know what timeout value to use in there.
> > 
> > In fact, failures of pd_power_down() don't really matter (at worst the power
> > domain won't be powered off really) and pd_power_up() should only return error
> > code if the error is known to be unrecoverable.
> > 
> > So, what about this: repeat certain number of times (I verified that it took
> > several iterations of the loop until the new value settled on my hardware)
> > and then return from pd_power_down() or go to sleep for a short time and repeat
> > in pd_power_up()?
> 
> Having discussed that with Magnus I changed pd_power_down() to use a timeout.
> Specifically, it will bail out if the state doesn't change after approximately
> 1 ms.
> 
Looks good to me, thanks for fixing it up.

Acked-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6)
  2011-06-21 11:57                   ` Rafael J. Wysocki
  (?)
@ 2011-06-21 12:47                   ` Paul Mundt
  -1 siblings, 0 replies; 261+ messages in thread
From: Paul Mundt @ 2011-06-21 12:47 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tue, Jun 21, 2011 at 01:57:08PM +0200, Rafael J. Wysocki wrote:
> On Tuesday, June 21, 2011, Rafael J. Wysocki wrote:
> > On Monday, June 20, 2011, Paul Mundt wrote:
> > > Given that these functions can return errors, it's probably more prudent
> > > to implement some timeout logic on top of the busy loop. Hardware does
> > > get stuck, after all.
> > 
> > Yes, it does, but we don't really know what timeout value to use in there.
> > 
> > In fact, failures of pd_power_down() don't really matter (at worst the power
> > domain won't be powered off really) and pd_power_up() should only return error
> > code if the error is known to be unrecoverable.
> > 
> > So, what about this: repeat certain number of times (I verified that it took
> > several iterations of the loop until the new value settled on my hardware)
> > and then return from pd_power_down() or go to sleep for a short time and repeat
> > in pd_power_up()?
> 
> Having discussed that with Magnus I changed pd_power_down() to use a timeout.
> Specifically, it will bail out if the state doesn't change after approximately
> 1 ms.
> 
Looks good to me, thanks for fixing it up.

Acked-by: Paul Mundt <lethal@linux-sh.org>

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains
  2011-06-21 11:06     ` Rafael J. Wysocki
@ 2011-06-21 14:47       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21 14:47 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

On Tue, 2011-06-21 at 13:06 +0200, Rafael J. Wysocki wrote:
> On Tuesday, June 21, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > Hi,
> > >
> > > This is the 4th update of the patchset adding support for generic I/O PM
> > > domains.  
> > >
> > > The patches have been reworked quite a bit to take feedback into
> > > account, but I left the Greg's ACK in [4/8] in the hope it still applies
> > > (Greg, please let me know in case it doesn't :-)).
> > >
> > > The model here is that a bunch of devices share a common power resource
> > > that can be turned on and off by software.  In addition to that, there
> > > are means to start and stop the activity of each device, for example
> > > by manipulating their clocks.  Moreover, there may be hierarchy of
> > > such things, for example power resource A may be necessary for devices
> > > a, b, c, which don't rely on any other power resources, and for devices
> > > x, y, z that also rely on power resource X.  In that case there one PM
> > > domain object representing devices a, b, c and power resource A, and 
> > > another PM domain object will represent devices x, y, z with power
> > > resource X, plus the first object will be the second one's parent.
> > >
> > > Note to Kevin: I know you'd like each PM domain to be able to go into several
> > > different states, but the situation will always be that in some of those
> > > states the devices' registers will remain intact, while in the rest of those
> > > states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> > > 1-3 preserve device registers.  Then it is not necessary to save device
> > > registers for "domain" states 1-3 and it only is necessary to save them
> > > when going to state 4.  In that case, .power_off() may map to the "go to
> > > state 4" operation (and analogously .power_on()), while the rest may be
> > > done by .stop_device() and .start_device().  IOW, .power_is_off = true
> > > means "the devices' registers have to be restored", so it need not map to
> > > any particular physical state of a (hardware) power domain.
> > 
> > Sure, but it's not only about register context save/restore.  It's about
> > the the governor hook and how you decide which state to enter.  IOW, the
> > governor decision is not only about whether or not you will lose
> > register context but also about the latency involved in entering &
> > exiting those states.
> > 
> > So from my perspective, having only 2-states at this level makes the
> > governor rather pointless since any decision making will have to be done
> > where ever the knowledge of the mulitple power states lives.
> 
> Well, in principle you can make the governor whatever you want, so it may
> as well know of multiple states.
> 
> Anyway, if using multiple domain states turns out to be useful at the core
> level, it may be added later with a separate patch.

OK

> > > Note to Magnus and Paul: I didn't use a global lock as suggested, because
> > > I think it may lead to completely unnecessary congestion in situations in
> > > which there are no hierarchies of PM domains.  It is quite easy to show that
> > > the code doesn't deadlock, because (1) no more than 2 locks are held by the
> > > same thread at a time (parent lock and child lock) and (2) they are always
> > > acquired in the same order (parent before the child).
> > >
> > > Overall, I think I've taken all of the important dependencies into
> > > consideration, but if you spot something suspicious, please let me know. :-)
> > > Wakeup is not covered at this point, because it's not necessary for the
> > > SH7372's A4LC power domain that's the first user of the new code, but it
> > > is quite clear how add the support for it.  Also, for more complicated
> > > cases it is necessary to take QoS requirements (latencies) into account,
> > > which is in the works (kind of).
> > >
> > > [1/8] - Update documentation to reflect the fact that struct dev_power_domain
> > >         callbacks take precedence over subsystem PM callbacks.
> > >
> > > [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
> > >         fact that those objects need not correspond to hardware power domains
> > >         directly.
> > >
> > > [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
> > >
> > > [4/8] - Introduce runtime PM support for generic I/O PM domains.
> > >
> > > [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
> > >         (that's necessary for the next patches).
> > >
> > > [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
> > >
> > > [7/8] - Add system-wide PM support for generic I/O PM domains.
> > >
> > > [8/8] - Use the new code to represent the SH7372's A4MP power domain.
> > >
> > > The patchset has been tested on SH7372 Mackerel board and appears to work
> > > correctly.
> > >
> > > I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> > > that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> > > which people may possibly use the naming that's going to change in their new
> > > code.  If you agree with that, please let me know, I'll need some serious
> > > ACKs below that patch if it's to be pushed for 3.0. ;-)
> > 
> > Just gave you my ack,
> 
> I thought the ACK was for [2/8] only, so do I understand correctly that it's
> for the entire series? :-)

So far, only for 2/8.  I'm planning to spend some time looking at the
rest of the series today.

Kevin

> > but [2/8] will need a minor update to apply on
> > Linus' master branch since another fix to mach-omap1/pm_bus.c just got
> > merged[1] via the OMAP tree.
> 
> Yes, I already rebased my patches on top of 3.0-rc4.
> 
> > I don't have any other fixes touching those files queued for v3.0 so I
> > don't expect any other conflicts there.
> 
> Thanks,
> Rafael



^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
@ 2011-06-21 14:47       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21 14:47 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Alan Stern, LKML,
	Linux PM mailing list, linux-sh

On Tue, 2011-06-21 at 13:06 +0200, Rafael J. Wysocki wrote:
> On Tuesday, June 21, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > Hi,
> > >
> > > This is the 4th update of the patchset adding support for generic I/O PM
> > > domains.  
> > >
> > > The patches have been reworked quite a bit to take feedback into
> > > account, but I left the Greg's ACK in [4/8] in the hope it still applies
> > > (Greg, please let me know in case it doesn't :-)).
> > >
> > > The model here is that a bunch of devices share a common power resource
> > > that can be turned on and off by software.  In addition to that, there
> > > are means to start and stop the activity of each device, for example
> > > by manipulating their clocks.  Moreover, there may be hierarchy of
> > > such things, for example power resource A may be necessary for devices
> > > a, b, c, which don't rely on any other power resources, and for devices
> > > x, y, z that also rely on power resource X.  In that case there one PM
> > > domain object representing devices a, b, c and power resource A, and 
> > > another PM domain object will represent devices x, y, z with power
> > > resource X, plus the first object will be the second one's parent.
> > >
> > > Note to Kevin: I know you'd like each PM domain to be able to go into several
> > > different states, but the situation will always be that in some of those
> > > states the devices' registers will remain intact, while in the rest of those
> > > states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> > > 1-3 preserve device registers.  Then it is not necessary to save device
> > > registers for "domain" states 1-3 and it only is necessary to save them
> > > when going to state 4.  In that case, .power_off() may map to the "go to
> > > state 4" operation (and analogously .power_on()), while the rest may be
> > > done by .stop_device() and .start_device().  IOW, .power_is_off == true
> > > means "the devices' registers have to be restored", so it need not map to
> > > any particular physical state of a (hardware) power domain.
> > 
> > Sure, but it's not only about register context save/restore.  It's about
> > the the governor hook and how you decide which state to enter.  IOW, the
> > governor decision is not only about whether or not you will lose
> > register context but also about the latency involved in entering &
> > exiting those states.
> > 
> > So from my perspective, having only 2-states at this level makes the
> > governor rather pointless since any decision making will have to be done
> > where ever the knowledge of the mulitple power states lives.
> 
> Well, in principle you can make the governor whatever you want, so it may
> as well know of multiple states.
> 
> Anyway, if using multiple domain states turns out to be useful at the core
> level, it may be added later with a separate patch.

OK

> > > Note to Magnus and Paul: I didn't use a global lock as suggested, because
> > > I think it may lead to completely unnecessary congestion in situations in
> > > which there are no hierarchies of PM domains.  It is quite easy to show that
> > > the code doesn't deadlock, because (1) no more than 2 locks are held by the
> > > same thread at a time (parent lock and child lock) and (2) they are always
> > > acquired in the same order (parent before the child).
> > >
> > > Overall, I think I've taken all of the important dependencies into
> > > consideration, but if you spot something suspicious, please let me know. :-)
> > > Wakeup is not covered at this point, because it's not necessary for the
> > > SH7372's A4LC power domain that's the first user of the new code, but it
> > > is quite clear how add the support for it.  Also, for more complicated
> > > cases it is necessary to take QoS requirements (latencies) into account,
> > > which is in the works (kind of).
> > >
> > > [1/8] - Update documentation to reflect the fact that struct dev_power_domain
> > >         callbacks take precedence over subsystem PM callbacks.
> > >
> > > [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
> > >         fact that those objects need not correspond to hardware power domains
> > >         directly.
> > >
> > > [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
> > >
> > > [4/8] - Introduce runtime PM support for generic I/O PM domains.
> > >
> > > [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
> > >         (that's necessary for the next patches).
> > >
> > > [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
> > >
> > > [7/8] - Add system-wide PM support for generic I/O PM domains.
> > >
> > > [8/8] - Use the new code to represent the SH7372's A4MP power domain.
> > >
> > > The patchset has been tested on SH7372 Mackerel board and appears to work
> > > correctly.
> > >
> > > I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> > > that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> > > which people may possibly use the naming that's going to change in their new
> > > code.  If you agree with that, please let me know, I'll need some serious
> > > ACKs below that patch if it's to be pushed for 3.0. ;-)
> > 
> > Just gave you my ack,
> 
> I thought the ACK was for [2/8] only, so do I understand correctly that it's
> for the entire series? :-)

So far, only for 2/8.  I'm planning to spend some time looking at the
rest of the series today.

Kevin

> > but [2/8] will need a minor update to apply on
> > Linus' master branch since another fix to mach-omap1/pm_bus.c just got
> > merged[1] via the OMAP tree.
> 
> Yes, I already rebased my patches on top of 3.0-rc4.
> 
> > I don't have any other fixes touching those files queued for v3.0 so I
> > don't expect any other conflicts there.
> 
> Thanks,
> Rafael



^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5)
  2011-06-21 11:06     ` Rafael J. Wysocki
  (?)
@ 2011-06-21 14:47     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21 14:47 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tue, 2011-06-21 at 13:06 +0200, Rafael J. Wysocki wrote:
> On Tuesday, June 21, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > Hi,
> > >
> > > This is the 4th update of the patchset adding support for generic I/O PM
> > > domains.  
> > >
> > > The patches have been reworked quite a bit to take feedback into
> > > account, but I left the Greg's ACK in [4/8] in the hope it still applies
> > > (Greg, please let me know in case it doesn't :-)).
> > >
> > > The model here is that a bunch of devices share a common power resource
> > > that can be turned on and off by software.  In addition to that, there
> > > are means to start and stop the activity of each device, for example
> > > by manipulating their clocks.  Moreover, there may be hierarchy of
> > > such things, for example power resource A may be necessary for devices
> > > a, b, c, which don't rely on any other power resources, and for devices
> > > x, y, z that also rely on power resource X.  In that case there one PM
> > > domain object representing devices a, b, c and power resource A, and 
> > > another PM domain object will represent devices x, y, z with power
> > > resource X, plus the first object will be the second one's parent.
> > >
> > > Note to Kevin: I know you'd like each PM domain to be able to go into several
> > > different states, but the situation will always be that in some of those
> > > states the devices' registers will remain intact, while in the rest of those
> > > states they will be reset.  Say, there are states 1, 2, 3, 4 and states
> > > 1-3 preserve device registers.  Then it is not necessary to save device
> > > registers for "domain" states 1-3 and it only is necessary to save them
> > > when going to state 4.  In that case, .power_off() may map to the "go to
> > > state 4" operation (and analogously .power_on()), while the rest may be
> > > done by .stop_device() and .start_device().  IOW, .power_is_off == true
> > > means "the devices' registers have to be restored", so it need not map to
> > > any particular physical state of a (hardware) power domain.
> > 
> > Sure, but it's not only about register context save/restore.  It's about
> > the the governor hook and how you decide which state to enter.  IOW, the
> > governor decision is not only about whether or not you will lose
> > register context but also about the latency involved in entering &
> > exiting those states.
> > 
> > So from my perspective, having only 2-states at this level makes the
> > governor rather pointless since any decision making will have to be done
> > where ever the knowledge of the mulitple power states lives.
> 
> Well, in principle you can make the governor whatever you want, so it may
> as well know of multiple states.
> 
> Anyway, if using multiple domain states turns out to be useful at the core
> level, it may be added later with a separate patch.

OK

> > > Note to Magnus and Paul: I didn't use a global lock as suggested, because
> > > I think it may lead to completely unnecessary congestion in situations in
> > > which there are no hierarchies of PM domains.  It is quite easy to show that
> > > the code doesn't deadlock, because (1) no more than 2 locks are held by the
> > > same thread at a time (parent lock and child lock) and (2) they are always
> > > acquired in the same order (parent before the child).
> > >
> > > Overall, I think I've taken all of the important dependencies into
> > > consideration, but if you spot something suspicious, please let me know. :-)
> > > Wakeup is not covered at this point, because it's not necessary for the
> > > SH7372's A4LC power domain that's the first user of the new code, but it
> > > is quite clear how add the support for it.  Also, for more complicated
> > > cases it is necessary to take QoS requirements (latencies) into account,
> > > which is in the works (kind of).
> > >
> > > [1/8] - Update documentation to reflect the fact that struct dev_power_domain
> > >         callbacks take precedence over subsystem PM callbacks.
> > >
> > > [2/8] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
> > >         fact that those objects need not correspond to hardware power domains
> > >         directly.
> > >
> > > [3/8] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME
> > >
> > > [4/8] - Introduce runtime PM support for generic I/O PM domains.
> > >
> > > [5/8] - Introduce generic "noirq" callbacks for system suspend/hibernation
> > >         (that's necessary for the next patches).
> > >
> > > [6/8] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME
> > >
> > > [7/8] - Add system-wide PM support for generic I/O PM domains.
> > >
> > > [8/8] - Use the new code to represent the SH7372's A4MP power domain.
> > >
> > > The patchset has been tested on SH7372 Mackerel board and appears to work
> > > correctly.
> > >
> > > I'd like to push [1/8] for 3.0 (it may be regarded as a fix), but I _think_
> > > that it may be a good idea to push [2/8] for 3.0 too, to limit the time in
> > > which people may possibly use the naming that's going to change in their new
> > > code.  If you agree with that, please let me know, I'll need some serious
> > > ACKs below that patch if it's to be pushed for 3.0. ;-)
> > 
> > Just gave you my ack,
> 
> I thought the ACK was for [2/8] only, so do I understand correctly that it's
> for the entire series? :-)

So far, only for 2/8.  I'm planning to spend some time looking at the
rest of the series today.

Kevin

> > but [2/8] will need a minor update to apply on
> > Linus' master branch since another fix to mach-omap1/pm_bus.c just got
> > merged[1] via the OMAP tree.
> 
> Yes, I already rebased my patches on top of 3.0-rc4.
> 
> > I don't have any other fixes touching those files queued for v3.0 so I
> > don't expect any other conflicts there.
> 
> Thanks,
> Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-19 22:02     ` Rafael J. Wysocki
@ 2011-06-21 17:42       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21 17:42 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Introduce common headers, helper functions and callbacks allowing
> platforms to use simple generic power domains for runtime power
> management.
>
> Introduce struct generic_pm_domain to be used for representing
> power domains that each contain a number of devices and may be
> parent domains or subdomains with respect to other power domains.
> Among other things, this structure includes callbacks to be
> provided by platforms for performing specific tasks related to
> power management (i.e. ->stop_device() may disable a device's
> clocks, while ->start_device() may enable them, ->power_off() is
> supposed to remove power from the entire power domain
> and ->power_on() is supposed to restore it).
>
> Introduce functions that can be used as power domain runtime PM
> callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> as well as helper functions for the initialization of a power
> domain represented by a struct generic_power_domain object,
> adding a device to or removing a device from it and adding or
> removing subdomains.
>
> Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> selected by the platforms that want to use the new code.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> ---
>
> Hi,
>
> I this version of the patch I made the following modifications:
>
> * Removed the change adding platform_data to struct dev_pm_domain,
>   because that field is not going to be necessary in the near future.
>
> * Moved the code calling drv->pm->runtime_suspend(dev) to a separate
>   function that returns immediately if dle->need_restore is set for the
>   given device (meaning that drv->pm->runtime_suspend(dev) has already
>   been called for it and the corresponding ->runtime_resume() hasn't).
>   This fixes a bug where drv->pm->runtime_suspend() could be called for
>   a device whose state hasn't been restored after power cycling its
>   PM domain.
>
> * Made pm_genpd_add_device() return error code on an attempt to add a device
>   do a PM domain whose power_is_off is set (that complemets the previous
>   modification).
>
> * Makde the .power_on() and .power_off() generic PM domain callbacks take
>   (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).
>
> Thanks,
> Rafael

There's a guiding assumption in this generic PM domain layer that the
runtime PM callbacks need only be called if power to the underlying PM
domain is actually being cut.  As a result, devices no longer have a
callback called for other low-power states where the power may not
actually be cut (a.k.a low-power with memory & logic retention.)

However, there are devices (at least on OMAP, but I presume on all SoCs)
where the driver will need to do other "stuff" for *all* low-power
transitions, not just the power-off ones (e.g. device specific idle mode
registers, clearing device-specific events/state that prevent low power
transitions, etc.)

Because of this, I don't currently see how to use these generic PM
domains on devices with multiple power states since the runtime PM
callbacks are only called for a subset of the power states.

I haven't given this too much thought yet (especially the system PM
aspects), but in order for generic PM domains to be more broadly useful
for runtime PM, I'm starting to think that this series should add
another set of callbacks: .power_off, .power_on or something similar.
The .runtime_suspend/.runtime_resume callbacks would then be used for
all power states and the .power_off/.power_on callbacks used only when
power is actually cut.

[...]

> Index: linux-2.6/drivers/base/power/domain.c
> =================================> --- /dev/null
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -0,0 +1,490 @@
> +/*
> + * drivers/base/power/domain.c - Common code related to device power domains.
> + *
> + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
> +#include <linux/pm_domain.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +
> +#ifdef CONFIG_PM_RUNTIME
> +
> +static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
> +{
> +	if (!WARN_ON(genpd->sd_count = 0))
> +			genpd->sd_count--;
> +}
> +
> +/**
> + * __pm_genpd_save_device - Save the pre-suspend state of a device.
> + * @dle: Device list entry of the device to save the state of.
> + * @genpd: PM domain the device belongs to.
> + */
> +static int __pm_genpd_save_device(struct dev_list_entry *dle,
> +				  struct generic_pm_domain *genpd)
> +{
> +	struct device *dev = dle->dev;
> +	struct device_driver *drv = dev->driver;
> +	int ret = 0;
> +
> +	if (dle->need_restore)
> +		return 0;
> +
> +	if (genpd->start_device)
> +		genpd->start_device(dev);
> +
> +	if (drv && drv->pm && drv->pm->runtime_suspend)

The start/stop device calls should probably be included inside this 'if'
since there's no reason to restart and re-stop the device if there is no
callback to be run.

Some drivers have alterntive ways of saving context (shadow registers,
manual save/restore per-xfer, etc.) and thus have no callbacks for
context save/restore.

> +		ret = drv->pm->runtime_suspend(dev);
> +
> +	if (genpd->stop_device)
> +		genpd->stop_device(dev);
> +
> +	if (!ret)
> +		dle->need_restore = true;
> +
> +	return ret;
> +}
> +
> +/**
> + * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
> + * @dle: Device list entry of the device to restore the state of.
> + * @genpd: PM domain the device belongs to.
> + */
> +static void __pm_genpd_restore_device(struct dev_list_entry *dle,
> +				      struct generic_pm_domain *genpd)
> +{
> +	struct device *dev = dle->dev;
> +	struct device_driver *drv = dev->driver;
> +
> +	if (!dle->need_restore)
> +		return;
> +
> +	if (genpd->start_device)
> +		genpd->start_device(dev);
> +
> +	if (drv && drv->pm && drv->pm->runtime_resume)

Similar to the 'save' case, the start/stop device calls should also be
included inside this 'if'.

> +		drv->pm->runtime_resume(dev);
> +
> +	if (genpd->stop_device)
> +		genpd->stop_device(dev);
> +
> +	dle->need_restore = false;
> +}

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
@ 2011-06-21 17:42       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21 17:42 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Introduce common headers, helper functions and callbacks allowing
> platforms to use simple generic power domains for runtime power
> management.
>
> Introduce struct generic_pm_domain to be used for representing
> power domains that each contain a number of devices and may be
> parent domains or subdomains with respect to other power domains.
> Among other things, this structure includes callbacks to be
> provided by platforms for performing specific tasks related to
> power management (i.e. ->stop_device() may disable a device's
> clocks, while ->start_device() may enable them, ->power_off() is
> supposed to remove power from the entire power domain
> and ->power_on() is supposed to restore it).
>
> Introduce functions that can be used as power domain runtime PM
> callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> as well as helper functions for the initialization of a power
> domain represented by a struct generic_power_domain object,
> adding a device to or removing a device from it and adding or
> removing subdomains.
>
> Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> selected by the platforms that want to use the new code.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> ---
>
> Hi,
>
> I this version of the patch I made the following modifications:
>
> * Removed the change adding platform_data to struct dev_pm_domain,
>   because that field is not going to be necessary in the near future.
>
> * Moved the code calling drv->pm->runtime_suspend(dev) to a separate
>   function that returns immediately if dle->need_restore is set for the
>   given device (meaning that drv->pm->runtime_suspend(dev) has already
>   been called for it and the corresponding ->runtime_resume() hasn't).
>   This fixes a bug where drv->pm->runtime_suspend() could be called for
>   a device whose state hasn't been restored after power cycling its
>   PM domain.
>
> * Made pm_genpd_add_device() return error code on an attempt to add a device
>   do a PM domain whose power_is_off is set (that complemets the previous
>   modification).
>
> * Makde the .power_on() and .power_off() generic PM domain callbacks take
>   (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).
>
> Thanks,
> Rafael

There's a guiding assumption in this generic PM domain layer that the
runtime PM callbacks need only be called if power to the underlying PM
domain is actually being cut.  As a result, devices no longer have a
callback called for other low-power states where the power may not
actually be cut (a.k.a low-power with memory & logic retention.)

However, there are devices (at least on OMAP, but I presume on all SoCs)
where the driver will need to do other "stuff" for *all* low-power
transitions, not just the power-off ones (e.g. device specific idle mode
registers, clearing device-specific events/state that prevent low power
transitions, etc.)

Because of this, I don't currently see how to use these generic PM
domains on devices with multiple power states since the runtime PM
callbacks are only called for a subset of the power states.

I haven't given this too much thought yet (especially the system PM
aspects), but in order for generic PM domains to be more broadly useful
for runtime PM, I'm starting to think that this series should add
another set of callbacks: .power_off, .power_on or something similar.
The .runtime_suspend/.runtime_resume callbacks would then be used for
all power states and the .power_off/.power_on callbacks used only when
power is actually cut.

[...]

> Index: linux-2.6/drivers/base/power/domain.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -0,0 +1,490 @@
> +/*
> + * drivers/base/power/domain.c - Common code related to device power domains.
> + *
> + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
> +#include <linux/pm_domain.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +
> +#ifdef CONFIG_PM_RUNTIME
> +
> +static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
> +{
> +	if (!WARN_ON(genpd->sd_count == 0))
> +			genpd->sd_count--;
> +}
> +
> +/**
> + * __pm_genpd_save_device - Save the pre-suspend state of a device.
> + * @dle: Device list entry of the device to save the state of.
> + * @genpd: PM domain the device belongs to.
> + */
> +static int __pm_genpd_save_device(struct dev_list_entry *dle,
> +				  struct generic_pm_domain *genpd)
> +{
> +	struct device *dev = dle->dev;
> +	struct device_driver *drv = dev->driver;
> +	int ret = 0;
> +
> +	if (dle->need_restore)
> +		return 0;
> +
> +	if (genpd->start_device)
> +		genpd->start_device(dev);
> +
> +	if (drv && drv->pm && drv->pm->runtime_suspend)

The start/stop device calls should probably be included inside this 'if'
since there's no reason to restart and re-stop the device if there is no
callback to be run.

Some drivers have alterntive ways of saving context (shadow registers,
manual save/restore per-xfer, etc.) and thus have no callbacks for
context save/restore.

> +		ret = drv->pm->runtime_suspend(dev);
> +
> +	if (genpd->stop_device)
> +		genpd->stop_device(dev);
> +
> +	if (!ret)
> +		dle->need_restore = true;
> +
> +	return ret;
> +}
> +
> +/**
> + * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
> + * @dle: Device list entry of the device to restore the state of.
> + * @genpd: PM domain the device belongs to.
> + */
> +static void __pm_genpd_restore_device(struct dev_list_entry *dle,
> +				      struct generic_pm_domain *genpd)
> +{
> +	struct device *dev = dle->dev;
> +	struct device_driver *drv = dev->driver;
> +
> +	if (!dle->need_restore)
> +		return;
> +
> +	if (genpd->start_device)
> +		genpd->start_device(dev);
> +
> +	if (drv && drv->pm && drv->pm->runtime_resume)

Similar to the 'save' case, the start/stop device calls should also be
included inside this 'if'.

> +		drv->pm->runtime_resume(dev);
> +
> +	if (genpd->stop_device)
> +		genpd->stop_device(dev);
> +
> +	dle->need_restore = false;
> +}

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-19 22:02     ` Rafael J. Wysocki
  (?)
@ 2011-06-21 17:42     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-21 17:42 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Introduce common headers, helper functions and callbacks allowing
> platforms to use simple generic power domains for runtime power
> management.
>
> Introduce struct generic_pm_domain to be used for representing
> power domains that each contain a number of devices and may be
> parent domains or subdomains with respect to other power domains.
> Among other things, this structure includes callbacks to be
> provided by platforms for performing specific tasks related to
> power management (i.e. ->stop_device() may disable a device's
> clocks, while ->start_device() may enable them, ->power_off() is
> supposed to remove power from the entire power domain
> and ->power_on() is supposed to restore it).
>
> Introduce functions that can be used as power domain runtime PM
> callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> as well as helper functions for the initialization of a power
> domain represented by a struct generic_power_domain object,
> adding a device to or removing a device from it and adding or
> removing subdomains.
>
> Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> selected by the platforms that want to use the new code.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> ---
>
> Hi,
>
> I this version of the patch I made the following modifications:
>
> * Removed the change adding platform_data to struct dev_pm_domain,
>   because that field is not going to be necessary in the near future.
>
> * Moved the code calling drv->pm->runtime_suspend(dev) to a separate
>   function that returns immediately if dle->need_restore is set for the
>   given device (meaning that drv->pm->runtime_suspend(dev) has already
>   been called for it and the corresponding ->runtime_resume() hasn't).
>   This fixes a bug where drv->pm->runtime_suspend() could be called for
>   a device whose state hasn't been restored after power cycling its
>   PM domain.
>
> * Made pm_genpd_add_device() return error code on an attempt to add a device
>   do a PM domain whose power_is_off is set (that complemets the previous
>   modification).
>
> * Makde the .power_on() and .power_off() generic PM domain callbacks take
>   (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).
>
> Thanks,
> Rafael

There's a guiding assumption in this generic PM domain layer that the
runtime PM callbacks need only be called if power to the underlying PM
domain is actually being cut.  As a result, devices no longer have a
callback called for other low-power states where the power may not
actually be cut (a.k.a low-power with memory & logic retention.)

However, there are devices (at least on OMAP, but I presume on all SoCs)
where the driver will need to do other "stuff" for *all* low-power
transitions, not just the power-off ones (e.g. device specific idle mode
registers, clearing device-specific events/state that prevent low power
transitions, etc.)

Because of this, I don't currently see how to use these generic PM
domains on devices with multiple power states since the runtime PM
callbacks are only called for a subset of the power states.

I haven't given this too much thought yet (especially the system PM
aspects), but in order for generic PM domains to be more broadly useful
for runtime PM, I'm starting to think that this series should add
another set of callbacks: .power_off, .power_on or something similar.
The .runtime_suspend/.runtime_resume callbacks would then be used for
all power states and the .power_off/.power_on callbacks used only when
power is actually cut.

[...]

> Index: linux-2.6/drivers/base/power/domain.c
> ===================================================================
> --- /dev/null
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -0,0 +1,490 @@
> +/*
> + * drivers/base/power/domain.c - Common code related to device power domains.
> + *
> + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
> +#include <linux/pm_domain.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +
> +#ifdef CONFIG_PM_RUNTIME
> +
> +static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
> +{
> +	if (!WARN_ON(genpd->sd_count == 0))
> +			genpd->sd_count--;
> +}
> +
> +/**
> + * __pm_genpd_save_device - Save the pre-suspend state of a device.
> + * @dle: Device list entry of the device to save the state of.
> + * @genpd: PM domain the device belongs to.
> + */
> +static int __pm_genpd_save_device(struct dev_list_entry *dle,
> +				  struct generic_pm_domain *genpd)
> +{
> +	struct device *dev = dle->dev;
> +	struct device_driver *drv = dev->driver;
> +	int ret = 0;
> +
> +	if (dle->need_restore)
> +		return 0;
> +
> +	if (genpd->start_device)
> +		genpd->start_device(dev);
> +
> +	if (drv && drv->pm && drv->pm->runtime_suspend)

The start/stop device calls should probably be included inside this 'if'
since there's no reason to restart and re-stop the device if there is no
callback to be run.

Some drivers have alterntive ways of saving context (shadow registers,
manual save/restore per-xfer, etc.) and thus have no callbacks for
context save/restore.

> +		ret = drv->pm->runtime_suspend(dev);
> +
> +	if (genpd->stop_device)
> +		genpd->stop_device(dev);
> +
> +	if (!ret)
> +		dle->need_restore = true;
> +
> +	return ret;
> +}
> +
> +/**
> + * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
> + * @dle: Device list entry of the device to restore the state of.
> + * @genpd: PM domain the device belongs to.
> + */
> +static void __pm_genpd_restore_device(struct dev_list_entry *dle,
> +				      struct generic_pm_domain *genpd)
> +{
> +	struct device *dev = dle->dev;
> +	struct device_driver *drv = dev->driver;
> +
> +	if (!dle->need_restore)
> +		return;
> +
> +	if (genpd->start_device)
> +		genpd->start_device(dev);
> +
> +	if (drv && drv->pm && drv->pm->runtime_resume)

Similar to the 'save' case, the start/stop device calls should also be
included inside this 'if'.

> +		drv->pm->runtime_resume(dev);
> +
> +	if (genpd->stop_device)
> +		genpd->stop_device(dev);
> +
> +	dle->need_restore = false;
> +}

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-21 17:42       ` Kevin Hilman
@ 2011-06-22  0:07         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22  0:07 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Tuesday, June 21, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Introduce common headers, helper functions and callbacks allowing
> > platforms to use simple generic power domains for runtime power
> > management.
> >
> > Introduce struct generic_pm_domain to be used for representing
> > power domains that each contain a number of devices and may be
> > parent domains or subdomains with respect to other power domains.
> > Among other things, this structure includes callbacks to be
> > provided by platforms for performing specific tasks related to
> > power management (i.e. ->stop_device() may disable a device's
> > clocks, while ->start_device() may enable them, ->power_off() is
> > supposed to remove power from the entire power domain
> > and ->power_on() is supposed to restore it).
> >
> > Introduce functions that can be used as power domain runtime PM
> > callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> > as well as helper functions for the initialization of a power
> > domain represented by a struct generic_power_domain object,
> > adding a device to or removing a device from it and adding or
> > removing subdomains.
> >
> > Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> > selected by the platforms that want to use the new code.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> > ---
> >
> > Hi,
> >
> > I this version of the patch I made the following modifications:
> >
> > * Removed the change adding platform_data to struct dev_pm_domain,
> >   because that field is not going to be necessary in the near future.
> >
> > * Moved the code calling drv->pm->runtime_suspend(dev) to a separate
> >   function that returns immediately if dle->need_restore is set for the
> >   given device (meaning that drv->pm->runtime_suspend(dev) has already
> >   been called for it and the corresponding ->runtime_resume() hasn't).
> >   This fixes a bug where drv->pm->runtime_suspend() could be called for
> >   a device whose state hasn't been restored after power cycling its
> >   PM domain.
> >
> > * Made pm_genpd_add_device() return error code on an attempt to add a device
> >   do a PM domain whose power_is_off is set (that complemets the previous
> >   modification).
> >
> > * Makde the .power_on() and .power_off() generic PM domain callbacks take
> >   (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).
> >
> > Thanks,
> > Rafael
> 
> There's a guiding assumption in this generic PM domain layer that the
> runtime PM callbacks need only be called if power to the underlying PM
> domain is actually being cut.  As a result, devices no longer have a
> callback called for other low-power states where the power may not
> actually be cut (a.k.a low-power with memory & logic retention.)
> 
> However, there are devices (at least on OMAP, but I presume on all SoCs)
> where the driver will need to do other "stuff" for *all* low-power
> transitions, not just the power-off ones (e.g. device specific idle mode
> registers, clearing device-specific events/state that prevent low power
> transitions, etc.)
> 
> Because of this, I don't currently see how to use these generic PM
> domains on devices with multiple power states since the runtime PM
> callbacks are only called for a subset of the power states.
> 
> I haven't given this too much thought yet (especially the system PM
> aspects), but in order for generic PM domains to be more broadly useful
> for runtime PM, I'm starting to think that this series should add
> another set of callbacks: .power_off, .power_on or something similar.
> The .runtime_suspend/.runtime_resume callbacks would then be used for
> all power states and the .power_off/.power_on callbacks used only when
> power is actually cut.

Well, I _really_ would like to avoid adding more callbacks to struct
dev_pm_ops, if that's what you mean, because we already seem to have
problems with managing the existing ones.

Also, IMO, you can always map every system with more power states to the
model where there are only "device active" (runtime PM RPM_ACTIVE) "device
stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
represented here.

If anything more fine grained is necessary or useful, I'd say you need a
more complicated model, but I'd prefer to avoid further complications in this
patchset.

> [...]
> 
> > Index: linux-2.6/drivers/base/power/domain.c
> > =================================> > --- /dev/null
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -0,0 +1,490 @@
> > +/*
> > + * drivers/base/power/domain.c - Common code related to device power domains.
> > + *
> > + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
> > +#include <linux/pm_domain.h>
> > +#include <linux/slab.h>
> > +#include <linux/err.h>
> > +
> > +#ifdef CONFIG_PM_RUNTIME
> > +
> > +static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
> > +{
> > +	if (!WARN_ON(genpd->sd_count = 0))
> > +			genpd->sd_count--;
> > +}
> > +
> > +/**
> > + * __pm_genpd_save_device - Save the pre-suspend state of a device.
> > + * @dle: Device list entry of the device to save the state of.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static int __pm_genpd_save_device(struct dev_list_entry *dle,
> > +				  struct generic_pm_domain *genpd)
> > +{
> > +	struct device *dev = dle->dev;
> > +	struct device_driver *drv = dev->driver;
> > +	int ret = 0;
> > +
> > +	if (dle->need_restore)
> > +		return 0;
> > +
> > +	if (genpd->start_device)
> > +		genpd->start_device(dev);
> > +
> > +	if (drv && drv->pm && drv->pm->runtime_suspend)
> 
> The start/stop device calls should probably be included inside this 'if'
> since there's no reason to restart and re-stop the device if there is no
> callback to be run.

That's a good point, I'll do that.

> Some drivers have alterntive ways of saving context (shadow registers,
> manual save/restore per-xfer, etc.) and thus have no callbacks for
> context save/restore.
> 
> > +		ret = drv->pm->runtime_suspend(dev);
> > +
> > +	if (genpd->stop_device)
> > +		genpd->stop_device(dev);
> > +
> > +	if (!ret)
> > +		dle->need_restore = true;
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
> > + * @dle: Device list entry of the device to restore the state of.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static void __pm_genpd_restore_device(struct dev_list_entry *dle,
> > +				      struct generic_pm_domain *genpd)
> > +{
> > +	struct device *dev = dle->dev;
> > +	struct device_driver *drv = dev->driver;
> > +
> > +	if (!dle->need_restore)
> > +		return;
> > +
> > +	if (genpd->start_device)
> > +		genpd->start_device(dev);
> > +
> > +	if (drv && drv->pm && drv->pm->runtime_resume)
> 
> Similar to the 'save' case, the start/stop device calls should also be
> included inside this 'if'.

Agreed.

> > +		drv->pm->runtime_resume(dev);
> > +
> > +	if (genpd->stop_device)
> > +		genpd->stop_device(dev);
> > +
> > +	dle->need_restore = false;
> > +}

Thanks for the comments, I'm going to implement your suggestions.

Take care,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
@ 2011-06-22  0:07         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22  0:07 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Tuesday, June 21, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Introduce common headers, helper functions and callbacks allowing
> > platforms to use simple generic power domains for runtime power
> > management.
> >
> > Introduce struct generic_pm_domain to be used for representing
> > power domains that each contain a number of devices and may be
> > parent domains or subdomains with respect to other power domains.
> > Among other things, this structure includes callbacks to be
> > provided by platforms for performing specific tasks related to
> > power management (i.e. ->stop_device() may disable a device's
> > clocks, while ->start_device() may enable them, ->power_off() is
> > supposed to remove power from the entire power domain
> > and ->power_on() is supposed to restore it).
> >
> > Introduce functions that can be used as power domain runtime PM
> > callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> > as well as helper functions for the initialization of a power
> > domain represented by a struct generic_power_domain object,
> > adding a device to or removing a device from it and adding or
> > removing subdomains.
> >
> > Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> > selected by the platforms that want to use the new code.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> > ---
> >
> > Hi,
> >
> > I this version of the patch I made the following modifications:
> >
> > * Removed the change adding platform_data to struct dev_pm_domain,
> >   because that field is not going to be necessary in the near future.
> >
> > * Moved the code calling drv->pm->runtime_suspend(dev) to a separate
> >   function that returns immediately if dle->need_restore is set for the
> >   given device (meaning that drv->pm->runtime_suspend(dev) has already
> >   been called for it and the corresponding ->runtime_resume() hasn't).
> >   This fixes a bug where drv->pm->runtime_suspend() could be called for
> >   a device whose state hasn't been restored after power cycling its
> >   PM domain.
> >
> > * Made pm_genpd_add_device() return error code on an attempt to add a device
> >   do a PM domain whose power_is_off is set (that complemets the previous
> >   modification).
> >
> > * Makde the .power_on() and .power_off() generic PM domain callbacks take
> >   (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).
> >
> > Thanks,
> > Rafael
> 
> There's a guiding assumption in this generic PM domain layer that the
> runtime PM callbacks need only be called if power to the underlying PM
> domain is actually being cut.  As a result, devices no longer have a
> callback called for other low-power states where the power may not
> actually be cut (a.k.a low-power with memory & logic retention.)
> 
> However, there are devices (at least on OMAP, but I presume on all SoCs)
> where the driver will need to do other "stuff" for *all* low-power
> transitions, not just the power-off ones (e.g. device specific idle mode
> registers, clearing device-specific events/state that prevent low power
> transitions, etc.)
> 
> Because of this, I don't currently see how to use these generic PM
> domains on devices with multiple power states since the runtime PM
> callbacks are only called for a subset of the power states.
> 
> I haven't given this too much thought yet (especially the system PM
> aspects), but in order for generic PM domains to be more broadly useful
> for runtime PM, I'm starting to think that this series should add
> another set of callbacks: .power_off, .power_on or something similar.
> The .runtime_suspend/.runtime_resume callbacks would then be used for
> all power states and the .power_off/.power_on callbacks used only when
> power is actually cut.

Well, I _really_ would like to avoid adding more callbacks to struct
dev_pm_ops, if that's what you mean, because we already seem to have
problems with managing the existing ones.

Also, IMO, you can always map every system with more power states to the
model where there are only "device active" (runtime PM RPM_ACTIVE) "device
stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
represented here.

If anything more fine grained is necessary or useful, I'd say you need a
more complicated model, but I'd prefer to avoid further complications in this
patchset.

> [...]
> 
> > Index: linux-2.6/drivers/base/power/domain.c
> > ===================================================================
> > --- /dev/null
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -0,0 +1,490 @@
> > +/*
> > + * drivers/base/power/domain.c - Common code related to device power domains.
> > + *
> > + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
> > +#include <linux/pm_domain.h>
> > +#include <linux/slab.h>
> > +#include <linux/err.h>
> > +
> > +#ifdef CONFIG_PM_RUNTIME
> > +
> > +static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
> > +{
> > +	if (!WARN_ON(genpd->sd_count == 0))
> > +			genpd->sd_count--;
> > +}
> > +
> > +/**
> > + * __pm_genpd_save_device - Save the pre-suspend state of a device.
> > + * @dle: Device list entry of the device to save the state of.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static int __pm_genpd_save_device(struct dev_list_entry *dle,
> > +				  struct generic_pm_domain *genpd)
> > +{
> > +	struct device *dev = dle->dev;
> > +	struct device_driver *drv = dev->driver;
> > +	int ret = 0;
> > +
> > +	if (dle->need_restore)
> > +		return 0;
> > +
> > +	if (genpd->start_device)
> > +		genpd->start_device(dev);
> > +
> > +	if (drv && drv->pm && drv->pm->runtime_suspend)
> 
> The start/stop device calls should probably be included inside this 'if'
> since there's no reason to restart and re-stop the device if there is no
> callback to be run.

That's a good point, I'll do that.

> Some drivers have alterntive ways of saving context (shadow registers,
> manual save/restore per-xfer, etc.) and thus have no callbacks for
> context save/restore.
> 
> > +		ret = drv->pm->runtime_suspend(dev);
> > +
> > +	if (genpd->stop_device)
> > +		genpd->stop_device(dev);
> > +
> > +	if (!ret)
> > +		dle->need_restore = true;
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
> > + * @dle: Device list entry of the device to restore the state of.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static void __pm_genpd_restore_device(struct dev_list_entry *dle,
> > +				      struct generic_pm_domain *genpd)
> > +{
> > +	struct device *dev = dle->dev;
> > +	struct device_driver *drv = dev->driver;
> > +
> > +	if (!dle->need_restore)
> > +		return;
> > +
> > +	if (genpd->start_device)
> > +		genpd->start_device(dev);
> > +
> > +	if (drv && drv->pm && drv->pm->runtime_resume)
> 
> Similar to the 'save' case, the start/stop device calls should also be
> included inside this 'if'.

Agreed.

> > +		drv->pm->runtime_resume(dev);
> > +
> > +	if (genpd->stop_device)
> > +		genpd->stop_device(dev);
> > +
> > +	dle->need_restore = false;
> > +}

Thanks for the comments, I'm going to implement your suggestions.

Take care,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-21 17:42       ` Kevin Hilman
  (?)
  (?)
@ 2011-06-22  0:07       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22  0:07 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tuesday, June 21, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Introduce common headers, helper functions and callbacks allowing
> > platforms to use simple generic power domains for runtime power
> > management.
> >
> > Introduce struct generic_pm_domain to be used for representing
> > power domains that each contain a number of devices and may be
> > parent domains or subdomains with respect to other power domains.
> > Among other things, this structure includes callbacks to be
> > provided by platforms for performing specific tasks related to
> > power management (i.e. ->stop_device() may disable a device's
> > clocks, while ->start_device() may enable them, ->power_off() is
> > supposed to remove power from the entire power domain
> > and ->power_on() is supposed to restore it).
> >
> > Introduce functions that can be used as power domain runtime PM
> > callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> > as well as helper functions for the initialization of a power
> > domain represented by a struct generic_power_domain object,
> > adding a device to or removing a device from it and adding or
> > removing subdomains.
> >
> > Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> > selected by the platforms that want to use the new code.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> > ---
> >
> > Hi,
> >
> > I this version of the patch I made the following modifications:
> >
> > * Removed the change adding platform_data to struct dev_pm_domain,
> >   because that field is not going to be necessary in the near future.
> >
> > * Moved the code calling drv->pm->runtime_suspend(dev) to a separate
> >   function that returns immediately if dle->need_restore is set for the
> >   given device (meaning that drv->pm->runtime_suspend(dev) has already
> >   been called for it and the corresponding ->runtime_resume() hasn't).
> >   This fixes a bug where drv->pm->runtime_suspend() could be called for
> >   a device whose state hasn't been restored after power cycling its
> >   PM domain.
> >
> > * Made pm_genpd_add_device() return error code on an attempt to add a device
> >   do a PM domain whose power_is_off is set (that complemets the previous
> >   modification).
> >
> > * Makde the .power_on() and .power_off() generic PM domain callbacks take
> >   (struct generic_pm_domain *) arguments instead of (struct dev_pm_domain *).
> >
> > Thanks,
> > Rafael
> 
> There's a guiding assumption in this generic PM domain layer that the
> runtime PM callbacks need only be called if power to the underlying PM
> domain is actually being cut.  As a result, devices no longer have a
> callback called for other low-power states where the power may not
> actually be cut (a.k.a low-power with memory & logic retention.)
> 
> However, there are devices (at least on OMAP, but I presume on all SoCs)
> where the driver will need to do other "stuff" for *all* low-power
> transitions, not just the power-off ones (e.g. device specific idle mode
> registers, clearing device-specific events/state that prevent low power
> transitions, etc.)
> 
> Because of this, I don't currently see how to use these generic PM
> domains on devices with multiple power states since the runtime PM
> callbacks are only called for a subset of the power states.
> 
> I haven't given this too much thought yet (especially the system PM
> aspects), but in order for generic PM domains to be more broadly useful
> for runtime PM, I'm starting to think that this series should add
> another set of callbacks: .power_off, .power_on or something similar.
> The .runtime_suspend/.runtime_resume callbacks would then be used for
> all power states and the .power_off/.power_on callbacks used only when
> power is actually cut.

Well, I _really_ would like to avoid adding more callbacks to struct
dev_pm_ops, if that's what you mean, because we already seem to have
problems with managing the existing ones.

Also, IMO, you can always map every system with more power states to the
model where there are only "device active" (runtime PM RPM_ACTIVE) "device
stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
represented here.

If anything more fine grained is necessary or useful, I'd say you need a
more complicated model, but I'd prefer to avoid further complications in this
patchset.

> [...]
> 
> > Index: linux-2.6/drivers/base/power/domain.c
> > ===================================================================
> > --- /dev/null
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -0,0 +1,490 @@
> > +/*
> > + * drivers/base/power/domain.c - Common code related to device power domains.
> > + *
> > + * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
> > +#include <linux/pm_domain.h>
> > +#include <linux/slab.h>
> > +#include <linux/err.h>
> > +
> > +#ifdef CONFIG_PM_RUNTIME
> > +
> > +static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
> > +{
> > +	if (!WARN_ON(genpd->sd_count == 0))
> > +			genpd->sd_count--;
> > +}
> > +
> > +/**
> > + * __pm_genpd_save_device - Save the pre-suspend state of a device.
> > + * @dle: Device list entry of the device to save the state of.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static int __pm_genpd_save_device(struct dev_list_entry *dle,
> > +				  struct generic_pm_domain *genpd)
> > +{
> > +	struct device *dev = dle->dev;
> > +	struct device_driver *drv = dev->driver;
> > +	int ret = 0;
> > +
> > +	if (dle->need_restore)
> > +		return 0;
> > +
> > +	if (genpd->start_device)
> > +		genpd->start_device(dev);
> > +
> > +	if (drv && drv->pm && drv->pm->runtime_suspend)
> 
> The start/stop device calls should probably be included inside this 'if'
> since there's no reason to restart and re-stop the device if there is no
> callback to be run.

That's a good point, I'll do that.

> Some drivers have alterntive ways of saving context (shadow registers,
> manual save/restore per-xfer, etc.) and thus have no callbacks for
> context save/restore.
> 
> > +		ret = drv->pm->runtime_suspend(dev);
> > +
> > +	if (genpd->stop_device)
> > +		genpd->stop_device(dev);
> > +
> > +	if (!ret)
> > +		dle->need_restore = true;
> > +
> > +	return ret;
> > +}
> > +
> > +/**
> > + * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
> > + * @dle: Device list entry of the device to restore the state of.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static void __pm_genpd_restore_device(struct dev_list_entry *dle,
> > +				      struct generic_pm_domain *genpd)
> > +{
> > +	struct device *dev = dle->dev;
> > +	struct device_driver *drv = dev->driver;
> > +
> > +	if (!dle->need_restore)
> > +		return;
> > +
> > +	if (genpd->start_device)
> > +		genpd->start_device(dev);
> > +
> > +	if (drv && drv->pm && drv->pm->runtime_resume)
> 
> Similar to the 'save' case, the start/stop device calls should also be
> included inside this 'if'.

Agreed.

> > +		drv->pm->runtime_resume(dev);
> > +
> > +	if (genpd->stop_device)
> > +		genpd->stop_device(dev);
> > +
> > +	dle->need_restore = false;
> > +}

Thanks for the comments, I'm going to implement your suggestions.

Take care,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-22  0:07         ` Rafael J. Wysocki
@ 2011-06-22 19:51           ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 19:51 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Tuesday, June 21, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:

[...]

>> 
>> There's a guiding assumption in this generic PM domain layer that the
>> runtime PM callbacks need only be called if power to the underlying PM
>> domain is actually being cut.  As a result, devices no longer have a
>> callback called for other low-power states where the power may not
>> actually be cut (a.k.a low-power with memory & logic retention.)
>> 
>> However, there are devices (at least on OMAP, but I presume on all SoCs)
>> where the driver will need to do other "stuff" for *all* low-power
>> transitions, not just the power-off ones (e.g. device specific idle mode
>> registers, clearing device-specific events/state that prevent low power
>> transitions, etc.)
>> 
>> Because of this, I don't currently see how to use these generic PM
>> domains on devices with multiple power states since the runtime PM
>> callbacks are only called for a subset of the power states.
>> 
>> I haven't given this too much thought yet (especially the system PM
>> aspects), but in order for generic PM domains to be more broadly useful
>> for runtime PM, I'm starting to think that this series should add
>> another set of callbacks: .power_off, .power_on or something similar.
>> The .runtime_suspend/.runtime_resume callbacks would then be used for
>> all power states and the .power_off/.power_on callbacks used only when
>> power is actually cut.
>
> Well, I _really_ would like to avoid adding more callbacks to struct
> dev_pm_ops, if that's what you mean, because we already seem to have
> problems with managing the existing ones.

I agree, I don't really want to see more callbacks either.

> Also, IMO, you can always map every system with more power states to the
> model where there are only "device active" (runtime PM RPM_ACTIVE) "device
> stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
> power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
> represented here.

Yes, but note that you list 2 RPM_SUSPENDED states, but there is only
one .runtime_suspend callback, so the driver is only be notified of one
of the them.

More specifically, the problem is that using generic PM domains, there
are no callbacks to the driver for "device stopped", since the driver's
.runtime_suspend() is not called until "device power off."

What I tried to say in my initial reply is that many devices actually
have device specific stuff to do in preparation for "device stopped", or
the hardware will not actually reach the targetted power state.  Without
a callback, no device-specific preparation for "device stopped" can be
done.

thinking out loud: hmm..., the more I think of this, maybe we should
actually be using RPM_IDLE to represent your definition of "device
stopped."

> If anything more fine grained is necessary or useful, I'd say you need
> a more complicated model, but I'd prefer to avoid further
> complications in this patchset.

Unfortunately, PM on embedded devices is very fine grained and very
complicated.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
@ 2011-06-22 19:51           ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 19:51 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Tuesday, June 21, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:

[...]

>> 
>> There's a guiding assumption in this generic PM domain layer that the
>> runtime PM callbacks need only be called if power to the underlying PM
>> domain is actually being cut.  As a result, devices no longer have a
>> callback called for other low-power states where the power may not
>> actually be cut (a.k.a low-power with memory & logic retention.)
>> 
>> However, there are devices (at least on OMAP, but I presume on all SoCs)
>> where the driver will need to do other "stuff" for *all* low-power
>> transitions, not just the power-off ones (e.g. device specific idle mode
>> registers, clearing device-specific events/state that prevent low power
>> transitions, etc.)
>> 
>> Because of this, I don't currently see how to use these generic PM
>> domains on devices with multiple power states since the runtime PM
>> callbacks are only called for a subset of the power states.
>> 
>> I haven't given this too much thought yet (especially the system PM
>> aspects), but in order for generic PM domains to be more broadly useful
>> for runtime PM, I'm starting to think that this series should add
>> another set of callbacks: .power_off, .power_on or something similar.
>> The .runtime_suspend/.runtime_resume callbacks would then be used for
>> all power states and the .power_off/.power_on callbacks used only when
>> power is actually cut.
>
> Well, I _really_ would like to avoid adding more callbacks to struct
> dev_pm_ops, if that's what you mean, because we already seem to have
> problems with managing the existing ones.

I agree, I don't really want to see more callbacks either.

> Also, IMO, you can always map every system with more power states to the
> model where there are only "device active" (runtime PM RPM_ACTIVE) "device
> stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
> power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
> represented here.

Yes, but note that you list 2 RPM_SUSPENDED states, but there is only
one .runtime_suspend callback, so the driver is only be notified of one
of the them.

More specifically, the problem is that using generic PM domains, there
are no callbacks to the driver for "device stopped", since the driver's
.runtime_suspend() is not called until "device power off."

What I tried to say in my initial reply is that many devices actually
have device specific stuff to do in preparation for "device stopped", or
the hardware will not actually reach the targetted power state.  Without
a callback, no device-specific preparation for "device stopped" can be
done.

thinking out loud: hmm..., the more I think of this, maybe we should
actually be using RPM_IDLE to represent your definition of "device
stopped."

> If anything more fine grained is necessary or useful, I'd say you need
> a more complicated model, but I'd prefer to avoid further
> complications in this patchset.

Unfortunately, PM on embedded devices is very fine grained and very
complicated.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-22  0:07         ` Rafael J. Wysocki
  (?)
@ 2011-06-22 19:51         ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 19:51 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Tuesday, June 21, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:

[...]

>> 
>> There's a guiding assumption in this generic PM domain layer that the
>> runtime PM callbacks need only be called if power to the underlying PM
>> domain is actually being cut.  As a result, devices no longer have a
>> callback called for other low-power states where the power may not
>> actually be cut (a.k.a low-power with memory & logic retention.)
>> 
>> However, there are devices (at least on OMAP, but I presume on all SoCs)
>> where the driver will need to do other "stuff" for *all* low-power
>> transitions, not just the power-off ones (e.g. device specific idle mode
>> registers, clearing device-specific events/state that prevent low power
>> transitions, etc.)
>> 
>> Because of this, I don't currently see how to use these generic PM
>> domains on devices with multiple power states since the runtime PM
>> callbacks are only called for a subset of the power states.
>> 
>> I haven't given this too much thought yet (especially the system PM
>> aspects), but in order for generic PM domains to be more broadly useful
>> for runtime PM, I'm starting to think that this series should add
>> another set of callbacks: .power_off, .power_on or something similar.
>> The .runtime_suspend/.runtime_resume callbacks would then be used for
>> all power states and the .power_off/.power_on callbacks used only when
>> power is actually cut.
>
> Well, I _really_ would like to avoid adding more callbacks to struct
> dev_pm_ops, if that's what you mean, because we already seem to have
> problems with managing the existing ones.

I agree, I don't really want to see more callbacks either.

> Also, IMO, you can always map every system with more power states to the
> model where there are only "device active" (runtime PM RPM_ACTIVE) "device
> stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
> power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
> represented here.

Yes, but note that you list 2 RPM_SUSPENDED states, but there is only
one .runtime_suspend callback, so the driver is only be notified of one
of the them.

More specifically, the problem is that using generic PM domains, there
are no callbacks to the driver for "device stopped", since the driver's
.runtime_suspend() is not called until "device power off."

What I tried to say in my initial reply is that many devices actually
have device specific stuff to do in preparation for "device stopped", or
the hardware will not actually reach the targetted power state.  Without
a callback, no device-specific preparation for "device stopped" can be
done.

thinking out loud: hmm..., the more I think of this, maybe we should
actually be using RPM_IDLE to represent your definition of "device
stopped."

> If anything more fine grained is necessary or useful, I'd say you need
> a more complicated model, but I'd prefer to avoid further
> complications in this patchset.

Unfortunately, PM on embedded devices is very fine grained and very
complicated.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-22 19:51           ` Kevin Hilman
@ 2011-06-22 21:30             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 21:30 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Wednesday, June 22, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Tuesday, June 21, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> [...]
> 
> >> 
> >> There's a guiding assumption in this generic PM domain layer that the
> >> runtime PM callbacks need only be called if power to the underlying PM
> >> domain is actually being cut.  As a result, devices no longer have a
> >> callback called for other low-power states where the power may not
> >> actually be cut (a.k.a low-power with memory & logic retention.)
> >> 
> >> However, there are devices (at least on OMAP, but I presume on all SoCs)
> >> where the driver will need to do other "stuff" for *all* low-power
> >> transitions, not just the power-off ones (e.g. device specific idle mode
> >> registers, clearing device-specific events/state that prevent low power
> >> transitions, etc.)
> >> 
> >> Because of this, I don't currently see how to use these generic PM
> >> domains on devices with multiple power states since the runtime PM
> >> callbacks are only called for a subset of the power states.
> >> 
> >> I haven't given this too much thought yet (especially the system PM
> >> aspects), but in order for generic PM domains to be more broadly useful
> >> for runtime PM, I'm starting to think that this series should add
> >> another set of callbacks: .power_off, .power_on or something similar.
> >> The .runtime_suspend/.runtime_resume callbacks would then be used for
> >> all power states and the .power_off/.power_on callbacks used only when
> >> power is actually cut.
> >
> > Well, I _really_ would like to avoid adding more callbacks to struct
> > dev_pm_ops, if that's what you mean, because we already seem to have
> > problems with managing the existing ones.
> 
> I agree, I don't really want to see more callbacks either.
> 
> > Also, IMO, you can always map every system with more power states to the
> > model where there are only "device active" (runtime PM RPM_ACTIVE) "device
> > stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
> > power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
> > represented here.
> 
> Yes, but note that you list 2 RPM_SUSPENDED states, but there is only
> one .runtime_suspend callback, so the driver is only be notified of one
> of the them.

That's correct.

> More specifically, the problem is that using generic PM domains, there
> are no callbacks to the driver for "device stopped", since the driver's
> .runtime_suspend() is not called until "device power off."

Yes, it is, because that is not necessary for the first user (the shmobile
domain added by [8/8]).  However, I have a plan to add such a mechanism
using the subsys_data field from struct dev_pm_info (it's only used for
the clocks management right now, but it's going to see more usage anyway :-)).

> What I tried to say in my initial reply is that many devices actually
> have device specific stuff to do in preparation for "device stopped", or
> the hardware will not actually reach the targetted power state.  Without
> a callback, no device-specific preparation for "device stopped" can be
> done.
> 
> thinking out loud: hmm..., the more I think of this, maybe we should
> actually be using RPM_IDLE to represent your definition of "device
> stopped."

I don't think that will work as expected.

> > If anything more fine grained is necessary or useful, I'd say you need
> > a more complicated model, but I'd prefer to avoid further
> > complications in this patchset.
> 
> Unfortunately, PM on embedded devices is very fine grained and very
> complicated.

Which hopefully doesn't mean the code has to cover all of the possible
complications from the start. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
@ 2011-06-22 21:30             ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 21:30 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Wednesday, June 22, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Tuesday, June 21, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> [...]
> 
> >> 
> >> There's a guiding assumption in this generic PM domain layer that the
> >> runtime PM callbacks need only be called if power to the underlying PM
> >> domain is actually being cut.  As a result, devices no longer have a
> >> callback called for other low-power states where the power may not
> >> actually be cut (a.k.a low-power with memory & logic retention.)
> >> 
> >> However, there are devices (at least on OMAP, but I presume on all SoCs)
> >> where the driver will need to do other "stuff" for *all* low-power
> >> transitions, not just the power-off ones (e.g. device specific idle mode
> >> registers, clearing device-specific events/state that prevent low power
> >> transitions, etc.)
> >> 
> >> Because of this, I don't currently see how to use these generic PM
> >> domains on devices with multiple power states since the runtime PM
> >> callbacks are only called for a subset of the power states.
> >> 
> >> I haven't given this too much thought yet (especially the system PM
> >> aspects), but in order for generic PM domains to be more broadly useful
> >> for runtime PM, I'm starting to think that this series should add
> >> another set of callbacks: .power_off, .power_on or something similar.
> >> The .runtime_suspend/.runtime_resume callbacks would then be used for
> >> all power states and the .power_off/.power_on callbacks used only when
> >> power is actually cut.
> >
> > Well, I _really_ would like to avoid adding more callbacks to struct
> > dev_pm_ops, if that's what you mean, because we already seem to have
> > problems with managing the existing ones.
> 
> I agree, I don't really want to see more callbacks either.
> 
> > Also, IMO, you can always map every system with more power states to the
> > model where there are only "device active" (runtime PM RPM_ACTIVE) "device
> > stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
> > power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
> > represented here.
> 
> Yes, but note that you list 2 RPM_SUSPENDED states, but there is only
> one .runtime_suspend callback, so the driver is only be notified of one
> of the them.

That's correct.

> More specifically, the problem is that using generic PM domains, there
> are no callbacks to the driver for "device stopped", since the driver's
> .runtime_suspend() is not called until "device power off."

Yes, it is, because that is not necessary for the first user (the shmobile
domain added by [8/8]).  However, I have a plan to add such a mechanism
using the subsys_data field from struct dev_pm_info (it's only used for
the clocks management right now, but it's going to see more usage anyway :-)).

> What I tried to say in my initial reply is that many devices actually
> have device specific stuff to do in preparation for "device stopped", or
> the hardware will not actually reach the targetted power state.  Without
> a callback, no device-specific preparation for "device stopped" can be
> done.
> 
> thinking out loud: hmm..., the more I think of this, maybe we should
> actually be using RPM_IDLE to represent your definition of "device
> stopped."

I don't think that will work as expected.

> > If anything more fine grained is necessary or useful, I'd say you need
> > a more complicated model, but I'd prefer to avoid further
> > complications in this patchset.
> 
> Unfortunately, PM on embedded devices is very fine grained and very
> complicated.

Which hopefully doesn't mean the code has to cover all of the possible
complications from the start. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6)
  2011-06-22 19:51           ` Kevin Hilman
  (?)
@ 2011-06-22 21:30           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 21:30 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Wednesday, June 22, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Tuesday, June 21, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> [...]
> 
> >> 
> >> There's a guiding assumption in this generic PM domain layer that the
> >> runtime PM callbacks need only be called if power to the underlying PM
> >> domain is actually being cut.  As a result, devices no longer have a
> >> callback called for other low-power states where the power may not
> >> actually be cut (a.k.a low-power with memory & logic retention.)
> >> 
> >> However, there are devices (at least on OMAP, but I presume on all SoCs)
> >> where the driver will need to do other "stuff" for *all* low-power
> >> transitions, not just the power-off ones (e.g. device specific idle mode
> >> registers, clearing device-specific events/state that prevent low power
> >> transitions, etc.)
> >> 
> >> Because of this, I don't currently see how to use these generic PM
> >> domains on devices with multiple power states since the runtime PM
> >> callbacks are only called for a subset of the power states.
> >> 
> >> I haven't given this too much thought yet (especially the system PM
> >> aspects), but in order for generic PM domains to be more broadly useful
> >> for runtime PM, I'm starting to think that this series should add
> >> another set of callbacks: .power_off, .power_on or something similar.
> >> The .runtime_suspend/.runtime_resume callbacks would then be used for
> >> all power states and the .power_off/.power_on callbacks used only when
> >> power is actually cut.
> >
> > Well, I _really_ would like to avoid adding more callbacks to struct
> > dev_pm_ops, if that's what you mean, because we already seem to have
> > problems with managing the existing ones.
> 
> I agree, I don't really want to see more callbacks either.
> 
> > Also, IMO, you can always map every system with more power states to the
> > model where there are only "device active" (runtime PM RPM_ACTIVE) "device
> > stopped" (runtime PM RPM_SUSPENDED, need not save state) and "device
> > power off" (runtime PM RPM_SUSPENDED, must save state) "software" states
> > represented here.
> 
> Yes, but note that you list 2 RPM_SUSPENDED states, but there is only
> one .runtime_suspend callback, so the driver is only be notified of one
> of the them.

That's correct.

> More specifically, the problem is that using generic PM domains, there
> are no callbacks to the driver for "device stopped", since the driver's
> .runtime_suspend() is not called until "device power off."

Yes, it is, because that is not necessary for the first user (the shmobile
domain added by [8/8]).  However, I have a plan to add such a mechanism
using the subsys_data field from struct dev_pm_info (it's only used for
the clocks management right now, but it's going to see more usage anyway :-)).

> What I tried to say in my initial reply is that many devices actually
> have device specific stuff to do in preparation for "device stopped", or
> the hardware will not actually reach the targetted power state.  Without
> a callback, no device-specific preparation for "device stopped" can be
> done.
> 
> thinking out loud: hmm..., the more I think of this, maybe we should
> actually be using RPM_IDLE to represent your definition of "device
> stopped."

I don't think that will work as expected.

> > If anything more fine grained is necessary or useful, I'd say you need
> > a more complicated model, but I'd prefer to avoid further
> > complications in this patchset.
> 
> Unfortunately, PM on embedded devices is very fine grained and very
> complicated.

Which hopefully doesn't mean the code has to cover all of the possible
complications from the start. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-19 22:06     ` Rafael J. Wysocki
@ 2011-06-22 21:50       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 21:50 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
>
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  

Great, this is the approach I prefer too, but...

Now I'm confused.  Leaving runtime suspended devices alone is what I was
doing in my subsystem but was told not to.  According to

    http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html

"it's generally agreed that _all_ devices should return to full 
power during system resume -- even if they were runtime suspended 
before the system sleep."

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-22 21:50       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 21:50 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
>
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  

Great, this is the approach I prefer too, but...

Now I'm confused.  Leaving runtime suspended devices alone is what I was
doing in my subsystem but was told not to.  According to

    http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html

"it's generally agreed that _all_ devices should return to full 
power during system resume -- even if they were runtime suspended 
before the system sleep."

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-19 22:06     ` Rafael J. Wysocki
                       ` (2 preceding siblings ...)
  (?)
@ 2011-06-22 21:50     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 21:50 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
>
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  

Great, this is the approach I prefer too, but...

Now I'm confused.  Leaving runtime suspended devices alone is what I was
doing in my subsystem but was told not to.  According to

    http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html

"it's generally agreed that _all_ devices should return to full 
power during system resume -- even if they were runtime suspended 
before the system sleep."

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 21:50       ` Kevin Hilman
@ 2011-06-22 22:16         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 22:16 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Wednesday, June 22, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Make generic PM domains support system-wide power transitions
> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > poweroff and restore callbacks to be associated with struct
> > generic_pm_domain objects and make pm_genpd_init() use them as
> > appropriate.
> >
> > The new callbacks do nothing for devices belonging to power domains
> > that were powered down at run time (before the transition).  
> 
> Great, this is the approach I prefer too, but...
> 
> Now I'm confused.  Leaving runtime suspended devices alone is what I was
> doing in my subsystem but was told not to.  According to
> 
>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
> 
> "it's generally agreed that _all_ devices should return to full 
> power during system resume -- even if they were runtime suspended 
> before the system sleep."

Well, let's say this part of the documentation is slightly outdated.

It basically refers to the model in which system suspend is a separate global
hardware or firmware operation, so the state of devices may be changed by the
BIOS or whatever takes over control in the meantime.  In that case the kernel
has to ensure that the states of devices are consistent with what it thinks
about them and the simplest way to achieve that is to put the devices to
full power during resume (and back to low power if that's desirable).

However, in the case of the systems this patchset is intended for system
suspend is achieved by putting various hardware components into low-power
states directly in a coordinated way and the system sleep state effectively
follows from the low-power states the hardware components end up in.  The
system is woken up from this state by an interrupt or another mechanism under
the kernel's control.  As a result, the kernel never gives control away, so
the state of devices after the resume is precisely known to it.
In consequence, it need not ensure that the state of devices is consistent with
its view, because it knows that this is the case. :-)

So the documentation should be updated to say what hardware model it is
referring to.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-22 22:16         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 22:16 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Wednesday, June 22, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Make generic PM domains support system-wide power transitions
> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > poweroff and restore callbacks to be associated with struct
> > generic_pm_domain objects and make pm_genpd_init() use them as
> > appropriate.
> >
> > The new callbacks do nothing for devices belonging to power domains
> > that were powered down at run time (before the transition).  
> 
> Great, this is the approach I prefer too, but...
> 
> Now I'm confused.  Leaving runtime suspended devices alone is what I was
> doing in my subsystem but was told not to.  According to
> 
>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
> 
> "it's generally agreed that _all_ devices should return to full 
> power during system resume -- even if they were runtime suspended 
> before the system sleep."

Well, let's say this part of the documentation is slightly outdated.

It basically refers to the model in which system suspend is a separate global
hardware or firmware operation, so the state of devices may be changed by the
BIOS or whatever takes over control in the meantime.  In that case the kernel
has to ensure that the states of devices are consistent with what it thinks
about them and the simplest way to achieve that is to put the devices to
full power during resume (and back to low power if that's desirable).

However, in the case of the systems this patchset is intended for system
suspend is achieved by putting various hardware components into low-power
states directly in a coordinated way and the system sleep state effectively
follows from the low-power states the hardware components end up in.  The
system is woken up from this state by an interrupt or another mechanism under
the kernel's control.  As a result, the kernel never gives control away, so
the state of devices after the resume is precisely known to it.
In consequence, it need not ensure that the state of devices is consistent with
its view, because it knows that this is the case. :-)

So the documentation should be updated to say what hardware model it is
referring to.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 21:50       ` Kevin Hilman
  (?)
@ 2011-06-22 22:16       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 22:16 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Wednesday, June 22, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Make generic PM domains support system-wide power transitions
> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > poweroff and restore callbacks to be associated with struct
> > generic_pm_domain objects and make pm_genpd_init() use them as
> > appropriate.
> >
> > The new callbacks do nothing for devices belonging to power domains
> > that were powered down at run time (before the transition).  
> 
> Great, this is the approach I prefer too, but...
> 
> Now I'm confused.  Leaving runtime suspended devices alone is what I was
> doing in my subsystem but was told not to.  According to
> 
>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
> 
> "it's generally agreed that _all_ devices should return to full 
> power during system resume -- even if they were runtime suspended 
> before the system sleep."

Well, let's say this part of the documentation is slightly outdated.

It basically refers to the model in which system suspend is a separate global
hardware or firmware operation, so the state of devices may be changed by the
BIOS or whatever takes over control in the meantime.  In that case the kernel
has to ensure that the states of devices are consistent with what it thinks
about them and the simplest way to achieve that is to put the devices to
full power during resume (and back to low power if that's desirable).

However, in the case of the systems this patchset is intended for system
suspend is achieved by putting various hardware components into low-power
states directly in a coordinated way and the system sleep state effectively
follows from the low-power states the hardware components end up in.  The
system is woken up from this state by an interrupt or another mechanism under
the kernel's control.  As a result, the kernel never gives control away, so
the state of devices after the resume is precisely known to it.
In consequence, it need not ensure that the state of devices is consistent with
its view, because it knows that this is the case. :-)

So the documentation should be updated to say what hardware model it is
referring to.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 22:16         ` Rafael J. Wysocki
@ 2011-06-22 22:18           ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 22:18 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Wednesday, June 22, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Make generic PM domains support system-wide power transitions
>> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
>> > poweroff and restore callbacks to be associated with struct
>> > generic_pm_domain objects and make pm_genpd_init() use them as
>> > appropriate.
>> >
>> > The new callbacks do nothing for devices belonging to power domains
>> > that were powered down at run time (before the transition).  
>> 
>> Great, this is the approach I prefer too, but...
>> 
>> Now I'm confused.  Leaving runtime suspended devices alone is what I was
>> doing in my subsystem but was told not to.  According to
>> 
>>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
>> 
>> "it's generally agreed that _all_ devices should return to full 
>> power during system resume -- even if they were runtime suspended 
>> before the system sleep."
>
> Well, let's say this part of the documentation is slightly outdated.
>
> It basically refers to the model in which system suspend is a separate global
> hardware or firmware operation, so the state of devices may be changed by the
> BIOS or whatever takes over control in the meantime.  In that case the kernel
> has to ensure that the states of devices are consistent with what it thinks
> about them and the simplest way to achieve that is to put the devices to
> full power during resume (and back to low power if that's desirable).
>
> However, in the case of the systems this patchset is intended for system
> suspend is achieved by putting various hardware components into low-power
> states directly in a coordinated way and the system sleep state effectively
> follows from the low-power states the hardware components end up in.  The
> system is woken up from this state by an interrupt or another mechanism under
> the kernel's control.  As a result, the kernel never gives control away, so
> the state of devices after the resume is precisely known to it.
> In consequence, it need not ensure that the state of devices is consistent with
> its view, because it knows that this is the case. :-)
>
> So the documentation should be updated to say what hardware model it is
> referring to.

Great!   Thanks for the clarification.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-22 22:18           ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 22:18 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Wednesday, June 22, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Make generic PM domains support system-wide power transitions
>> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
>> > poweroff and restore callbacks to be associated with struct
>> > generic_pm_domain objects and make pm_genpd_init() use them as
>> > appropriate.
>> >
>> > The new callbacks do nothing for devices belonging to power domains
>> > that were powered down at run time (before the transition).  
>> 
>> Great, this is the approach I prefer too, but...
>> 
>> Now I'm confused.  Leaving runtime suspended devices alone is what I was
>> doing in my subsystem but was told not to.  According to
>> 
>>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
>> 
>> "it's generally agreed that _all_ devices should return to full 
>> power during system resume -- even if they were runtime suspended 
>> before the system sleep."
>
> Well, let's say this part of the documentation is slightly outdated.
>
> It basically refers to the model in which system suspend is a separate global
> hardware or firmware operation, so the state of devices may be changed by the
> BIOS or whatever takes over control in the meantime.  In that case the kernel
> has to ensure that the states of devices are consistent with what it thinks
> about them and the simplest way to achieve that is to put the devices to
> full power during resume (and back to low power if that's desirable).
>
> However, in the case of the systems this patchset is intended for system
> suspend is achieved by putting various hardware components into low-power
> states directly in a coordinated way and the system sleep state effectively
> follows from the low-power states the hardware components end up in.  The
> system is woken up from this state by an interrupt or another mechanism under
> the kernel's control.  As a result, the kernel never gives control away, so
> the state of devices after the resume is precisely known to it.
> In consequence, it need not ensure that the state of devices is consistent with
> its view, because it knows that this is the case. :-)
>
> So the documentation should be updated to say what hardware model it is
> referring to.

Great!   Thanks for the clarification.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 22:16         ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-22 22:18         ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-22 22:18 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Wednesday, June 22, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Make generic PM domains support system-wide power transitions
>> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
>> > poweroff and restore callbacks to be associated with struct
>> > generic_pm_domain objects and make pm_genpd_init() use them as
>> > appropriate.
>> >
>> > The new callbacks do nothing for devices belonging to power domains
>> > that were powered down at run time (before the transition).  
>> 
>> Great, this is the approach I prefer too, but...
>> 
>> Now I'm confused.  Leaving runtime suspended devices alone is what I was
>> doing in my subsystem but was told not to.  According to
>> 
>>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
>> 
>> "it's generally agreed that _all_ devices should return to full 
>> power during system resume -- even if they were runtime suspended 
>> before the system sleep."
>
> Well, let's say this part of the documentation is slightly outdated.
>
> It basically refers to the model in which system suspend is a separate global
> hardware or firmware operation, so the state of devices may be changed by the
> BIOS or whatever takes over control in the meantime.  In that case the kernel
> has to ensure that the states of devices are consistent with what it thinks
> about them and the simplest way to achieve that is to put the devices to
> full power during resume (and back to low power if that's desirable).
>
> However, in the case of the systems this patchset is intended for system
> suspend is achieved by putting various hardware components into low-power
> states directly in a coordinated way and the system sleep state effectively
> follows from the low-power states the hardware components end up in.  The
> system is woken up from this state by an interrupt or another mechanism under
> the kernel's control.  As a result, the kernel never gives control away, so
> the state of devices after the resume is precisely known to it.
> In consequence, it need not ensure that the state of devices is consistent with
> its view, because it knows that this is the case. :-)
>
> So the documentation should be updated to say what hardware model it is
> referring to.

Great!   Thanks for the clarification.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 22:18           ` Kevin Hilman
@ 2011-06-22 22:22             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 22:22 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Thursday, June 23, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Wednesday, June 22, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Make generic PM domains support system-wide power transitions
> >> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> >> > poweroff and restore callbacks to be associated with struct
> >> > generic_pm_domain objects and make pm_genpd_init() use them as
> >> > appropriate.
> >> >
> >> > The new callbacks do nothing for devices belonging to power domains
> >> > that were powered down at run time (before the transition).  
> >> 
> >> Great, this is the approach I prefer too, but...
> >> 
> >> Now I'm confused.  Leaving runtime suspended devices alone is what I was
> >> doing in my subsystem but was told not to.  According to
> >> 
> >>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
> >> 
> >> "it's generally agreed that _all_ devices should return to full 
> >> power during system resume -- even if they were runtime suspended 
> >> before the system sleep."
> >
> > Well, let's say this part of the documentation is slightly outdated.
> >
> > It basically refers to the model in which system suspend is a separate global
> > hardware or firmware operation, so the state of devices may be changed by the
> > BIOS or whatever takes over control in the meantime.  In that case the kernel
> > has to ensure that the states of devices are consistent with what it thinks
> > about them and the simplest way to achieve that is to put the devices to
> > full power during resume (and back to low power if that's desirable).
> >
> > However, in the case of the systems this patchset is intended for system
> > suspend is achieved by putting various hardware components into low-power
> > states directly in a coordinated way and the system sleep state effectively
> > follows from the low-power states the hardware components end up in.  The
> > system is woken up from this state by an interrupt or another mechanism under
> > the kernel's control.  As a result, the kernel never gives control away, so
> > the state of devices after the resume is precisely known to it.
> > In consequence, it need not ensure that the state of devices is consistent with
> > its view, because it knows that this is the case. :-)
> >
> > So the documentation should be updated to say what hardware model it is
> > referring to.
> 
> Great!   Thanks for the clarification.

No problem, I guess I should update the documentation eventually.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-22 22:22             ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 22:22 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

On Thursday, June 23, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Wednesday, June 22, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Make generic PM domains support system-wide power transitions
> >> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> >> > poweroff and restore callbacks to be associated with struct
> >> > generic_pm_domain objects and make pm_genpd_init() use them as
> >> > appropriate.
> >> >
> >> > The new callbacks do nothing for devices belonging to power domains
> >> > that were powered down at run time (before the transition).  
> >> 
> >> Great, this is the approach I prefer too, but...
> >> 
> >> Now I'm confused.  Leaving runtime suspended devices alone is what I was
> >> doing in my subsystem but was told not to.  According to
> >> 
> >>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
> >> 
> >> "it's generally agreed that _all_ devices should return to full 
> >> power during system resume -- even if they were runtime suspended 
> >> before the system sleep."
> >
> > Well, let's say this part of the documentation is slightly outdated.
> >
> > It basically refers to the model in which system suspend is a separate global
> > hardware or firmware operation, so the state of devices may be changed by the
> > BIOS or whatever takes over control in the meantime.  In that case the kernel
> > has to ensure that the states of devices are consistent with what it thinks
> > about them and the simplest way to achieve that is to put the devices to
> > full power during resume (and back to low power if that's desirable).
> >
> > However, in the case of the systems this patchset is intended for system
> > suspend is achieved by putting various hardware components into low-power
> > states directly in a coordinated way and the system sleep state effectively
> > follows from the low-power states the hardware components end up in.  The
> > system is woken up from this state by an interrupt or another mechanism under
> > the kernel's control.  As a result, the kernel never gives control away, so
> > the state of devices after the resume is precisely known to it.
> > In consequence, it need not ensure that the state of devices is consistent with
> > its view, because it knows that this is the case. :-)
> >
> > So the documentation should be updated to say what hardware model it is
> > referring to.
> 
> Great!   Thanks for the clarification.

No problem, I guess I should update the documentation eventually.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 22:18           ` Kevin Hilman
  (?)
  (?)
@ 2011-06-22 22:22           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-22 22:22 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thursday, June 23, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Wednesday, June 22, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Make generic PM domains support system-wide power transitions
> >> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> >> > poweroff and restore callbacks to be associated with struct
> >> > generic_pm_domain objects and make pm_genpd_init() use them as
> >> > appropriate.
> >> >
> >> > The new callbacks do nothing for devices belonging to power domains
> >> > that were powered down at run time (before the transition).  
> >> 
> >> Great, this is the approach I prefer too, but...
> >> 
> >> Now I'm confused.  Leaving runtime suspended devices alone is what I was
> >> doing in my subsystem but was told not to.  According to
> >> 
> >>     http://www.mail-archive.com/linux-omap@vger.kernel.org/msg50690.html
> >> 
> >> "it's generally agreed that _all_ devices should return to full 
> >> power during system resume -- even if they were runtime suspended 
> >> before the system sleep."
> >
> > Well, let's say this part of the documentation is slightly outdated.
> >
> > It basically refers to the model in which system suspend is a separate global
> > hardware or firmware operation, so the state of devices may be changed by the
> > BIOS or whatever takes over control in the meantime.  In that case the kernel
> > has to ensure that the states of devices are consistent with what it thinks
> > about them and the simplest way to achieve that is to put the devices to
> > full power during resume (and back to low power if that's desirable).
> >
> > However, in the case of the systems this patchset is intended for system
> > suspend is achieved by putting various hardware components into low-power
> > states directly in a coordinated way and the system sleep state effectively
> > follows from the low-power states the hardware components end up in.  The
> > system is woken up from this state by an interrupt or another mechanism under
> > the kernel's control.  As a result, the kernel never gives control away, so
> > the state of devices after the resume is precisely known to it.
> > In consequence, it need not ensure that the state of devices is consistent with
> > its view, because it knows that this is the case. :-)
> >
> > So the documentation should be updated to say what hardware model it is
> > referring to.
> 
> Great!   Thanks for the clarification.

No problem, I guess I should update the documentation eventually.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH] PM / Runtime: Update documentation of interactions with system sleep
  2011-06-22 22:22             ` Rafael J. Wysocki
@ 2011-06-23 13:57               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 13:57 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

The documents describing the interactions between runtime PM and
system sleep generally refer to the model in which the system sleep
state is entered through a global firmware or hardware operation.
As a result, some recommendations given in there are not entirely
suitable for systems in which this is not the case.  Update the
documentation take the existence of those systems into accout.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt    |    6 +++---
 Documentation/power/runtime_pm.txt |   25 +++++++++++++++++--------
 2 files changed, 20 insertions(+), 11 deletions(-)

Index: linux-2.6/Documentation/power/runtime_pm.txt
=================================--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -537,9 +537,9 @@ suspend routine).  It may be necessary t
 in order to do so.  The same is true if the driver uses different power levels
 or other settings for run-time suspend and system sleep.
 
-During system resume, devices generally should be brought back to full power,
-even if they were suspended before the system sleep began.  There are several
-reasons for this, including:
+During system resume, the simplest approach is to bring all devices back to full
+power, even if they were suspended before the system sleep began.  There are
+several reasons for this, including:
 
   * The device might need to switch power levels, wake-up settings, etc.
 
@@ -556,16 +556,25 @@ reasons for this, including:
   * Even though the device was suspended, if its usage counter was > 0 then most
     likely it would need a run-time resume in the near future anyway.
 
-  * Always going back to full power is simplest.
-
-If the device was suspended before the sleep began, then its run-time PM status
-will have to be updated to reflect the actual post-system sleep status.  The way
-to do this is:
+If the device had been suspended before the sleep began and it's brought back to
+full power during resume, then its run-time PM status will have to be updated to
+reflect the actual post-system sleep status.  The way to do this is:
 
 	pm_runtime_disable(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
 
+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.  If that is the case and none of the situations listed above takes
+place, it may be more efficient to leave the devices that had been suspended
+before the system sleep began in the suspended state.
+
 7. Generic subsystem callbacks
 
 Subsystems may wish to conserve code space by using the set of generic power
Index: linux-2.6/Documentation/power/devices.txt
=================================--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -604,7 +604,7 @@ state temporarily, for example so that i
 disabled.  This all depends on the hardware and the design of the subsystem and
 device driver in question.
 
-During system-wide resume from a sleep state it's best to put devices into the
-full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer to
-that document for more information regarding this particular issue as well as
+During system-wide resume from a sleep state it's easiest to put devices into
+the full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer
+to that document for more information regarding this particular issue as well as
 for information on the device runtime power management framework in general.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH] PM / Runtime: Update documentation of interactions with system sleep
@ 2011-06-23 13:57               ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 13:57 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

From: Rafael J. Wysocki <rjw@sisk.pl>

The documents describing the interactions between runtime PM and
system sleep generally refer to the model in which the system sleep
state is entered through a global firmware or hardware operation.
As a result, some recommendations given in there are not entirely
suitable for systems in which this is not the case.  Update the
documentation take the existence of those systems into accout.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt    |    6 +++---
 Documentation/power/runtime_pm.txt |   25 +++++++++++++++++--------
 2 files changed, 20 insertions(+), 11 deletions(-)

Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -537,9 +537,9 @@ suspend routine).  It may be necessary t
 in order to do so.  The same is true if the driver uses different power levels
 or other settings for run-time suspend and system sleep.
 
-During system resume, devices generally should be brought back to full power,
-even if they were suspended before the system sleep began.  There are several
-reasons for this, including:
+During system resume, the simplest approach is to bring all devices back to full
+power, even if they were suspended before the system sleep began.  There are
+several reasons for this, including:
 
   * The device might need to switch power levels, wake-up settings, etc.
 
@@ -556,16 +556,25 @@ reasons for this, including:
   * Even though the device was suspended, if its usage counter was > 0 then most
     likely it would need a run-time resume in the near future anyway.
 
-  * Always going back to full power is simplest.
-
-If the device was suspended before the sleep began, then its run-time PM status
-will have to be updated to reflect the actual post-system sleep status.  The way
-to do this is:
+If the device had been suspended before the sleep began and it's brought back to
+full power during resume, then its run-time PM status will have to be updated to
+reflect the actual post-system sleep status.  The way to do this is:
 
 	pm_runtime_disable(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
 
+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.  If that is the case and none of the situations listed above takes
+place, it may be more efficient to leave the devices that had been suspended
+before the system sleep began in the suspended state.
+
 7. Generic subsystem callbacks
 
 Subsystems may wish to conserve code space by using the set of generic power
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -604,7 +604,7 @@ state temporarily, for example so that i
 disabled.  This all depends on the hardware and the design of the subsystem and
 device driver in question.
 
-During system-wide resume from a sleep state it's best to put devices into the
-full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer to
-that document for more information regarding this particular issue as well as
+During system-wide resume from a sleep state it's easiest to put devices into
+the full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer
+to that document for more information regarding this particular issue as well as
 for information on the device runtime power management framework in general.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH] PM / Runtime: Update documentation of interactions with system sleep
  2011-06-22 22:22             ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-23 13:57             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 13:57 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

From: Rafael J. Wysocki <rjw@sisk.pl>

The documents describing the interactions between runtime PM and
system sleep generally refer to the model in which the system sleep
state is entered through a global firmware or hardware operation.
As a result, some recommendations given in there are not entirely
suitable for systems in which this is not the case.  Update the
documentation take the existence of those systems into accout.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt    |    6 +++---
 Documentation/power/runtime_pm.txt |   25 +++++++++++++++++--------
 2 files changed, 20 insertions(+), 11 deletions(-)

Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -537,9 +537,9 @@ suspend routine).  It may be necessary t
 in order to do so.  The same is true if the driver uses different power levels
 or other settings for run-time suspend and system sleep.
 
-During system resume, devices generally should be brought back to full power,
-even if they were suspended before the system sleep began.  There are several
-reasons for this, including:
+During system resume, the simplest approach is to bring all devices back to full
+power, even if they were suspended before the system sleep began.  There are
+several reasons for this, including:
 
   * The device might need to switch power levels, wake-up settings, etc.
 
@@ -556,16 +556,25 @@ reasons for this, including:
   * Even though the device was suspended, if its usage counter was > 0 then most
     likely it would need a run-time resume in the near future anyway.
 
-  * Always going back to full power is simplest.
-
-If the device was suspended before the sleep began, then its run-time PM status
-will have to be updated to reflect the actual post-system sleep status.  The way
-to do this is:
+If the device had been suspended before the sleep began and it's brought back to
+full power during resume, then its run-time PM status will have to be updated to
+reflect the actual post-system sleep status.  The way to do this is:
 
 	pm_runtime_disable(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
 
+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.  If that is the case and none of the situations listed above takes
+place, it may be more efficient to leave the devices that had been suspended
+before the system sleep began in the suspended state.
+
 7. Generic subsystem callbacks
 
 Subsystems may wish to conserve code space by using the set of generic power
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -604,7 +604,7 @@ state temporarily, for example so that i
 disabled.  This all depends on the hardware and the design of the subsystem and
 device driver in question.
 
-During system-wide resume from a sleep state it's best to put devices into the
-full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer to
-that document for more information regarding this particular issue as well as
+During system-wide resume from a sleep state it's easiest to put devices into
+the full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer
+to that document for more information regarding this particular issue as well as
 for information on the device runtime power management framework in general.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support
  2011-06-22 22:16         ` Rafael J. Wysocki
@ 2011-06-23 14:19           ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 14:19 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> Well, let's say this part of the documentation is slightly outdated.
> 
> It basically refers to the model in which system suspend is a separate global
> hardware or firmware operation, so the state of devices may be changed by the
> BIOS or whatever takes over control in the meantime.  In that case the kernel
> has to ensure that the states of devices are consistent with what it thinks
> about them and the simplest way to achieve that is to put the devices to
> full power during resume (and back to low power if that's desirable).
> 
> However, in the case of the systems this patchset is intended for system
> suspend is achieved by putting various hardware components into low-power
> states directly in a coordinated way and the system sleep state effectively
> follows from the low-power states the hardware components end up in.  The
> system is woken up from this state by an interrupt or another mechanism under
> the kernel's control.  As a result, the kernel never gives control away, so
> the state of devices after the resume is precisely known to it.
> In consequence, it need not ensure that the state of devices is consistent with
> its view, because it knows that this is the case. :-)

That's true for system suspend, but it's probably not true for
hibernation, even in embedded systems.  Of course, many embedded
systems don't use hibernation at all -- but those that do should be
aware of this issue.

> So the documentation should be updated to say what hardware model it is
> referring to.

It might be worthwhile to include a little warning about the difference 
between suspend and hibernate.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-23 14:19           ` Alan Stern
  0 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 14:19 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> Well, let's say this part of the documentation is slightly outdated.
> 
> It basically refers to the model in which system suspend is a separate global
> hardware or firmware operation, so the state of devices may be changed by the
> BIOS or whatever takes over control in the meantime.  In that case the kernel
> has to ensure that the states of devices are consistent with what it thinks
> about them and the simplest way to achieve that is to put the devices to
> full power during resume (and back to low power if that's desirable).
> 
> However, in the case of the systems this patchset is intended for system
> suspend is achieved by putting various hardware components into low-power
> states directly in a coordinated way and the system sleep state effectively
> follows from the low-power states the hardware components end up in.  The
> system is woken up from this state by an interrupt or another mechanism under
> the kernel's control.  As a result, the kernel never gives control away, so
> the state of devices after the resume is precisely known to it.
> In consequence, it need not ensure that the state of devices is consistent with
> its view, because it knows that this is the case. :-)

That's true for system suspend, but it's probably not true for
hibernation, even in embedded systems.  Of course, many embedded
systems don't use hibernation at all -- but those that do should be
aware of this issue.

> So the documentation should be updated to say what hardware model it is
> referring to.

It might be worthwhile to include a little warning about the difference 
between suspend and hibernate.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-22 22:16         ` Rafael J. Wysocki
                           ` (2 preceding siblings ...)
  (?)
@ 2011-06-23 14:19         ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 14:19 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> Well, let's say this part of the documentation is slightly outdated.
> 
> It basically refers to the model in which system suspend is a separate global
> hardware or firmware operation, so the state of devices may be changed by the
> BIOS or whatever takes over control in the meantime.  In that case the kernel
> has to ensure that the states of devices are consistent with what it thinks
> about them and the simplest way to achieve that is to put the devices to
> full power during resume (and back to low power if that's desirable).
> 
> However, in the case of the systems this patchset is intended for system
> suspend is achieved by putting various hardware components into low-power
> states directly in a coordinated way and the system sleep state effectively
> follows from the low-power states the hardware components end up in.  The
> system is woken up from this state by an interrupt or another mechanism under
> the kernel's control.  As a result, the kernel never gives control away, so
> the state of devices after the resume is precisely known to it.
> In consequence, it need not ensure that the state of devices is consistent with
> its view, because it knows that this is the case. :-)

That's true for system suspend, but it's probably not true for
hibernation, even in embedded systems.  Of course, many embedded
systems don't use hibernation at all -- but those that do should be
aware of this issue.

> So the documentation should be updated to say what hardware model it is
> referring to.

It might be worthwhile to include a little warning about the difference 
between suspend and hibernate.

Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 14:19           ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
@ 2011-06-23 14:44             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 14:44 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > Well, let's say this part of the documentation is slightly outdated.
> > 
> > It basically refers to the model in which system suspend is a separate global
> > hardware or firmware operation, so the state of devices may be changed by the
> > BIOS or whatever takes over control in the meantime.  In that case the kernel
> > has to ensure that the states of devices are consistent with what it thinks
> > about them and the simplest way to achieve that is to put the devices to
> > full power during resume (and back to low power if that's desirable).
> > 
> > However, in the case of the systems this patchset is intended for system
> > suspend is achieved by putting various hardware components into low-power
> > states directly in a coordinated way and the system sleep state effectively
> > follows from the low-power states the hardware components end up in.  The
> > system is woken up from this state by an interrupt or another mechanism under
> > the kernel's control.  As a result, the kernel never gives control away, so
> > the state of devices after the resume is precisely known to it.
> > In consequence, it need not ensure that the state of devices is consistent with
> > its view, because it knows that this is the case. :-)
> 
> That's true for system suspend, but it's probably not true for
> hibernation, even in embedded systems.  Of course, many embedded
> systems don't use hibernation at all -- but those that do should be
> aware of this issue.
> 
> > So the documentation should be updated to say what hardware model it is
> > referring to.
> 
> It might be worthwhile to include a little warning about the difference 
> between suspend and hibernate.

Well, there already is one:

"* The driver's idea of the device state may not agree with the device's
    physical state.  This can happen during resume from hibernation."

(in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:

"If that is the case and none of the situations listed above takes place, it
 may be more efficient to leave the devices that had been suspended before
 the system sleep began in the suspended state."

where the "situations listed above" include it.

Do you think that's not sufficient?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-23 14:44             ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 14:44 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > Well, let's say this part of the documentation is slightly outdated.
> > 
> > It basically refers to the model in which system suspend is a separate global
> > hardware or firmware operation, so the state of devices may be changed by the
> > BIOS or whatever takes over control in the meantime.  In that case the kernel
> > has to ensure that the states of devices are consistent with what it thinks
> > about them and the simplest way to achieve that is to put the devices to
> > full power during resume (and back to low power if that's desirable).
> > 
> > However, in the case of the systems this patchset is intended for system
> > suspend is achieved by putting various hardware components into low-power
> > states directly in a coordinated way and the system sleep state effectively
> > follows from the low-power states the hardware components end up in.  The
> > system is woken up from this state by an interrupt or another mechanism under
> > the kernel's control.  As a result, the kernel never gives control away, so
> > the state of devices after the resume is precisely known to it.
> > In consequence, it need not ensure that the state of devices is consistent with
> > its view, because it knows that this is the case. :-)
> 
> That's true for system suspend, but it's probably not true for
> hibernation, even in embedded systems.  Of course, many embedded
> systems don't use hibernation at all -- but those that do should be
> aware of this issue.
> 
> > So the documentation should be updated to say what hardware model it is
> > referring to.
> 
> It might be worthwhile to include a little warning about the difference 
> between suspend and hibernate.

Well, there already is one:

"* The driver's idea of the device state may not agree with the device's
    physical state.  This can happen during resume from hibernation."

(in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:

"If that is the case and none of the situations listed above takes place, it
 may be more efficient to leave the devices that had been suspended before
 the system sleep began in the suspended state."

where the "situations listed above" include it.

Do you think that's not sufficient?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 14:19           ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
  (?)
@ 2011-06-23 14:44           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 14:44 UTC (permalink / raw)
  To: Alan Stern; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > Well, let's say this part of the documentation is slightly outdated.
> > 
> > It basically refers to the model in which system suspend is a separate global
> > hardware or firmware operation, so the state of devices may be changed by the
> > BIOS or whatever takes over control in the meantime.  In that case the kernel
> > has to ensure that the states of devices are consistent with what it thinks
> > about them and the simplest way to achieve that is to put the devices to
> > full power during resume (and back to low power if that's desirable).
> > 
> > However, in the case of the systems this patchset is intended for system
> > suspend is achieved by putting various hardware components into low-power
> > states directly in a coordinated way and the system sleep state effectively
> > follows from the low-power states the hardware components end up in.  The
> > system is woken up from this state by an interrupt or another mechanism under
> > the kernel's control.  As a result, the kernel never gives control away, so
> > the state of devices after the resume is precisely known to it.
> > In consequence, it need not ensure that the state of devices is consistent with
> > its view, because it knows that this is the case. :-)
> 
> That's true for system suspend, but it's probably not true for
> hibernation, even in embedded systems.  Of course, many embedded
> systems don't use hibernation at all -- but those that do should be
> aware of this issue.
> 
> > So the documentation should be updated to say what hardware model it is
> > referring to.
> 
> It might be worthwhile to include a little warning about the difference 
> between suspend and hibernate.

Well, there already is one:

"* The driver's idea of the device state may not agree with the device's
    physical state.  This can happen during resume from hibernation."

(in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:

"If that is the case and none of the situations listed above takes place, it
 may be more efficient to leave the devices that had been suspended before
 the system sleep began in the suspended state."

where the "situations listed above" include it.

Do you think that's not sufficient?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support
  2011-06-23 14:44             ` Rafael J. Wysocki
@ 2011-06-23 15:11               ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 15:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> > It might be worthwhile to include a little warning about the difference 
> > between suspend and hibernate.
> 
> Well, there already is one:
> 
> "* The driver's idea of the device state may not agree with the device's
>     physical state.  This can happen during resume from hibernation."
> 
> (in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
> https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:
> 
> "If that is the case and none of the situations listed above takes place, it
>  may be more efficient to leave the devices that had been suspended before
>  the system sleep began in the suspended state."
> 
> where the "situations listed above" include it.
> 
> Do you think that's not sufficient?

The preceding text in your new patch says:

+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.

It should say "system suspend" rather than "system sleep".

Then to drive the point home, the following sentence chould say 
something like this:

If that is the case and none of the situations listed above takes place
(in particular, if the system is waking up from suspend and not from
hibernation), it may be more efficient to leave the devices that had
been suspended before the system suspend began in the suspended state.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-23 15:11               ` Alan Stern
  0 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 15:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> > It might be worthwhile to include a little warning about the difference 
> > between suspend and hibernate.
> 
> Well, there already is one:
> 
> "* The driver's idea of the device state may not agree with the device's
>     physical state.  This can happen during resume from hibernation."
> 
> (in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
> https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:
> 
> "If that is the case and none of the situations listed above takes place, it
>  may be more efficient to leave the devices that had been suspended before
>  the system sleep began in the suspended state."
> 
> where the "situations listed above" include it.
> 
> Do you think that's not sufficient?

The preceding text in your new patch says:

+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.

It should say "system suspend" rather than "system sleep".

Then to drive the point home, the following sentence chould say 
something like this:

If that is the case and none of the situations listed above takes place
(in particular, if the system is waking up from suspend and not from
hibernation), it may be more efficient to leave the devices that had
been suspended before the system suspend began in the suspended state.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 14:44             ` Rafael J. Wysocki
  (?)
@ 2011-06-23 15:11             ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 15:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> > It might be worthwhile to include a little warning about the difference 
> > between suspend and hibernate.
> 
> Well, there already is one:
> 
> "* The driver's idea of the device state may not agree with the device's
>     physical state.  This can happen during resume from hibernation."
> 
> (in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
> https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:
> 
> "If that is the case and none of the situations listed above takes place, it
>  may be more efficient to leave the devices that had been suspended before
>  the system sleep began in the suspended state."
> 
> where the "situations listed above" include it.
> 
> Do you think that's not sufficient?

The preceding text in your new patch says:

+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.

It should say "system suspend" rather than "system sleep".

Then to drive the point home, the following sentence chould say 
something like this:

If that is the case and none of the situations listed above takes place
(in particular, if the system is waking up from suspend and not from
hibernation), it may be more efficient to leave the devices that had
been suspended before the system suspend began in the suspended state.

Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 15:11               ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
@ 2011-06-23 17:41                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 17:41 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > > It might be worthwhile to include a little warning about the difference 
> > > between suspend and hibernate.
> > 
> > Well, there already is one:
> > 
> > "* The driver's idea of the device state may not agree with the device's
> >     physical state.  This can happen during resume from hibernation."
> > 
> > (in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
> > https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:
> > 
> > "If that is the case and none of the situations listed above takes place, it
> >  may be more efficient to leave the devices that had been suspended before
> >  the system sleep began in the suspended state."
> > 
> > where the "situations listed above" include it.
> > 
> > Do you think that's not sufficient?
> 
> The preceding text in your new patch says:
> 
> +On some systems, however, system sleep is not entered through a global firmware
> +or hardware operation.  Instead, all hardware components are put into low-power
> +states directly by the kernel in a coordinated way.  Then, the system sleep
> +state effectively follows from the states the hardware components end up in
> +and the system is woken up from that state by a hardware interrupt or a similar
> +mechanism entirely under the kernel's control.  As a result, the kernel never
> +gives control away and the states of all devices during resume are precisely
> +known to it.
> 
> It should say "system suspend" rather than "system sleep".

It says "system sleep" to distinguish between the state of the system
("system sleep") and the operation leading to that state ("system suspend").
That terminology is used all over the document, so I don't think it's a good
idea to change it just for this specific paragraph.

I agree that "suspend" should be used where it talks about starting, stopping
etc.

> Then to drive the point home, the following sentence chould say 
> something like this:
> 
> If that is the case and none of the situations listed above takes place
> (in particular, if the system is waking up from suspend and not from
> hibernation), it may be more efficient to leave the devices that had
> been suspended before the system suspend began in the suspended state.

That's fine by me, except that I'd simply say "(in particular, if the system
is not waking up from hibernation)".

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-23 17:41                 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 17:41 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > > It might be worthwhile to include a little warning about the difference 
> > > between suspend and hibernate.
> > 
> > Well, there already is one:
> > 
> > "* The driver's idea of the device state may not agree with the device's
> >     physical state.  This can happen during resume from hibernation."
> > 
> > (in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
> > https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:
> > 
> > "If that is the case and none of the situations listed above takes place, it
> >  may be more efficient to leave the devices that had been suspended before
> >  the system sleep began in the suspended state."
> > 
> > where the "situations listed above" include it.
> > 
> > Do you think that's not sufficient?
> 
> The preceding text in your new patch says:
> 
> +On some systems, however, system sleep is not entered through a global firmware
> +or hardware operation.  Instead, all hardware components are put into low-power
> +states directly by the kernel in a coordinated way.  Then, the system sleep
> +state effectively follows from the states the hardware components end up in
> +and the system is woken up from that state by a hardware interrupt or a similar
> +mechanism entirely under the kernel's control.  As a result, the kernel never
> +gives control away and the states of all devices during resume are precisely
> +known to it.
> 
> It should say "system suspend" rather than "system sleep".

It says "system sleep" to distinguish between the state of the system
("system sleep") and the operation leading to that state ("system suspend").
That terminology is used all over the document, so I don't think it's a good
idea to change it just for this specific paragraph.

I agree that "suspend" should be used where it talks about starting, stopping
etc.

> Then to drive the point home, the following sentence chould say 
> something like this:
> 
> If that is the case and none of the situations listed above takes place
> (in particular, if the system is waking up from suspend and not from
> hibernation), it may be more efficient to leave the devices that had
> been suspended before the system suspend began in the suspended state.

That's fine by me, except that I'd simply say "(in particular, if the system
is not waking up from hibernation)".

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 15:11               ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
  (?)
@ 2011-06-23 17:41               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 17:41 UTC (permalink / raw)
  To: Alan Stern; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > > It might be worthwhile to include a little warning about the difference 
> > > between suspend and hibernate.
> > 
> > Well, there already is one:
> > 
> > "* The driver's idea of the device state may not agree with the device's
> >     physical state.  This can happen during resume from hibernation."
> > 
> > (in Documentation/power/runtime_pm.txt).  Also, the new text in the patch
> > https://lkml.org/lkml/2011/6/23/200 I've just sent says literally:
> > 
> > "If that is the case and none of the situations listed above takes place, it
> >  may be more efficient to leave the devices that had been suspended before
> >  the system sleep began in the suspended state."
> > 
> > where the "situations listed above" include it.
> > 
> > Do you think that's not sufficient?
> 
> The preceding text in your new patch says:
> 
> +On some systems, however, system sleep is not entered through a global firmware
> +or hardware operation.  Instead, all hardware components are put into low-power
> +states directly by the kernel in a coordinated way.  Then, the system sleep
> +state effectively follows from the states the hardware components end up in
> +and the system is woken up from that state by a hardware interrupt or a similar
> +mechanism entirely under the kernel's control.  As a result, the kernel never
> +gives control away and the states of all devices during resume are precisely
> +known to it.
> 
> It should say "system suspend" rather than "system sleep".

It says "system sleep" to distinguish between the state of the system
("system sleep") and the operation leading to that state ("system suspend").
That terminology is used all over the document, so I don't think it's a good
idea to change it just for this specific paragraph.

I agree that "suspend" should be used where it talks about starting, stopping
etc.

> Then to drive the point home, the following sentence chould say 
> something like this:
> 
> If that is the case and none of the situations listed above takes place
> (in particular, if the system is waking up from suspend and not from
> hibernation), it may be more efficient to leave the devices that had
> been suspended before the system suspend began in the suspended state.

That's fine by me, except that I'd simply say "(in particular, if the system
is not waking up from hibernation)".

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support
  2011-06-23 17:41                 ` Rafael J. Wysocki
@ 2011-06-23 18:22                   ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 18:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> > It should say "system suspend" rather than "system sleep".
> 
> It says "system sleep" to distinguish between the state of the system
> ("system sleep") and the operation leading to that state ("system suspend").
> That terminology is used all over the document, so I don't think it's a good
> idea to change it just for this specific paragraph.
> 
> I agree that "suspend" should be used where it talks about starting, stopping
> etc.
> 
> > Then to drive the point home, the following sentence chould say 
> > something like this:
> > 
> > If that is the case and none of the situations listed above takes place
> > (in particular, if the system is waking up from suspend and not from
> > hibernation), it may be more efficient to leave the devices that had
> > been suspended before the system suspend began in the suspended state.
> 
> That's fine by me, except that I'd simply say "(in particular, if the system
> is not waking up from hibernation)".

Okay.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-23 18:22                   ` Alan Stern
  0 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 18:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> > It should say "system suspend" rather than "system sleep".
> 
> It says "system sleep" to distinguish between the state of the system
> ("system sleep") and the operation leading to that state ("system suspend").
> That terminology is used all over the document, so I don't think it's a good
> idea to change it just for this specific paragraph.
> 
> I agree that "suspend" should be used where it talks about starting, stopping
> etc.
> 
> > Then to drive the point home, the following sentence chould say 
> > something like this:
> > 
> > If that is the case and none of the situations listed above takes place
> > (in particular, if the system is waking up from suspend and not from
> > hibernation), it may be more efficient to leave the devices that had
> > been suspended before the system suspend began in the suspended state.
> 
> That's fine by me, except that I'd simply say "(in particular, if the system
> is not waking up from hibernation)".

Okay.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 17:41                 ` Rafael J. Wysocki
  (?)
@ 2011-06-23 18:22                 ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-06-23 18:22 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:

> > It should say "system suspend" rather than "system sleep".
> 
> It says "system sleep" to distinguish between the state of the system
> ("system sleep") and the operation leading to that state ("system suspend").
> That terminology is used all over the document, so I don't think it's a good
> idea to change it just for this specific paragraph.
> 
> I agree that "suspend" should be used where it talks about starting, stopping
> etc.
> 
> > Then to drive the point home, the following sentence chould say 
> > something like this:
> > 
> > If that is the case and none of the situations listed above takes place
> > (in particular, if the system is waking up from suspend and not from
> > hibernation), it may be more efficient to leave the devices that had
> > been suspended before the system suspend began in the suspended state.
> 
> That's fine by me, except that I'd simply say "(in particular, if the system
> is not waking up from hibernation)".

Okay.

Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 18:22                   ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
@ 2011-06-23 21:03                     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 21:03 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > > It should say "system suspend" rather than "system sleep".
> > 
> > It says "system sleep" to distinguish between the state of the system
> > ("system sleep") and the operation leading to that state ("system suspend").
> > That terminology is used all over the document, so I don't think it's a good
> > idea to change it just for this specific paragraph.
> > 
> > I agree that "suspend" should be used where it talks about starting, stopping
> > etc.
> > 
> > > Then to drive the point home, the following sentence chould say 
> > > something like this:
> > > 
> > > If that is the case and none of the situations listed above takes place
> > > (in particular, if the system is waking up from suspend and not from
> > > hibernation), it may be more efficient to leave the devices that had
> > > been suspended before the system suspend began in the suspended state.
> > 
> > That's fine by me, except that I'd simply say "(in particular, if the system
> > is not waking up from hibernation)".
> 
> Okay.

Cool. :-)

Appended is an updated patch.

Thanks,
Rafael


---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Runtime: Update documentation of interactions with system sleep

The documents describing the interactions between runtime PM and
system sleep generally refer to the model in which the system sleep
state is entered through a global firmware or hardware operation.
As a result, some recommendations given in there are not entirely
suitable for systems in which this is not the case.  Update the
documentation take the existence of those systems into accout.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt    |    6 +++---
 Documentation/power/runtime_pm.txt |   27 +++++++++++++++++++--------
 2 files changed, 22 insertions(+), 11 deletions(-)

Index: linux-2.6/Documentation/power/runtime_pm.txt
=================================--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -537,9 +537,9 @@ suspend routine).  It may be necessary t
 in order to do so.  The same is true if the driver uses different power levels
 or other settings for run-time suspend and system sleep.
 
-During system resume, devices generally should be brought back to full power,
-even if they were suspended before the system sleep began.  There are several
-reasons for this, including:
+During system resume, the simplest approach is to bring all devices back to full
+power, even if they had been suspended before the system suspend began.  There
+are several reasons for this, including:
 
   * The device might need to switch power levels, wake-up settings, etc.
 
@@ -556,16 +556,27 @@ reasons for this, including:
   * Even though the device was suspended, if its usage counter was > 0 then most
     likely it would need a run-time resume in the near future anyway.
 
-  * Always going back to full power is simplest.
-
-If the device was suspended before the sleep began, then its run-time PM status
-will have to be updated to reflect the actual post-system sleep status.  The way
-to do this is:
+If the device had been suspended before the system suspend began and it's
+brought back to full power during resume, then its run-time PM status will have
+to be updated to reflect the actual post-system sleep status.  The way to do
+this is:
 
 	pm_runtime_disable(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
 
+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.  If that is the case and none of the situations listed above takes
+place (in particular, if the system is not waking up from hibernation), it may
+be more efficient to leave the devices that had been suspended before the system
+suspend began in the suspended state.
+
 7. Generic subsystem callbacks
 
 Subsystems may wish to conserve code space by using the set of generic power
Index: linux-2.6/Documentation/power/devices.txt
=================================--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -604,7 +604,7 @@ state temporarily, for example so that i
 disabled.  This all depends on the hardware and the design of the subsystem and
 device driver in question.
 
-During system-wide resume from a sleep state it's best to put devices into the
-full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer to
-that document for more information regarding this particular issue as well as
+During system-wide resume from a sleep state it's easiest to put devices into
+the full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer
+to that document for more information regarding this particular issue as well as
 for information on the device runtime power management framework in general.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
@ 2011-06-23 21:03                     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 21:03 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > > It should say "system suspend" rather than "system sleep".
> > 
> > It says "system sleep" to distinguish between the state of the system
> > ("system sleep") and the operation leading to that state ("system suspend").
> > That terminology is used all over the document, so I don't think it's a good
> > idea to change it just for this specific paragraph.
> > 
> > I agree that "suspend" should be used where it talks about starting, stopping
> > etc.
> > 
> > > Then to drive the point home, the following sentence chould say 
> > > something like this:
> > > 
> > > If that is the case and none of the situations listed above takes place
> > > (in particular, if the system is waking up from suspend and not from
> > > hibernation), it may be more efficient to leave the devices that had
> > > been suspended before the system suspend began in the suspended state.
> > 
> > That's fine by me, except that I'd simply say "(in particular, if the system
> > is not waking up from hibernation)".
> 
> Okay.

Cool. :-)

Appended is an updated patch.

Thanks,
Rafael


---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Runtime: Update documentation of interactions with system sleep

The documents describing the interactions between runtime PM and
system sleep generally refer to the model in which the system sleep
state is entered through a global firmware or hardware operation.
As a result, some recommendations given in there are not entirely
suitable for systems in which this is not the case.  Update the
documentation take the existence of those systems into accout.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt    |    6 +++---
 Documentation/power/runtime_pm.txt |   27 +++++++++++++++++++--------
 2 files changed, 22 insertions(+), 11 deletions(-)

Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -537,9 +537,9 @@ suspend routine).  It may be necessary t
 in order to do so.  The same is true if the driver uses different power levels
 or other settings for run-time suspend and system sleep.
 
-During system resume, devices generally should be brought back to full power,
-even if they were suspended before the system sleep began.  There are several
-reasons for this, including:
+During system resume, the simplest approach is to bring all devices back to full
+power, even if they had been suspended before the system suspend began.  There
+are several reasons for this, including:
 
   * The device might need to switch power levels, wake-up settings, etc.
 
@@ -556,16 +556,27 @@ reasons for this, including:
   * Even though the device was suspended, if its usage counter was > 0 then most
     likely it would need a run-time resume in the near future anyway.
 
-  * Always going back to full power is simplest.
-
-If the device was suspended before the sleep began, then its run-time PM status
-will have to be updated to reflect the actual post-system sleep status.  The way
-to do this is:
+If the device had been suspended before the system suspend began and it's
+brought back to full power during resume, then its run-time PM status will have
+to be updated to reflect the actual post-system sleep status.  The way to do
+this is:
 
 	pm_runtime_disable(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
 
+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.  If that is the case and none of the situations listed above takes
+place (in particular, if the system is not waking up from hibernation), it may
+be more efficient to leave the devices that had been suspended before the system
+suspend began in the suspended state.
+
 7. Generic subsystem callbacks
 
 Subsystems may wish to conserve code space by using the set of generic power
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -604,7 +604,7 @@ state temporarily, for example so that i
 disabled.  This all depends on the hardware and the design of the subsystem and
 device driver in question.
 
-During system-wide resume from a sleep state it's best to put devices into the
-full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer to
-that document for more information regarding this particular issue as well as
+During system-wide resume from a sleep state it's easiest to put devices into
+the full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer
+to that document for more information regarding this particular issue as well as
 for information on the device runtime power management framework in general.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3)
  2011-06-23 18:22                   ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
  (?)
@ 2011-06-23 21:03                   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-23 21:03 UTC (permalink / raw)
  To: Alan Stern; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thursday, June 23, 2011, Alan Stern wrote:
> On Thu, 23 Jun 2011, Rafael J. Wysocki wrote:
> 
> > > It should say "system suspend" rather than "system sleep".
> > 
> > It says "system sleep" to distinguish between the state of the system
> > ("system sleep") and the operation leading to that state ("system suspend").
> > That terminology is used all over the document, so I don't think it's a good
> > idea to change it just for this specific paragraph.
> > 
> > I agree that "suspend" should be used where it talks about starting, stopping
> > etc.
> > 
> > > Then to drive the point home, the following sentence chould say 
> > > something like this:
> > > 
> > > If that is the case and none of the situations listed above takes place
> > > (in particular, if the system is waking up from suspend and not from
> > > hibernation), it may be more efficient to leave the devices that had
> > > been suspended before the system suspend began in the suspended state.
> > 
> > That's fine by me, except that I'd simply say "(in particular, if the system
> > is not waking up from hibernation)".
> 
> Okay.

Cool. :-)

Appended is an updated patch.

Thanks,
Rafael


---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Runtime: Update documentation of interactions with system sleep

The documents describing the interactions between runtime PM and
system sleep generally refer to the model in which the system sleep
state is entered through a global firmware or hardware operation.
As a result, some recommendations given in there are not entirely
suitable for systems in which this is not the case.  Update the
documentation take the existence of those systems into accout.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/devices.txt    |    6 +++---
 Documentation/power/runtime_pm.txt |   27 +++++++++++++++++++--------
 2 files changed, 22 insertions(+), 11 deletions(-)

Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -537,9 +537,9 @@ suspend routine).  It may be necessary t
 in order to do so.  The same is true if the driver uses different power levels
 or other settings for run-time suspend and system sleep.
 
-During system resume, devices generally should be brought back to full power,
-even if they were suspended before the system sleep began.  There are several
-reasons for this, including:
+During system resume, the simplest approach is to bring all devices back to full
+power, even if they had been suspended before the system suspend began.  There
+are several reasons for this, including:
 
   * The device might need to switch power levels, wake-up settings, etc.
 
@@ -556,16 +556,27 @@ reasons for this, including:
   * Even though the device was suspended, if its usage counter was > 0 then most
     likely it would need a run-time resume in the near future anyway.
 
-  * Always going back to full power is simplest.
-
-If the device was suspended before the sleep began, then its run-time PM status
-will have to be updated to reflect the actual post-system sleep status.  The way
-to do this is:
+If the device had been suspended before the system suspend began and it's
+brought back to full power during resume, then its run-time PM status will have
+to be updated to reflect the actual post-system sleep status.  The way to do
+this is:
 
 	pm_runtime_disable(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
 
+On some systems, however, system sleep is not entered through a global firmware
+or hardware operation.  Instead, all hardware components are put into low-power
+states directly by the kernel in a coordinated way.  Then, the system sleep
+state effectively follows from the states the hardware components end up in
+and the system is woken up from that state by a hardware interrupt or a similar
+mechanism entirely under the kernel's control.  As a result, the kernel never
+gives control away and the states of all devices during resume are precisely
+known to it.  If that is the case and none of the situations listed above takes
+place (in particular, if the system is not waking up from hibernation), it may
+be more efficient to leave the devices that had been suspended before the system
+suspend began in the suspended state.
+
 7. Generic subsystem callbacks
 
 Subsystems may wish to conserve code space by using the set of generic power
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -604,7 +604,7 @@ state temporarily, for example so that i
 disabled.  This all depends on the hardware and the design of the subsystem and
 device driver in question.
 
-During system-wide resume from a sleep state it's best to put devices into the
-full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer to
-that document for more information regarding this particular issue as well as
+During system-wide resume from a sleep state it's easiest to put devices into
+the full-power state, as explained in Documentation/power/runtime_pm.txt.  Refer
+to that document for more information regarding this particular issue as well as
 for information on the device runtime power management framework in general.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH] PM / Runtime: Update documentation of interactions with system sleep
  2011-06-23 13:57               ` Rafael J. Wysocki
@ 2011-06-24 18:25                 ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-24 18:25 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The documents describing the interactions between runtime PM and
> system sleep generally refer to the model in which the system sleep
> state is entered through a global firmware or hardware operation.
> As a result, some recommendations given in there are not entirely
> suitable for systems in which this is not the case.  Update the
> documentation take the existence of those systems into accout.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Looks good, thanks for updating this.

Reviewed-by: Kevin Hilman <khilman@ti.com>

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH] PM / Runtime: Update documentation of interactions with system sleep
@ 2011-06-24 18:25                 ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-24 18:25 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The documents describing the interactions between runtime PM and
> system sleep generally refer to the model in which the system sleep
> state is entered through a global firmware or hardware operation.
> As a result, some recommendations given in there are not entirely
> suitable for systems in which this is not the case.  Update the
> documentation take the existence of those systems into accout.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Looks good, thanks for updating this.

Reviewed-by: Kevin Hilman <khilman@ti.com>

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH] PM / Runtime: Update documentation of interactions with system sleep
  2011-06-23 13:57               ` Rafael J. Wysocki
  (?)
@ 2011-06-24 18:25               ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-24 18:25 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> The documents describing the interactions between runtime PM and
> system sleep generally refer to the model in which the system sleep
> state is entered through a global firmware or hardware operation.
> As a result, some recommendations given in there are not entirely
> suitable for systems in which this is not the case.  Update the
> documentation take the existence of those systems into accout.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Looks good, thanks for updating this.

Reviewed-by: Kevin Hilman <khilman@ti.com>

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains
  2011-06-11 20:23 ` Rafael J. Wysocki
@ 2011-06-25 21:24   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:24 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

Hi,

Well, one more update. :-)

This is the 6th version of the patchset adding support for generic I/O PM
domains.  The majority of patches have had some small fixes here and there
to make things work when CONFIG_PM_RUNTIME is unset.  In addition to that,
to make the shmobile code introduced by the last patch work for
CONFIG_PM_RUNTIME unset I had to add patches [8-9/10].  Patch [7/10]
adds wakeup support that was missing from the previous version and I
decided not to push patch [1/10] for 3.0 after all.

On Saturday, June 11, 2011, Rafael J. Wysocki wrote:
> 
> The model here is that a bunch of devices share a common power resource
> that can be turned on and off by software.  In addition to that, there
> are means to start and stop the activity of each device, for example
> by manipulating their clocks.  Moreover, there may be hierarchy of
> such things, for example power resource A may be necessary for devices
> a, b, c, which don't rely on any other power resources, and for devices
> x, y, z that also rely on power resource X.  In that case there one PM
> domain object representing devices a, b, c and power resource A, and 
> another PM domain object will represent devices x, y, z with power
> resource X, plus the first object will be the second one's parent.
> 
> Overall, I think I've taken all of the important dependencies into
> consideration, but if you spot something suspicious, please let me know. :-)

[ 1/10] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
          fact that those objects need not correspond to hardware power domains
          directly.

[ 2/10] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME

[ 3/10] - Introduce runtime PM support for generic I/O PM domains.

[ 4/10] - Introduce generic "noirq" callbacks for system suspend/hibernation
          (that's necessary for the next patches).
 
[ 5/10] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME

[ 6/10] - Add system-wide PM support for generic I/O PM domains.

[ 7/10] - Add support for wakeup from system sleep to the I/O PM domains code.

[ 8/10] - Allow PM clocks management code to be used during system suspend.

[ 9/10] - Rename PM clock management functions so that they names reflect the
          fact that they can be used during system suspend too.

[10/10] - Use the new code to represent the SH7372's A4MP power domain.

All of these patches are regarded as 3.1 material and they are available
from the git branch at:

git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6.git pm-domains  

The patchset has been tested on SH7372 Mackerel board for both
CONFIG_PM_RUNTIME set and unset.

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains
@ 2011-06-25 21:24   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:24 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

Hi,

Well, one more update. :-)

This is the 6th version of the patchset adding support for generic I/O PM
domains.  The majority of patches have had some small fixes here and there
to make things work when CONFIG_PM_RUNTIME is unset.  In addition to that,
to make the shmobile code introduced by the last patch work for
CONFIG_PM_RUNTIME unset I had to add patches [8-9/10].  Patch [7/10]
adds wakeup support that was missing from the previous version and I
decided not to push patch [1/10] for 3.0 after all.

On Saturday, June 11, 2011, Rafael J. Wysocki wrote:
> 
> The model here is that a bunch of devices share a common power resource
> that can be turned on and off by software.  In addition to that, there
> are means to start and stop the activity of each device, for example
> by manipulating their clocks.  Moreover, there may be hierarchy of
> such things, for example power resource A may be necessary for devices
> a, b, c, which don't rely on any other power resources, and for devices
> x, y, z that also rely on power resource X.  In that case there one PM
> domain object representing devices a, b, c and power resource A, and 
> another PM domain object will represent devices x, y, z with power
> resource X, plus the first object will be the second one's parent.
> 
> Overall, I think I've taken all of the important dependencies into
> consideration, but if you spot something suspicious, please let me know. :-)

[ 1/10] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
          fact that those objects need not correspond to hardware power domains
          directly.

[ 2/10] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME

[ 3/10] - Introduce runtime PM support for generic I/O PM domains.

[ 4/10] - Introduce generic "noirq" callbacks for system suspend/hibernation
          (that's necessary for the next patches).
 
[ 5/10] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME

[ 6/10] - Add system-wide PM support for generic I/O PM domains.

[ 7/10] - Add support for wakeup from system sleep to the I/O PM domains code.

[ 8/10] - Allow PM clocks management code to be used during system suspend.

[ 9/10] - Rename PM clock management functions so that they names reflect the
          fact that they can be used during system suspend too.

[10/10] - Use the new code to represent the SH7372's A4MP power domain.

All of these patches are regarded as 3.1 material and they are available
from the git branch at:

git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6.git pm-domains  

The patchset has been tested on SH7372 Mackerel board for both
CONFIG_PM_RUNTIME set and unset.

Thanks,
Rafael


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains
  2011-06-11 20:23 ` Rafael J. Wysocki
                   ` (22 preceding siblings ...)
  (?)
@ 2011-06-25 21:24 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:24 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

Hi,

Well, one more update. :-)

This is the 6th version of the patchset adding support for generic I/O PM
domains.  The majority of patches have had some small fixes here and there
to make things work when CONFIG_PM_RUNTIME is unset.  In addition to that,
to make the shmobile code introduced by the last patch work for
CONFIG_PM_RUNTIME unset I had to add patches [8-9/10].  Patch [7/10]
adds wakeup support that was missing from the previous version and I
decided not to push patch [1/10] for 3.0 after all.

On Saturday, June 11, 2011, Rafael J. Wysocki wrote:
> 
> The model here is that a bunch of devices share a common power resource
> that can be turned on and off by software.  In addition to that, there
> are means to start and stop the activity of each device, for example
> by manipulating their clocks.  Moreover, there may be hierarchy of
> such things, for example power resource A may be necessary for devices
> a, b, c, which don't rely on any other power resources, and for devices
> x, y, z that also rely on power resource X.  In that case there one PM
> domain object representing devices a, b, c and power resource A, and 
> another PM domain object will represent devices x, y, z with power
> resource X, plus the first object will be the second one's parent.
> 
> Overall, I think I've taken all of the important dependencies into
> consideration, but if you spot something suspicious, please let me know. :-)

[ 1/10] - Rename struct dev_power_domain to struct dev_pm_domain to reflect the
          fact that those objects need not correspond to hardware power domains
          directly.

[ 2/10] - Move subsys_data in struct dev_pm_info out of #ifdef CONFIG_PM_RUNTIME

[ 3/10] - Introduce runtime PM support for generic I/O PM domains.

[ 4/10] - Introduce generic "noirq" callbacks for system suspend/hibernation
          (that's necessary for the next patches).
 
[ 5/10] - Move some PM domains support code fro under #ifdef CONFIG_PM_RUNTIME

[ 6/10] - Add system-wide PM support for generic I/O PM domains.

[ 7/10] - Add support for wakeup from system sleep to the I/O PM domains code.

[ 8/10] - Allow PM clocks management code to be used during system suspend.

[ 9/10] - Rename PM clock management functions so that they names reflect the
          fact that they can be used during system suspend too.

[10/10] - Use the new code to represent the SH7372's A4MP power domain.

All of these patches are regarded as 3.1 material and they are available
from the git branch at:

git://git.kernel.org/pub/scm/linux/kernel/git/rafael/suspend-2.6.git pm-domains  

The patchset has been tested on SH7372 Mackerel board for both
CONFIG_PM_RUNTIME set and unset.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 1/10 v6] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:24     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:24 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
(PM: Add support for device power domains), which introduced the
struct dev_power_domain type for representing device power domains,
evidently confuses some developers who tend to think that objects
of this type must correspond to "power domains" as defined by
hardware, which is not the case.  Namely, at the kernel level, a
struct dev_power_domain object can represent arbitrary set of devices
that are mutually dependent power management-wise and need not belong
to one hardware power domain.  To avoid that confusion, rename struct
dev_power_domain to struct dev_pm_domain and rename the related
pointers in struct device and struct pm_clk_notifier_block from
pwr_domain to pm_domain.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Kevin Hilman <khilman@ti.com>
---
 Documentation/power/devices.txt          |    8 ++++----
 arch/arm/mach-omap1/pm_bus.c             |    8 ++++----
 arch/arm/mach-shmobile/pm_runtime.c      |    8 ++++----
 arch/arm/plat-omap/omap_device.c         |    4 ++--
 arch/sh/kernel/cpu/shmobile/pm_runtime.c |    6 +++---
 drivers/base/power/clock_ops.c           |   14 +++++++-------
 drivers/base/power/main.c                |   30 +++++++++++++++---------------
 drivers/base/power/runtime.c             |   12 ++++++------
 include/linux/device.h                   |    4 ++--
 include/linux/pm.h                       |    2 +-
 include/linux/pm_runtime.h               |    2 +-
 11 files changed, 49 insertions(+), 49 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
@@ -49,20 +49,20 @@ static int omap1_pm_runtime_resume(struc
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = omap1_pm_runtime_suspend,
 		.runtime_resume = omap1_pm_runtime_resume,
 		USE_PLATFORM_PM_SLEEP_OPS
 	},
 };
-#define OMAP1_PWR_DOMAIN (&default_power_domain)
+#define OMAP1_PM_DOMAIN (&default_pm_domain)
 #else
-#define OMAP1_PWR_DOMAIN NULL
+#define OMAP1_PM_DOMAIN NULL
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = OMAP1_PWR_DOMAIN,
+	.pm_domain = OMAP1_PM_DOMAIN,
 	.con_ids = { "ick", "fck", NULL, },
 };
 
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
@@ -28,7 +28,7 @@ static int default_platform_runtime_idle
 	return pm_runtime_suspend(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = pm_runtime_clk_suspend,
 		.runtime_resume = pm_runtime_clk_resume,
@@ -37,16 +37,16 @@ static struct dev_power_domain default_p
 	},
 };
 
-#define DEFAULT_PWR_DOMAIN_PTR	(&default_power_domain)
+#define DEFAULT_PM_DOMAIN_PTR	(&default_pm_domain)
 
 #else
 
-#define DEFAULT_PWR_DOMAIN_PTR	NULL
+#define DEFAULT_PM_DOMAIN_PTR	NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+	.pm_domain = DEFAULT_PM_DOMAIN_PTR,
 	.con_ids = { NULL, },
 };
 
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
@@ -564,7 +564,7 @@ static int _od_runtime_resume(struct dev
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain omap_device_power_domain = {
+static struct dev_pm_domain omap_device_pm_domain = {
 	.ops = {
 		.runtime_suspend = _od_runtime_suspend,
 		.runtime_idle = _od_runtime_idle,
@@ -586,7 +586,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;
+	od->pdev.dev.pm_domain = &omap_device_pm_domain;
 	return platform_device_register(&od->pdev);
 }
 
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
@@ -256,7 +256,7 @@ out:
 	return ret;
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = default_platform_runtime_suspend,
 		.runtime_resume = default_platform_runtime_resume,
@@ -285,7 +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;
+		dev->pm_domain = &default_pm_domain;
 		break;
 	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
 	case BUS_NOTIFY_BOUND_DRIVER:
@@ -299,7 +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;
+		dev->pm_domain = NULL;
 		break;
 	}
 	return 0;
Index: linux-2.6/Documentation/power/devices.txt
=================================--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -506,8 +506,8 @@ routines.  Nevertheless, different callb
 situation where it actually matters.
 
 
-Device Power Domains
---------------------
+Device Power Management Domains
+-------------------------------
 Sometimes devices share reference clocks or other power resources.  In those
 cases it generally is not possible to put devices into low-power states
 individually.  Instead, a set of devices sharing a power resource can be put
@@ -516,8 +516,8 @@ power resource.  Of course, they also ne
 together, by turning the shared power resource on.  A set of devices with this
 property is often referred to as a power domain.
 
-Support for power domains is provided through the pwr_domain field of struct
-device.  This field is a pointer to an object of type struct dev_power_domain,
+Support for power domains is provided through the pm_domain field of struct
+device.  This field is a pointer to an object of type struct dev_pm_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
 for the given device during all power transitions, instead of the respective
Index: linux-2.6/include/linux/device.h
=================================--- linux-2.6.orig/include/linux/device.h
+++ linux-2.6/include/linux/device.h
@@ -516,7 +516,7 @@ struct device_dma_parameters {
  * 		minimizes board-specific #ifdefs in drivers.
  * @power:	For device power management.
  * 		See Documentation/power/devices.txt for details.
- * @pwr_domain:	Provide callbacks that are executed during system suspend,
+ * @pm_domain:	Provide callbacks that are executed during system suspend,
  * 		hibernation, system resume and during runtime PM transitions
  * 		along with subsystem-level and driver-level callbacks.
  * @numa_node:	NUMA node this device is close to.
@@ -568,7 +568,7 @@ struct device {
 	void		*platform_data;	/* Platform specific data, device
 					   core doesn't touch it */
 	struct dev_pm_info	power;
-	struct dev_power_domain	*pwr_domain;
+	struct dev_pm_domain	*pm_domain;
 
 #ifdef CONFIG_NUMA
 	int		numa_node;	/* NUMA node this device is close to */
Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -472,7 +472,7 @@ extern void update_pm_runtime_accounting
  * hibernation, system resume and during runtime PM transitions along with
  * subsystem-level and driver-level callbacks.
  */
-struct dev_power_domain {
+struct dev_pm_domain {
 	struct dev_pm_ops	ops;
 };
 
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
@@ -247,7 +247,7 @@ static inline void pm_runtime_dont_use_a
 
 struct pm_clk_notifier_block {
 	struct notifier_block nb;
-	struct dev_power_domain *pwr_domain;
+	struct dev_pm_domain *pm_domain;
 	char *con_ids[];
 };
 
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
@@ -278,11 +278,11 @@ int pm_runtime_clk_resume(struct device
  *
  * 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
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_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
+ * If the device's pm_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.
  */
@@ -300,14 +300,14 @@ static int pm_runtime_clk_notify(struct
 
 	switch (action) {
 	case BUS_NOTIFY_ADD_DEVICE:
-		if (dev->pwr_domain)
+		if (dev->pm_domain)
 			break;
 
 		error = pm_runtime_clk_init(dev);
 		if (error)
 			break;
 
-		dev->pwr_domain = clknb->pwr_domain;
+		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
 				pm_runtime_clk_add(dev, *con_id);
@@ -317,10 +317,10 @@ static int pm_runtime_clk_notify(struct
 
 		break;
 	case BUS_NOTIFY_DEL_DEVICE:
-		if (dev->pwr_domain != clknb->pwr_domain)
+		if (dev->pm_domain != clknb->pm_domain)
 			break;
 
-		dev->pwr_domain = NULL;
+		dev->pm_domain = NULL;
 		pm_runtime_clk_destroy(dev);
 		break;
 	}
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,9 +425,9 @@ static int device_resume_noirq(struct de
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "EARLY power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_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);
@@ -521,9 +521,9 @@ static int device_resume(struct device *
 	if (!dev->power.is_suspended)
 		goto Unlock;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -641,10 +641,10 @@ static void device_complete(struct devic
 {
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "completing power domain ");
-		if (dev->pwr_domain->ops.complete)
-			dev->pwr_domain->ops.complete(dev);
+		if (dev->pm_domain->ops.complete)
+			dev->pm_domain->ops.complete(dev);
 	} else if (dev->type && dev->type->pm) {
 		pm_dev_dbg(dev, state, "completing type ");
 		if (dev->type->pm->complete)
@@ -744,9 +744,9 @@ static int device_suspend_noirq(struct d
 {
 	int error;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "LATE power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
 		if (error)
 			return error;
 	} else if (dev->type && dev->type->pm) {
@@ -853,9 +853,9 @@ static int __device_suspend(struct devic
 		goto Unlock;
 	}
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -982,11 +982,11 @@ static int device_prepare(struct device
 
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_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 (dev->pm_domain->ops.prepare)
+			error = dev->pm_domain->ops.prepare(dev);
+		suspend_report_result(dev->pm_domain->ops.prepare, error);
 		if (error)
 			goto End;
 	} else if (dev->type && dev->type->pm) {
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
@@ -213,8 +213,8 @@ static int rpm_idle(struct device *dev,
 
 	dev->power.idle_notification = true;
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_idle;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_idle;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_idle;
 	else if (dev->class && dev->class->pm)
@@ -374,8 +374,8 @@ static int rpm_suspend(struct device *de
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_suspend;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_suspend;
 	else if (dev->class && dev->class->pm)
@@ -573,8 +573,8 @@ static int rpm_resume(struct device *dev
 
 	__update_runtime_status(dev, RPM_RESUMING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_resume;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_resume;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_resume;
 	else if (dev->class && dev->class->pm)


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 1/10 v6] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
@ 2011-06-25 21:24     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:24 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
(PM: Add support for device power domains), which introduced the
struct dev_power_domain type for representing device power domains,
evidently confuses some developers who tend to think that objects
of this type must correspond to "power domains" as defined by
hardware, which is not the case.  Namely, at the kernel level, a
struct dev_power_domain object can represent arbitrary set of devices
that are mutually dependent power management-wise and need not belong
to one hardware power domain.  To avoid that confusion, rename struct
dev_power_domain to struct dev_pm_domain and rename the related
pointers in struct device and struct pm_clk_notifier_block from
pwr_domain to pm_domain.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Kevin Hilman <khilman@ti.com>
---
 Documentation/power/devices.txt          |    8 ++++----
 arch/arm/mach-omap1/pm_bus.c             |    8 ++++----
 arch/arm/mach-shmobile/pm_runtime.c      |    8 ++++----
 arch/arm/plat-omap/omap_device.c         |    4 ++--
 arch/sh/kernel/cpu/shmobile/pm_runtime.c |    6 +++---
 drivers/base/power/clock_ops.c           |   14 +++++++-------
 drivers/base/power/main.c                |   30 +++++++++++++++---------------
 drivers/base/power/runtime.c             |   12 ++++++------
 include/linux/device.h                   |    4 ++--
 include/linux/pm.h                       |    2 +-
 include/linux/pm_runtime.h               |    2 +-
 11 files changed, 49 insertions(+), 49 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
@@ -49,20 +49,20 @@ static int omap1_pm_runtime_resume(struc
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = omap1_pm_runtime_suspend,
 		.runtime_resume = omap1_pm_runtime_resume,
 		USE_PLATFORM_PM_SLEEP_OPS
 	},
 };
-#define OMAP1_PWR_DOMAIN (&default_power_domain)
+#define OMAP1_PM_DOMAIN (&default_pm_domain)
 #else
-#define OMAP1_PWR_DOMAIN NULL
+#define OMAP1_PM_DOMAIN NULL
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = OMAP1_PWR_DOMAIN,
+	.pm_domain = OMAP1_PM_DOMAIN,
 	.con_ids = { "ick", "fck", NULL, },
 };
 
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
@@ -28,7 +28,7 @@ static int default_platform_runtime_idle
 	return pm_runtime_suspend(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = pm_runtime_clk_suspend,
 		.runtime_resume = pm_runtime_clk_resume,
@@ -37,16 +37,16 @@ static struct dev_power_domain default_p
 	},
 };
 
-#define DEFAULT_PWR_DOMAIN_PTR	(&default_power_domain)
+#define DEFAULT_PM_DOMAIN_PTR	(&default_pm_domain)
 
 #else
 
-#define DEFAULT_PWR_DOMAIN_PTR	NULL
+#define DEFAULT_PM_DOMAIN_PTR	NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+	.pm_domain = DEFAULT_PM_DOMAIN_PTR,
 	.con_ids = { NULL, },
 };
 
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
@@ -564,7 +564,7 @@ static int _od_runtime_resume(struct dev
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain omap_device_power_domain = {
+static struct dev_pm_domain omap_device_pm_domain = {
 	.ops = {
 		.runtime_suspend = _od_runtime_suspend,
 		.runtime_idle = _od_runtime_idle,
@@ -586,7 +586,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;
+	od->pdev.dev.pm_domain = &omap_device_pm_domain;
 	return platform_device_register(&od->pdev);
 }
 
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
@@ -256,7 +256,7 @@ out:
 	return ret;
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = default_platform_runtime_suspend,
 		.runtime_resume = default_platform_runtime_resume,
@@ -285,7 +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;
+		dev->pm_domain = &default_pm_domain;
 		break;
 	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
 	case BUS_NOTIFY_BOUND_DRIVER:
@@ -299,7 +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;
+		dev->pm_domain = NULL;
 		break;
 	}
 	return 0;
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -506,8 +506,8 @@ routines.  Nevertheless, different callb
 situation where it actually matters.
 
 
-Device Power Domains
---------------------
+Device Power Management Domains
+-------------------------------
 Sometimes devices share reference clocks or other power resources.  In those
 cases it generally is not possible to put devices into low-power states
 individually.  Instead, a set of devices sharing a power resource can be put
@@ -516,8 +516,8 @@ power resource.  Of course, they also ne
 together, by turning the shared power resource on.  A set of devices with this
 property is often referred to as a power domain.
 
-Support for power domains is provided through the pwr_domain field of struct
-device.  This field is a pointer to an object of type struct dev_power_domain,
+Support for power domains is provided through the pm_domain field of struct
+device.  This field is a pointer to an object of type struct dev_pm_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
 for the given device during all power transitions, instead of the respective
Index: linux-2.6/include/linux/device.h
===================================================================
--- linux-2.6.orig/include/linux/device.h
+++ linux-2.6/include/linux/device.h
@@ -516,7 +516,7 @@ struct device_dma_parameters {
  * 		minimizes board-specific #ifdefs in drivers.
  * @power:	For device power management.
  * 		See Documentation/power/devices.txt for details.
- * @pwr_domain:	Provide callbacks that are executed during system suspend,
+ * @pm_domain:	Provide callbacks that are executed during system suspend,
  * 		hibernation, system resume and during runtime PM transitions
  * 		along with subsystem-level and driver-level callbacks.
  * @numa_node:	NUMA node this device is close to.
@@ -568,7 +568,7 @@ struct device {
 	void		*platform_data;	/* Platform specific data, device
 					   core doesn't touch it */
 	struct dev_pm_info	power;
-	struct dev_power_domain	*pwr_domain;
+	struct dev_pm_domain	*pm_domain;
 
 #ifdef CONFIG_NUMA
 	int		numa_node;	/* NUMA node this device is close to */
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -472,7 +472,7 @@ extern void update_pm_runtime_accounting
  * hibernation, system resume and during runtime PM transitions along with
  * subsystem-level and driver-level callbacks.
  */
-struct dev_power_domain {
+struct dev_pm_domain {
 	struct dev_pm_ops	ops;
 };
 
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
@@ -247,7 +247,7 @@ static inline void pm_runtime_dont_use_a
 
 struct pm_clk_notifier_block {
 	struct notifier_block nb;
-	struct dev_power_domain *pwr_domain;
+	struct dev_pm_domain *pm_domain;
 	char *con_ids[];
 };
 
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
@@ -278,11 +278,11 @@ int pm_runtime_clk_resume(struct device
  *
  * 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
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_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
+ * If the device's pm_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.
  */
@@ -300,14 +300,14 @@ static int pm_runtime_clk_notify(struct
 
 	switch (action) {
 	case BUS_NOTIFY_ADD_DEVICE:
-		if (dev->pwr_domain)
+		if (dev->pm_domain)
 			break;
 
 		error = pm_runtime_clk_init(dev);
 		if (error)
 			break;
 
-		dev->pwr_domain = clknb->pwr_domain;
+		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
 				pm_runtime_clk_add(dev, *con_id);
@@ -317,10 +317,10 @@ static int pm_runtime_clk_notify(struct
 
 		break;
 	case BUS_NOTIFY_DEL_DEVICE:
-		if (dev->pwr_domain != clknb->pwr_domain)
+		if (dev->pm_domain != clknb->pm_domain)
 			break;
 
-		dev->pwr_domain = NULL;
+		dev->pm_domain = NULL;
 		pm_runtime_clk_destroy(dev);
 		break;
 	}
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,9 +425,9 @@ static int device_resume_noirq(struct de
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "EARLY power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_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);
@@ -521,9 +521,9 @@ static int device_resume(struct device *
 	if (!dev->power.is_suspended)
 		goto Unlock;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -641,10 +641,10 @@ static void device_complete(struct devic
 {
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "completing power domain ");
-		if (dev->pwr_domain->ops.complete)
-			dev->pwr_domain->ops.complete(dev);
+		if (dev->pm_domain->ops.complete)
+			dev->pm_domain->ops.complete(dev);
 	} else if (dev->type && dev->type->pm) {
 		pm_dev_dbg(dev, state, "completing type ");
 		if (dev->type->pm->complete)
@@ -744,9 +744,9 @@ static int device_suspend_noirq(struct d
 {
 	int error;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "LATE power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
 		if (error)
 			return error;
 	} else if (dev->type && dev->type->pm) {
@@ -853,9 +853,9 @@ static int __device_suspend(struct devic
 		goto Unlock;
 	}
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -982,11 +982,11 @@ static int device_prepare(struct device
 
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_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 (dev->pm_domain->ops.prepare)
+			error = dev->pm_domain->ops.prepare(dev);
+		suspend_report_result(dev->pm_domain->ops.prepare, error);
 		if (error)
 			goto End;
 	} else if (dev->type && dev->type->pm) {
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
@@ -213,8 +213,8 @@ static int rpm_idle(struct device *dev,
 
 	dev->power.idle_notification = true;
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_idle;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_idle;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_idle;
 	else if (dev->class && dev->class->pm)
@@ -374,8 +374,8 @@ static int rpm_suspend(struct device *de
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_suspend;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_suspend;
 	else if (dev->class && dev->class->pm)
@@ -573,8 +573,8 @@ static int rpm_resume(struct device *dev
 
 	__update_runtime_status(dev, RPM_RESUMING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_resume;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_resume;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_resume;
 	else if (dev->class && dev->class->pm)


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 1/10 v6] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain
  2011-06-25 21:24   ` Rafael J. Wysocki
  (?)
@ 2011-06-25 21:24   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:24 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

The naming convention used by commit 7538e3db6e015e890825fbd9f86599b
(PM: Add support for device power domains), which introduced the
struct dev_power_domain type for representing device power domains,
evidently confuses some developers who tend to think that objects
of this type must correspond to "power domains" as defined by
hardware, which is not the case.  Namely, at the kernel level, a
struct dev_power_domain object can represent arbitrary set of devices
that are mutually dependent power management-wise and need not belong
to one hardware power domain.  To avoid that confusion, rename struct
dev_power_domain to struct dev_pm_domain and rename the related
pointers in struct device and struct pm_clk_notifier_block from
pwr_domain to pm_domain.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Kevin Hilman <khilman@ti.com>
---
 Documentation/power/devices.txt          |    8 ++++----
 arch/arm/mach-omap1/pm_bus.c             |    8 ++++----
 arch/arm/mach-shmobile/pm_runtime.c      |    8 ++++----
 arch/arm/plat-omap/omap_device.c         |    4 ++--
 arch/sh/kernel/cpu/shmobile/pm_runtime.c |    6 +++---
 drivers/base/power/clock_ops.c           |   14 +++++++-------
 drivers/base/power/main.c                |   30 +++++++++++++++---------------
 drivers/base/power/runtime.c             |   12 ++++++------
 include/linux/device.h                   |    4 ++--
 include/linux/pm.h                       |    2 +-
 include/linux/pm_runtime.h               |    2 +-
 11 files changed, 49 insertions(+), 49 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
@@ -49,20 +49,20 @@ static int omap1_pm_runtime_resume(struc
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = omap1_pm_runtime_suspend,
 		.runtime_resume = omap1_pm_runtime_resume,
 		USE_PLATFORM_PM_SLEEP_OPS
 	},
 };
-#define OMAP1_PWR_DOMAIN (&default_power_domain)
+#define OMAP1_PM_DOMAIN (&default_pm_domain)
 #else
-#define OMAP1_PWR_DOMAIN NULL
+#define OMAP1_PM_DOMAIN NULL
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = OMAP1_PWR_DOMAIN,
+	.pm_domain = OMAP1_PM_DOMAIN,
 	.con_ids = { "ick", "fck", NULL, },
 };
 
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
@@ -28,7 +28,7 @@ static int default_platform_runtime_idle
 	return pm_runtime_suspend(dev);
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = pm_runtime_clk_suspend,
 		.runtime_resume = pm_runtime_clk_resume,
@@ -37,16 +37,16 @@ static struct dev_power_domain default_p
 	},
 };
 
-#define DEFAULT_PWR_DOMAIN_PTR	(&default_power_domain)
+#define DEFAULT_PM_DOMAIN_PTR	(&default_pm_domain)
 
 #else
 
-#define DEFAULT_PWR_DOMAIN_PTR	NULL
+#define DEFAULT_PM_DOMAIN_PTR	NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
 static struct pm_clk_notifier_block platform_bus_notifier = {
-	.pwr_domain = DEFAULT_PWR_DOMAIN_PTR,
+	.pm_domain = DEFAULT_PM_DOMAIN_PTR,
 	.con_ids = { NULL, },
 };
 
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
@@ -564,7 +564,7 @@ static int _od_runtime_resume(struct dev
 	return pm_generic_runtime_resume(dev);
 }
 
-static struct dev_power_domain omap_device_power_domain = {
+static struct dev_pm_domain omap_device_pm_domain = {
 	.ops = {
 		.runtime_suspend = _od_runtime_suspend,
 		.runtime_idle = _od_runtime_idle,
@@ -586,7 +586,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;
+	od->pdev.dev.pm_domain = &omap_device_pm_domain;
 	return platform_device_register(&od->pdev);
 }
 
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
@@ -256,7 +256,7 @@ out:
 	return ret;
 }
 
-static struct dev_power_domain default_power_domain = {
+static struct dev_pm_domain default_pm_domain = {
 	.ops = {
 		.runtime_suspend = default_platform_runtime_suspend,
 		.runtime_resume = default_platform_runtime_resume,
@@ -285,7 +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;
+		dev->pm_domain = &default_pm_domain;
 		break;
 	/* TODO: add BUS_NOTIFY_BIND_DRIVER and increase idle count */
 	case BUS_NOTIFY_BOUND_DRIVER:
@@ -299,7 +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;
+		dev->pm_domain = NULL;
 		break;
 	}
 	return 0;
Index: linux-2.6/Documentation/power/devices.txt
===================================================================
--- linux-2.6.orig/Documentation/power/devices.txt
+++ linux-2.6/Documentation/power/devices.txt
@@ -506,8 +506,8 @@ routines.  Nevertheless, different callb
 situation where it actually matters.
 
 
-Device Power Domains
---------------------
+Device Power Management Domains
+-------------------------------
 Sometimes devices share reference clocks or other power resources.  In those
 cases it generally is not possible to put devices into low-power states
 individually.  Instead, a set of devices sharing a power resource can be put
@@ -516,8 +516,8 @@ power resource.  Of course, they also ne
 together, by turning the shared power resource on.  A set of devices with this
 property is often referred to as a power domain.
 
-Support for power domains is provided through the pwr_domain field of struct
-device.  This field is a pointer to an object of type struct dev_power_domain,
+Support for power domains is provided through the pm_domain field of struct
+device.  This field is a pointer to an object of type struct dev_pm_domain,
 defined in include/linux/pm.h, providing a set of power management callbacks
 analogous to the subsystem-level and device driver callbacks that are executed
 for the given device during all power transitions, instead of the respective
Index: linux-2.6/include/linux/device.h
===================================================================
--- linux-2.6.orig/include/linux/device.h
+++ linux-2.6/include/linux/device.h
@@ -516,7 +516,7 @@ struct device_dma_parameters {
  * 		minimizes board-specific #ifdefs in drivers.
  * @power:	For device power management.
  * 		See Documentation/power/devices.txt for details.
- * @pwr_domain:	Provide callbacks that are executed during system suspend,
+ * @pm_domain:	Provide callbacks that are executed during system suspend,
  * 		hibernation, system resume and during runtime PM transitions
  * 		along with subsystem-level and driver-level callbacks.
  * @numa_node:	NUMA node this device is close to.
@@ -568,7 +568,7 @@ struct device {
 	void		*platform_data;	/* Platform specific data, device
 					   core doesn't touch it */
 	struct dev_pm_info	power;
-	struct dev_power_domain	*pwr_domain;
+	struct dev_pm_domain	*pm_domain;
 
 #ifdef CONFIG_NUMA
 	int		numa_node;	/* NUMA node this device is close to */
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -472,7 +472,7 @@ extern void update_pm_runtime_accounting
  * hibernation, system resume and during runtime PM transitions along with
  * subsystem-level and driver-level callbacks.
  */
-struct dev_power_domain {
+struct dev_pm_domain {
 	struct dev_pm_ops	ops;
 };
 
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
@@ -247,7 +247,7 @@ static inline void pm_runtime_dont_use_a
 
 struct pm_clk_notifier_block {
 	struct notifier_block nb;
-	struct dev_power_domain *pwr_domain;
+	struct dev_pm_domain *pm_domain;
 	char *con_ids[];
 };
 
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
@@ -278,11 +278,11 @@ int pm_runtime_clk_resume(struct device
  *
  * 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
+ * Specifically, the pm_domain member of that object is copied to the device's
+ * pm_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
+ * If the device's pm_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.
  */
@@ -300,14 +300,14 @@ static int pm_runtime_clk_notify(struct
 
 	switch (action) {
 	case BUS_NOTIFY_ADD_DEVICE:
-		if (dev->pwr_domain)
+		if (dev->pm_domain)
 			break;
 
 		error = pm_runtime_clk_init(dev);
 		if (error)
 			break;
 
-		dev->pwr_domain = clknb->pwr_domain;
+		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
 				pm_runtime_clk_add(dev, *con_id);
@@ -317,10 +317,10 @@ static int pm_runtime_clk_notify(struct
 
 		break;
 	case BUS_NOTIFY_DEL_DEVICE:
-		if (dev->pwr_domain != clknb->pwr_domain)
+		if (dev->pm_domain != clknb->pm_domain)
 			break;
 
-		dev->pwr_domain = NULL;
+		dev->pm_domain = NULL;
 		pm_runtime_clk_destroy(dev);
 		break;
 	}
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,9 +425,9 @@ static int device_resume_noirq(struct de
 	TRACE_DEVICE(dev);
 	TRACE_RESUME(0);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "EARLY power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_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);
@@ -521,9 +521,9 @@ static int device_resume(struct device *
 	if (!dev->power.is_suspended)
 		goto Unlock;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -641,10 +641,10 @@ static void device_complete(struct devic
 {
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "completing power domain ");
-		if (dev->pwr_domain->ops.complete)
-			dev->pwr_domain->ops.complete(dev);
+		if (dev->pm_domain->ops.complete)
+			dev->pm_domain->ops.complete(dev);
 	} else if (dev->type && dev->type->pm) {
 		pm_dev_dbg(dev, state, "completing type ");
 		if (dev->type->pm->complete)
@@ -744,9 +744,9 @@ static int device_suspend_noirq(struct d
 {
 	int error;
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "LATE power domain ");
-		error = pm_noirq_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_noirq_op(dev, &dev->pm_domain->ops, state);
 		if (error)
 			return error;
 	} else if (dev->type && dev->type->pm) {
@@ -853,9 +853,9 @@ static int __device_suspend(struct devic
 		goto Unlock;
 	}
 
-	if (dev->pwr_domain) {
+	if (dev->pm_domain) {
 		pm_dev_dbg(dev, state, "power domain ");
-		error = pm_op(dev, &dev->pwr_domain->ops, state);
+		error = pm_op(dev, &dev->pm_domain->ops, state);
 		goto End;
 	}
 
@@ -982,11 +982,11 @@ static int device_prepare(struct device
 
 	device_lock(dev);
 
-	if (dev->pwr_domain) {
+	if (dev->pm_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 (dev->pm_domain->ops.prepare)
+			error = dev->pm_domain->ops.prepare(dev);
+		suspend_report_result(dev->pm_domain->ops.prepare, error);
 		if (error)
 			goto End;
 	} else if (dev->type && dev->type->pm) {
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
@@ -213,8 +213,8 @@ static int rpm_idle(struct device *dev,
 
 	dev->power.idle_notification = true;
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_idle;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_idle;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_idle;
 	else if (dev->class && dev->class->pm)
@@ -374,8 +374,8 @@ static int rpm_suspend(struct device *de
 
 	__update_runtime_status(dev, RPM_SUSPENDING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_suspend;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_suspend;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_suspend;
 	else if (dev->class && dev->class->pm)
@@ -573,8 +573,8 @@ static int rpm_resume(struct device *dev
 
 	__update_runtime_status(dev, RPM_RESUMING);
 
-	if (dev->pwr_domain)
-		callback = dev->pwr_domain->ops.runtime_resume;
+	if (dev->pm_domain)
+		callback = dev->pm_domain->ops.runtime_resume;
 	else if (dev->type && dev->type->pm)
 		callback = dev->type->pm->runtime_resume;
 	else if (dev->class && dev->class->pm)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 2/10 v6] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:25     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:25 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The subsys_data field of struct dev_pm_info, introduced by commit
1d2b71f61b6a10216274e27b717becf9ae101fc7 (PM / Runtime: Add subsystem
data field to struct dev_pm_info), is going to be used even if
CONFIG_PM_RUNTIME is not set, so move it from under the #ifdef.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 include/linux/pm.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -461,8 +461,8 @@ struct dev_pm_info {
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	void			*subsys_data;  /* Owned by the subsystem. */
 #endif
+	void			*subsys_data;  /* Owned by the subsystem. */
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 2/10 v6] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME
@ 2011-06-25 21:25     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:25 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The subsys_data field of struct dev_pm_info, introduced by commit
1d2b71f61b6a10216274e27b717becf9ae101fc7 (PM / Runtime: Add subsystem
data field to struct dev_pm_info), is going to be used even if
CONFIG_PM_RUNTIME is not set, so move it from under the #ifdef.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 include/linux/pm.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -461,8 +461,8 @@ struct dev_pm_info {
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	void			*subsys_data;  /* Owned by the subsystem. */
 #endif
+	void			*subsys_data;  /* Owned by the subsystem. */
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 2/10 v6] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (2 preceding siblings ...)
  (?)
@ 2011-06-25 21:25   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:25 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

The subsys_data field of struct dev_pm_info, introduced by commit
1d2b71f61b6a10216274e27b717becf9ae101fc7 (PM / Runtime: Add subsystem
data field to struct dev_pm_info), is going to be used even if
CONFIG_PM_RUNTIME is not set, so move it from under the #ifdef.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 include/linux/pm.h |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -461,8 +461,8 @@ struct dev_pm_info {
 	unsigned long		active_jiffies;
 	unsigned long		suspended_jiffies;
 	unsigned long		accounting_timestamp;
-	void			*subsys_data;  /* Owned by the subsystem. */
 #endif
+	void			*subsys_data;  /* Owned by the subsystem. */
 };
 
 extern void update_pm_runtime_accounting(struct device *dev);

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:26     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:26 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  494 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 ++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 577 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
=================================--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
=================================--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count = 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (drv && drv->pm && drv->pm->runtime_suspend) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (drv && drv->pm && drv->pm->runtime_resume) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		drv->pm->runtime_resume(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count = 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev = dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain = new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
=================================--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
@ 2011-06-25 21:26     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:26 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  494 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 ++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 577 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (drv && drv->pm && drv->pm->runtime_suspend) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (drv && drv->pm && drv->pm->runtime_resume) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		drv->pm->runtime_resume(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (4 preceding siblings ...)
  (?)
@ 2011-06-25 21:26   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:26 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  494 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 ++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 577 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (drv && drv->pm && drv->pm->runtime_suspend) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (drv && drv->pm && drv->pm->runtime_resume) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		drv->pm->runtime_resume(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock(&new_subdomain->lock);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock(&subdomain->lock);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 4/10 v6] PM: Introduce generic "noirq" callback routines for subsystems (v2)
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:27     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:27 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce generic "noirq" power management callback routines for
subsystems in addition to the "regular" generic PM callback routines.

The new routines will be used, among other things, for implementing
system-wide PM transitions support for generic PM domains.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/runtime_pm.txt |   32 +++++++++++-
 drivers/base/power/generic_ops.c   |   98 +++++++++++++++++++++++++++++++------
 include/linux/pm.h                 |    6 ++
 3 files changed, 119 insertions(+), 17 deletions(-)

Index: linux-2.6/drivers/base/power/generic_ops.c
=================================--- linux-2.6.orig/drivers/base/power/generic_ops.c
+++ linux-2.6/drivers/base/power/generic_ops.c
@@ -94,12 +94,13 @@ int pm_generic_prepare(struct device *de
  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * If the device has not been suspended at run time, execute the
  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  * return its error code.  Otherwise, return zero.
  */
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -109,16 +110,16 @@ static int __pm_generic_call(struct devi
 
 	switch (event) {
 	case PM_EVENT_SUSPEND:
-		callback = pm->suspend;
+		callback = noirq ? pm->suspend_noirq : pm->suspend;
 		break;
 	case PM_EVENT_FREEZE:
-		callback = pm->freeze;
+		callback = noirq ? pm->freeze_noirq : pm->freeze;
 		break;
 	case PM_EVENT_HIBERNATE:
-		callback = pm->poweroff;
+		callback = noirq ? pm->poweroff_noirq : pm->poweroff;
 		break;
 	case PM_EVENT_THAW:
-		callback = pm->thaw;
+		callback = noirq ? pm->thaw_noirq : pm->thaw;
 		break;
 	default:
 		callback = NULL;
@@ -129,42 +130,82 @@ static int __pm_generic_call(struct devi
 }
 
 /**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
  * pm_generic_suspend - Generic suspend callback for subsystems.
  * @dev: Device to suspend.
  */
 int pm_generic_suspend(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_suspend);
 
 /**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
  * pm_generic_freeze - Generic freeze callback for subsystems.
  * @dev: Device to freeze.
  */
 int pm_generic_freeze(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_FREEZE);
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_freeze);
 
 /**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
  * pm_generic_poweroff - Generic poweroff callback for subsystems.
  * @dev: Device to handle.
  */
 int pm_generic_poweroff(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
 
 /**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
  * pm_generic_thaw - Generic thaw callback for subsystems.
  * @dev: Device to thaw.
  */
 int pm_generic_thaw(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_THAW);
+	return __pm_generic_call(dev, PM_EVENT_THAW, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_thaw);
 
@@ -172,12 +213,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
  * __pm_generic_resume - Generic resume/restore callback for subsystems.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * Execute the resume/resotre callback provided by the @dev's driver, if
  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
  * Return the callback's error code.
  */
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -188,10 +230,10 @@ static int __pm_generic_resume(struct de
 
 	switch (event) {
 	case PM_EVENT_RESUME:
-		callback = pm->resume;
+		callback = noirq ? pm->resume_noirq : pm->resume;
 		break;
 	case PM_EVENT_RESTORE:
-		callback = pm->restore;
+		callback = noirq ? pm->restore_noirq : pm->restore;
 		break;
 	default:
 		callback = NULL;
@@ -202,7 +244,7 @@ static int __pm_generic_resume(struct de
 		return 0;
 
 	ret = callback(dev);
-	if (!ret && pm_runtime_enabled(dev)) {
+	if (!ret && !noirq && pm_runtime_enabled(dev)) {
 		pm_runtime_disable(dev);
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
@@ -212,22 +254,42 @@ static int __pm_generic_resume(struct de
 }
 
 /**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
  * pm_generic_resume - Generic resume callback for subsystems.
  * @dev: Device to resume.
  */
 int pm_generic_resume(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESUME);
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_resume);
 
 /**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
  * pm_generic_restore - Generic restore callback for subsystems.
  * @dev: Device to restore.
  */
 int pm_generic_restore(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_restore);
 
@@ -256,11 +318,17 @@ struct dev_pm_ops generic_subsys_pm_ops
 #ifdef CONFIG_PM_SLEEP
 	.prepare = pm_generic_prepare,
 	.suspend = pm_generic_suspend,
+	.suspend_noirq = pm_generic_suspend_noirq,
 	.resume = pm_generic_resume,
+	.resume_noirq = pm_generic_resume_noirq,
 	.freeze = pm_generic_freeze,
+	.freeze_noirq = pm_generic_freeze_noirq,
 	.thaw = pm_generic_thaw,
+	.thaw_noirq = pm_generic_thaw_noirq,
 	.poweroff = pm_generic_poweroff,
+	.poweroff_noirq = pm_generic_poweroff_noirq,
 	.restore = pm_generic_restore,
+	.restore_noirq = pm_generic_restore_noirq,
 	.complete = pm_generic_complete,
 #endif
 #ifdef CONFIG_PM_RUNTIME
Index: linux-2.6/include/linux/pm.h
=================================--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -553,11 +553,17 @@ extern void __suspend_report_result(cons
 extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
 
 extern int pm_generic_prepare(struct device *dev);
+extern int pm_generic_suspend_noirq(struct device *dev);
 extern int pm_generic_suspend(struct device *dev);
+extern int pm_generic_resume_noirq(struct device *dev);
 extern int pm_generic_resume(struct device *dev);
+extern int pm_generic_freeze_noirq(struct device *dev);
 extern int pm_generic_freeze(struct device *dev);
+extern int pm_generic_thaw_noirq(struct device *dev);
 extern int pm_generic_thaw(struct device *dev);
+extern int pm_generic_restore_noirq(struct device *dev);
 extern int pm_generic_restore(struct device *dev);
+extern int pm_generic_poweroff_noirq(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 
Index: linux-2.6/Documentation/power/runtime_pm.txt
=================================--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -590,32 +590,60 @@ driver/base/power/generic_ops.c:
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_suspend_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_resume(struct device *dev);
     - invoke the ->resume() callback provided by the driver of this device and,
       if successful, change the device's runtime PM status to 'active'
 
+  int pm_generic_resume_noirq(struct device *dev);
+    - invoke the ->resume_noirq() callback provided by the driver of this device
+
   int pm_generic_freeze(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->freeze()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_freeze_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_thaw(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->thaw()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_thaw_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_poweroff(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->poweroff()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_poweroff_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_restore(struct device *dev);
     - invoke the ->restore() callback provided by the driver of this device and,
       if successful, change the device's runtime PM status to 'active'
 
+  int pm_generic_restore_noirq(struct device *dev);
+    - invoke the ->restore_noirq() callback provided by the device's driver
+
 These functions can be assigned to the ->runtime_idle(), ->runtime_suspend(),
-->runtime_resume(), ->suspend(), ->resume(), ->freeze(), ->thaw(), ->poweroff(),
-or ->restore() callback pointers in the subsystem-level dev_pm_ops structures.
+->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(),
+->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(),
+->poweroff(), ->poweroff_noirq(), ->restore(), ->restore_noirq() callback
+pointers in the subsystem-level dev_pm_ops structures.
 
 If a subsystem wishes to use all of them at the same time, it can simply assign
 the GENERIC_SUBSYS_PM_OPS macro, defined in include/linux/pm.h, to its


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 4/10 v6] PM: Introduce generic "noirq" callback routines for subsystems (v2)
@ 2011-06-25 21:27     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:27 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce generic "noirq" power management callback routines for
subsystems in addition to the "regular" generic PM callback routines.

The new routines will be used, among other things, for implementing
system-wide PM transitions support for generic PM domains.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/runtime_pm.txt |   32 +++++++++++-
 drivers/base/power/generic_ops.c   |   98 +++++++++++++++++++++++++++++++------
 include/linux/pm.h                 |    6 ++
 3 files changed, 119 insertions(+), 17 deletions(-)

Index: linux-2.6/drivers/base/power/generic_ops.c
===================================================================
--- linux-2.6.orig/drivers/base/power/generic_ops.c
+++ linux-2.6/drivers/base/power/generic_ops.c
@@ -94,12 +94,13 @@ int pm_generic_prepare(struct device *de
  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * If the device has not been suspended at run time, execute the
  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  * return its error code.  Otherwise, return zero.
  */
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -109,16 +110,16 @@ static int __pm_generic_call(struct devi
 
 	switch (event) {
 	case PM_EVENT_SUSPEND:
-		callback = pm->suspend;
+		callback = noirq ? pm->suspend_noirq : pm->suspend;
 		break;
 	case PM_EVENT_FREEZE:
-		callback = pm->freeze;
+		callback = noirq ? pm->freeze_noirq : pm->freeze;
 		break;
 	case PM_EVENT_HIBERNATE:
-		callback = pm->poweroff;
+		callback = noirq ? pm->poweroff_noirq : pm->poweroff;
 		break;
 	case PM_EVENT_THAW:
-		callback = pm->thaw;
+		callback = noirq ? pm->thaw_noirq : pm->thaw;
 		break;
 	default:
 		callback = NULL;
@@ -129,42 +130,82 @@ static int __pm_generic_call(struct devi
 }
 
 /**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
  * pm_generic_suspend - Generic suspend callback for subsystems.
  * @dev: Device to suspend.
  */
 int pm_generic_suspend(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_suspend);
 
 /**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
  * pm_generic_freeze - Generic freeze callback for subsystems.
  * @dev: Device to freeze.
  */
 int pm_generic_freeze(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_FREEZE);
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_freeze);
 
 /**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
  * pm_generic_poweroff - Generic poweroff callback for subsystems.
  * @dev: Device to handle.
  */
 int pm_generic_poweroff(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
 
 /**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
  * pm_generic_thaw - Generic thaw callback for subsystems.
  * @dev: Device to thaw.
  */
 int pm_generic_thaw(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_THAW);
+	return __pm_generic_call(dev, PM_EVENT_THAW, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_thaw);
 
@@ -172,12 +213,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
  * __pm_generic_resume - Generic resume/restore callback for subsystems.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * Execute the resume/resotre callback provided by the @dev's driver, if
  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
  * Return the callback's error code.
  */
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -188,10 +230,10 @@ static int __pm_generic_resume(struct de
 
 	switch (event) {
 	case PM_EVENT_RESUME:
-		callback = pm->resume;
+		callback = noirq ? pm->resume_noirq : pm->resume;
 		break;
 	case PM_EVENT_RESTORE:
-		callback = pm->restore;
+		callback = noirq ? pm->restore_noirq : pm->restore;
 		break;
 	default:
 		callback = NULL;
@@ -202,7 +244,7 @@ static int __pm_generic_resume(struct de
 		return 0;
 
 	ret = callback(dev);
-	if (!ret && pm_runtime_enabled(dev)) {
+	if (!ret && !noirq && pm_runtime_enabled(dev)) {
 		pm_runtime_disable(dev);
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
@@ -212,22 +254,42 @@ static int __pm_generic_resume(struct de
 }
 
 /**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
  * pm_generic_resume - Generic resume callback for subsystems.
  * @dev: Device to resume.
  */
 int pm_generic_resume(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESUME);
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_resume);
 
 /**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
  * pm_generic_restore - Generic restore callback for subsystems.
  * @dev: Device to restore.
  */
 int pm_generic_restore(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_restore);
 
@@ -256,11 +318,17 @@ struct dev_pm_ops generic_subsys_pm_ops
 #ifdef CONFIG_PM_SLEEP
 	.prepare = pm_generic_prepare,
 	.suspend = pm_generic_suspend,
+	.suspend_noirq = pm_generic_suspend_noirq,
 	.resume = pm_generic_resume,
+	.resume_noirq = pm_generic_resume_noirq,
 	.freeze = pm_generic_freeze,
+	.freeze_noirq = pm_generic_freeze_noirq,
 	.thaw = pm_generic_thaw,
+	.thaw_noirq = pm_generic_thaw_noirq,
 	.poweroff = pm_generic_poweroff,
+	.poweroff_noirq = pm_generic_poweroff_noirq,
 	.restore = pm_generic_restore,
+	.restore_noirq = pm_generic_restore_noirq,
 	.complete = pm_generic_complete,
 #endif
 #ifdef CONFIG_PM_RUNTIME
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -553,11 +553,17 @@ extern void __suspend_report_result(cons
 extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
 
 extern int pm_generic_prepare(struct device *dev);
+extern int pm_generic_suspend_noirq(struct device *dev);
 extern int pm_generic_suspend(struct device *dev);
+extern int pm_generic_resume_noirq(struct device *dev);
 extern int pm_generic_resume(struct device *dev);
+extern int pm_generic_freeze_noirq(struct device *dev);
 extern int pm_generic_freeze(struct device *dev);
+extern int pm_generic_thaw_noirq(struct device *dev);
 extern int pm_generic_thaw(struct device *dev);
+extern int pm_generic_restore_noirq(struct device *dev);
 extern int pm_generic_restore(struct device *dev);
+extern int pm_generic_poweroff_noirq(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 
Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -590,32 +590,60 @@ driver/base/power/generic_ops.c:
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_suspend_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_resume(struct device *dev);
     - invoke the ->resume() callback provided by the driver of this device and,
       if successful, change the device's runtime PM status to 'active'
 
+  int pm_generic_resume_noirq(struct device *dev);
+    - invoke the ->resume_noirq() callback provided by the driver of this device
+
   int pm_generic_freeze(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->freeze()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_freeze_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_thaw(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->thaw()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_thaw_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_poweroff(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->poweroff()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_poweroff_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_restore(struct device *dev);
     - invoke the ->restore() callback provided by the driver of this device and,
       if successful, change the device's runtime PM status to 'active'
 
+  int pm_generic_restore_noirq(struct device *dev);
+    - invoke the ->restore_noirq() callback provided by the device's driver
+
 These functions can be assigned to the ->runtime_idle(), ->runtime_suspend(),
-->runtime_resume(), ->suspend(), ->resume(), ->freeze(), ->thaw(), ->poweroff(),
-or ->restore() callback pointers in the subsystem-level dev_pm_ops structures.
+->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(),
+->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(),
+->poweroff(), ->poweroff_noirq(), ->restore(), ->restore_noirq() callback
+pointers in the subsystem-level dev_pm_ops structures.
 
 If a subsystem wishes to use all of them at the same time, it can simply assign
 the GENERIC_SUBSYS_PM_OPS macro, defined in include/linux/pm.h, to its


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 4/10 v6] PM: Introduce generic "noirq" callback routines for subsystems (v2)
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (6 preceding siblings ...)
  (?)
@ 2011-06-25 21:27   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:27 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Introduce generic "noirq" power management callback routines for
subsystems in addition to the "regular" generic PM callback routines.

The new routines will be used, among other things, for implementing
system-wide PM transitions support for generic PM domains.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 Documentation/power/runtime_pm.txt |   32 +++++++++++-
 drivers/base/power/generic_ops.c   |   98 +++++++++++++++++++++++++++++++------
 include/linux/pm.h                 |    6 ++
 3 files changed, 119 insertions(+), 17 deletions(-)

Index: linux-2.6/drivers/base/power/generic_ops.c
===================================================================
--- linux-2.6.orig/drivers/base/power/generic_ops.c
+++ linux-2.6/drivers/base/power/generic_ops.c
@@ -94,12 +94,13 @@ int pm_generic_prepare(struct device *de
  * __pm_generic_call - Generic suspend/freeze/poweroff/thaw subsystem callback.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * If the device has not been suspended at run time, execute the
  * suspend/freeze/poweroff/thaw callback provided by its driver, if defined, and
  * return its error code.  Otherwise, return zero.
  */
-static int __pm_generic_call(struct device *dev, int event)
+static int __pm_generic_call(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -109,16 +110,16 @@ static int __pm_generic_call(struct devi
 
 	switch (event) {
 	case PM_EVENT_SUSPEND:
-		callback = pm->suspend;
+		callback = noirq ? pm->suspend_noirq : pm->suspend;
 		break;
 	case PM_EVENT_FREEZE:
-		callback = pm->freeze;
+		callback = noirq ? pm->freeze_noirq : pm->freeze;
 		break;
 	case PM_EVENT_HIBERNATE:
-		callback = pm->poweroff;
+		callback = noirq ? pm->poweroff_noirq : pm->poweroff;
 		break;
 	case PM_EVENT_THAW:
-		callback = pm->thaw;
+		callback = noirq ? pm->thaw_noirq : pm->thaw;
 		break;
 	default:
 		callback = NULL;
@@ -129,42 +130,82 @@ static int __pm_generic_call(struct devi
 }
 
 /**
+ * pm_generic_suspend_noirq - Generic suspend_noirq callback for subsystems.
+ * @dev: Device to suspend.
+ */
+int pm_generic_suspend_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_suspend_noirq);
+
+/**
  * pm_generic_suspend - Generic suspend callback for subsystems.
  * @dev: Device to suspend.
  */
 int pm_generic_suspend(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_SUSPEND);
+	return __pm_generic_call(dev, PM_EVENT_SUSPEND, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_suspend);
 
 /**
+ * pm_generic_freeze_noirq - Generic freeze_noirq callback for subsystems.
+ * @dev: Device to freeze.
+ */
+int pm_generic_freeze_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_freeze_noirq);
+
+/**
  * pm_generic_freeze - Generic freeze callback for subsystems.
  * @dev: Device to freeze.
  */
 int pm_generic_freeze(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_FREEZE);
+	return __pm_generic_call(dev, PM_EVENT_FREEZE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_freeze);
 
 /**
+ * pm_generic_poweroff_noirq - Generic poweroff_noirq callback for subsystems.
+ * @dev: Device to handle.
+ */
+int pm_generic_poweroff_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_poweroff_noirq);
+
+/**
  * pm_generic_poweroff - Generic poweroff callback for subsystems.
  * @dev: Device to handle.
  */
 int pm_generic_poweroff(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_HIBERNATE);
+	return __pm_generic_call(dev, PM_EVENT_HIBERNATE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_poweroff);
 
 /**
+ * pm_generic_thaw_noirq - Generic thaw_noirq callback for subsystems.
+ * @dev: Device to thaw.
+ */
+int pm_generic_thaw_noirq(struct device *dev)
+{
+	return __pm_generic_call(dev, PM_EVENT_THAW, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_thaw_noirq);
+
+/**
  * pm_generic_thaw - Generic thaw callback for subsystems.
  * @dev: Device to thaw.
  */
 int pm_generic_thaw(struct device *dev)
 {
-	return __pm_generic_call(dev, PM_EVENT_THAW);
+	return __pm_generic_call(dev, PM_EVENT_THAW, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_thaw);
 
@@ -172,12 +213,13 @@ EXPORT_SYMBOL_GPL(pm_generic_thaw);
  * __pm_generic_resume - Generic resume/restore callback for subsystems.
  * @dev: Device to handle.
  * @event: PM transition of the system under way.
+ * @bool: Whether or not this is the "noirq" stage.
  *
  * Execute the resume/resotre callback provided by the @dev's driver, if
  * defined.  If it returns 0, change the device's runtime PM status to 'active'.
  * Return the callback's error code.
  */
-static int __pm_generic_resume(struct device *dev, int event)
+static int __pm_generic_resume(struct device *dev, int event, bool noirq)
 {
 	const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
 	int (*callback)(struct device *);
@@ -188,10 +230,10 @@ static int __pm_generic_resume(struct de
 
 	switch (event) {
 	case PM_EVENT_RESUME:
-		callback = pm->resume;
+		callback = noirq ? pm->resume_noirq : pm->resume;
 		break;
 	case PM_EVENT_RESTORE:
-		callback = pm->restore;
+		callback = noirq ? pm->restore_noirq : pm->restore;
 		break;
 	default:
 		callback = NULL;
@@ -202,7 +244,7 @@ static int __pm_generic_resume(struct de
 		return 0;
 
 	ret = callback(dev);
-	if (!ret && pm_runtime_enabled(dev)) {
+	if (!ret && !noirq && pm_runtime_enabled(dev)) {
 		pm_runtime_disable(dev);
 		pm_runtime_set_active(dev);
 		pm_runtime_enable(dev);
@@ -212,22 +254,42 @@ static int __pm_generic_resume(struct de
 }
 
 /**
+ * pm_generic_resume_noirq - Generic resume_noirq callback for subsystems.
+ * @dev: Device to resume.
+ */
+int pm_generic_resume_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_resume_noirq);
+
+/**
  * pm_generic_resume - Generic resume callback for subsystems.
  * @dev: Device to resume.
  */
 int pm_generic_resume(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESUME);
+	return __pm_generic_resume(dev, PM_EVENT_RESUME, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_resume);
 
 /**
+ * pm_generic_restore_noirq - Generic restore_noirq callback for subsystems.
+ * @dev: Device to restore.
+ */
+int pm_generic_restore_noirq(struct device *dev)
+{
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, true);
+}
+EXPORT_SYMBOL_GPL(pm_generic_restore_noirq);
+
+/**
  * pm_generic_restore - Generic restore callback for subsystems.
  * @dev: Device to restore.
  */
 int pm_generic_restore(struct device *dev)
 {
-	return __pm_generic_resume(dev, PM_EVENT_RESTORE);
+	return __pm_generic_resume(dev, PM_EVENT_RESTORE, false);
 }
 EXPORT_SYMBOL_GPL(pm_generic_restore);
 
@@ -256,11 +318,17 @@ struct dev_pm_ops generic_subsys_pm_ops
 #ifdef CONFIG_PM_SLEEP
 	.prepare = pm_generic_prepare,
 	.suspend = pm_generic_suspend,
+	.suspend_noirq = pm_generic_suspend_noirq,
 	.resume = pm_generic_resume,
+	.resume_noirq = pm_generic_resume_noirq,
 	.freeze = pm_generic_freeze,
+	.freeze_noirq = pm_generic_freeze_noirq,
 	.thaw = pm_generic_thaw,
+	.thaw_noirq = pm_generic_thaw_noirq,
 	.poweroff = pm_generic_poweroff,
+	.poweroff_noirq = pm_generic_poweroff_noirq,
 	.restore = pm_generic_restore,
+	.restore_noirq = pm_generic_restore_noirq,
 	.complete = pm_generic_complete,
 #endif
 #ifdef CONFIG_PM_RUNTIME
Index: linux-2.6/include/linux/pm.h
===================================================================
--- linux-2.6.orig/include/linux/pm.h
+++ linux-2.6/include/linux/pm.h
@@ -553,11 +553,17 @@ extern void __suspend_report_result(cons
 extern int device_pm_wait_for_dev(struct device *sub, struct device *dev);
 
 extern int pm_generic_prepare(struct device *dev);
+extern int pm_generic_suspend_noirq(struct device *dev);
 extern int pm_generic_suspend(struct device *dev);
+extern int pm_generic_resume_noirq(struct device *dev);
 extern int pm_generic_resume(struct device *dev);
+extern int pm_generic_freeze_noirq(struct device *dev);
 extern int pm_generic_freeze(struct device *dev);
+extern int pm_generic_thaw_noirq(struct device *dev);
 extern int pm_generic_thaw(struct device *dev);
+extern int pm_generic_restore_noirq(struct device *dev);
 extern int pm_generic_restore(struct device *dev);
+extern int pm_generic_poweroff_noirq(struct device *dev);
 extern int pm_generic_poweroff(struct device *dev);
 extern void pm_generic_complete(struct device *dev);
 
Index: linux-2.6/Documentation/power/runtime_pm.txt
===================================================================
--- linux-2.6.orig/Documentation/power/runtime_pm.txt
+++ linux-2.6/Documentation/power/runtime_pm.txt
@@ -590,32 +590,60 @@ driver/base/power/generic_ops.c:
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_suspend_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->suspend_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_resume(struct device *dev);
     - invoke the ->resume() callback provided by the driver of this device and,
       if successful, change the device's runtime PM status to 'active'
 
+  int pm_generic_resume_noirq(struct device *dev);
+    - invoke the ->resume_noirq() callback provided by the driver of this device
+
   int pm_generic_freeze(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->freeze()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_freeze_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->freeze_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_thaw(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->thaw()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_thaw_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", invoke the ->thaw_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_poweroff(struct device *dev);
     - if the device has not been suspended at run time, invoke the ->poweroff()
       callback provided by its driver and return its result, or return 0 if not
       defined
 
+  int pm_generic_poweroff_noirq(struct device *dev);
+    - if pm_runtime_suspended(dev) returns "false", run the ->poweroff_noirq()
+      callback provided by the device's driver and return its result, or return
+      0 if not defined
+
   int pm_generic_restore(struct device *dev);
     - invoke the ->restore() callback provided by the driver of this device and,
       if successful, change the device's runtime PM status to 'active'
 
+  int pm_generic_restore_noirq(struct device *dev);
+    - invoke the ->restore_noirq() callback provided by the device's driver
+
 These functions can be assigned to the ->runtime_idle(), ->runtime_suspend(),
-->runtime_resume(), ->suspend(), ->resume(), ->freeze(), ->thaw(), ->poweroff(),
-or ->restore() callback pointers in the subsystem-level dev_pm_ops structures.
+->runtime_resume(), ->suspend(), ->suspend_noirq(), ->resume(),
+->resume_noirq(), ->freeze(), ->freeze_noirq(), ->thaw(), ->thaw_noirq(),
+->poweroff(), ->poweroff_noirq(), ->restore(), ->restore_noirq() callback
+pointers in the subsystem-level dev_pm_ops structures.
 
 If a subsystem wishes to use all of them at the same time, it can simply assign
 the GENERIC_SUBSYS_PM_OPS macro, defined in include/linux/pm.h, to its

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 5/10 v6] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME (v2)
  2011-06-25 21:24   ` Rafael J. Wysocki
  (?)
@ 2011-06-25 21:27     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:27 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

There is some code in drivers/base/power/domain.c that will be useful
for both runtime PM and system-wide power transitions, so make it
depend on CONFIG_PM instead of CONFIG_PM_RUNTIME.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  120 +++++++++++++++++++++++---------------------
 1 file changed, 65 insertions(+), 55 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -14,7 +14,15 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return ERR_PTR(-EINVAL);
+
+	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+}
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
 {
@@ -23,6 +31,58 @@ static void genpd_sd_counter_dec(struct
 }
 
 /**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
+/**
  * __pm_genpd_save_device - Save the pre-suspend state of a device.
  * @dle: Device list entry of the device to save the state of.
  * @genpd: PM domain the device belongs to.
@@ -174,11 +234,10 @@ static int pm_genpd_runtime_suspend(stru
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	if (genpd->parent)
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
@@ -201,54 +260,6 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int pm_genpd_poweron(struct generic_pm_domain *genpd)
-{
-	int ret = 0;
-
- start:
-	if (genpd->parent)
-		mutex_lock(&genpd->parent->lock);
-	mutex_lock(&genpd->lock);
-
-	if (!genpd->power_is_off)
-		goto out;
-
-	if (genpd->parent && genpd->parent->power_is_off) {
-		mutex_unlock(&genpd->lock);
-		mutex_unlock(&genpd->parent->lock);
-
-		ret = pm_genpd_poweron(genpd->parent);
-		if (ret)
-			return ret;
-
-		goto start;
-	}
-
-	if (genpd->power_on) {
-		int ret = genpd->power_on(genpd);
-		if (ret)
-			goto out;
-	}
-
-	genpd->power_is_off = false;
-	if (genpd->parent)
-		genpd->parent->sd_count++;
-
- out:
-	mutex_unlock(&genpd->lock);
-	if (genpd->parent)
-		mutex_unlock(&genpd->parent->lock);
-
-	return ret;
-}
-
-/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -264,11 +275,10 @@ static int pm_genpd_runtime_resume(struc
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	ret = pm_genpd_poweron(genpd);
 	if (ret)
 		return ret;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 5/10 v6] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME (v2)
@ 2011-06-25 21:27     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:27 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

There is some code in drivers/base/power/domain.c that will be useful
for both runtime PM and system-wide power transitions, so make it
depend on CONFIG_PM instead of CONFIG_PM_RUNTIME.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  120 +++++++++++++++++++++++---------------------
 1 file changed, 65 insertions(+), 55 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -14,7 +14,15 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return ERR_PTR(-EINVAL);
+
+	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+}
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
 {
@@ -23,6 +31,58 @@ static void genpd_sd_counter_dec(struct
 }
 
 /**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
+/**
  * __pm_genpd_save_device - Save the pre-suspend state of a device.
  * @dle: Device list entry of the device to save the state of.
  * @genpd: PM domain the device belongs to.
@@ -174,11 +234,10 @@ static int pm_genpd_runtime_suspend(stru
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	if (genpd->parent)
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
@@ -201,54 +260,6 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int pm_genpd_poweron(struct generic_pm_domain *genpd)
-{
-	int ret = 0;
-
- start:
-	if (genpd->parent)
-		mutex_lock(&genpd->parent->lock);
-	mutex_lock(&genpd->lock);
-
-	if (!genpd->power_is_off)
-		goto out;
-
-	if (genpd->parent && genpd->parent->power_is_off) {
-		mutex_unlock(&genpd->lock);
-		mutex_unlock(&genpd->parent->lock);
-
-		ret = pm_genpd_poweron(genpd->parent);
-		if (ret)
-			return ret;
-
-		goto start;
-	}
-
-	if (genpd->power_on) {
-		int ret = genpd->power_on(genpd);
-		if (ret)
-			goto out;
-	}
-
-	genpd->power_is_off = false;
-	if (genpd->parent)
-		genpd->parent->sd_count++;
-
- out:
-	mutex_unlock(&genpd->lock);
-	if (genpd->parent)
-		mutex_unlock(&genpd->parent->lock);
-
-	return ret;
-}
-
-/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -264,11 +275,10 @@ static int pm_genpd_runtime_resume(struc
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	ret = pm_genpd_poweron(genpd);
 	if (ret)
 		return ret;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 5/10 v6] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME (v2)
@ 2011-06-25 21:27     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:27 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

There is some code in drivers/base/power/domain.c that will be useful
for both runtime PM and system-wide power transitions, so make it
depend on CONFIG_PM instead of CONFIG_PM_RUNTIME.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  120 +++++++++++++++++++++++---------------------
 1 file changed, 65 insertions(+), 55 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -14,7 +14,15 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
+
+static struct generic_pm_domain *dev_to_genpd(struct device *dev)
+{
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return ERR_PTR(-EINVAL);
+
+	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+}
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
 {
@@ -23,6 +31,58 @@ static void genpd_sd_counter_dec(struct
 }
 
 /**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock(&genpd->lock);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
+/**
  * __pm_genpd_save_device - Save the pre-suspend state of a device.
  * @dle: Device list entry of the device to save the state of.
  * @genpd: PM domain the device belongs to.
@@ -174,11 +234,10 @@ static int pm_genpd_runtime_suspend(stru
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	if (genpd->parent)
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
@@ -201,54 +260,6 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
- * pm_genpd_poweron - Restore power to a given PM domain and its parents.
- * @genpd: PM domain to power up.
- *
- * Restore power to @genpd and all of its parents so that it is possible to
- * resume a device belonging to it.
- */
-static int pm_genpd_poweron(struct generic_pm_domain *genpd)
-{
-	int ret = 0;
-
- start:
-	if (genpd->parent)
-		mutex_lock(&genpd->parent->lock);
-	mutex_lock(&genpd->lock);
-
-	if (!genpd->power_is_off)
-		goto out;
-
-	if (genpd->parent && genpd->parent->power_is_off) {
-		mutex_unlock(&genpd->lock);
-		mutex_unlock(&genpd->parent->lock);
-
-		ret = pm_genpd_poweron(genpd->parent);
-		if (ret)
-			return ret;
-
-		goto start;
-	}
-
-	if (genpd->power_on) {
-		int ret = genpd->power_on(genpd);
-		if (ret)
-			goto out;
-	}
-
-	genpd->power_is_off = false;
-	if (genpd->parent)
-		genpd->parent->sd_count++;
-
- out:
-	mutex_unlock(&genpd->lock);
-	if (genpd->parent)
-		mutex_unlock(&genpd->parent->lock);
-
-	return ret;
-}
-
-/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -264,11 +275,10 @@ static int pm_genpd_runtime_resume(struc
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (IS_ERR_OR_NULL(dev->pm_domain))
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
 		return -EINVAL;
 
-	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
-
 	ret = pm_genpd_poweron(genpd);
 	if (ret)
 		return ret;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 6/10 v6] PM / Domains: System-wide transitions support for generic domains (v4)
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:28     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:28 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  546 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 543 insertions(+), 15 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -46,7 +46,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -155,7 +156,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -260,6 +261,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -270,7 +292,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -284,17 +305,7 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
-
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev = dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
-	}
-
-	if (genpd->start_device)
-		genpd->start_device(dev);
-
+	__pm_genpd_runtime_resume(dev, genpd);
 	mutex_unlock(&genpd->lock);
 
 	return 0;
@@ -303,12 +314,488 @@ static int pm_genpd_runtime_resume(struc
 #else
 
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
 
 #define pm_genpd_runtime_suspend	NULL
 #define pm_genpd_runtime_resume		NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ = 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count = 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
+#else
+
+#define pm_genpd_prepare		NULL
+#define pm_genpd_suspend		NULL
+#define pm_genpd_suspend_noirq		NULL
+#define pm_genpd_resume_noirq		NULL
+#define pm_genpd_resume			NULL
+#define pm_genpd_freeze			NULL
+#define pm_genpd_freeze_noirq		NULL
+#define pm_genpd_thaw_noirq		NULL
+#define pm_genpd_thaw			NULL
+#define pm_genpd_dev_poweroff_noirq	NULL
+#define pm_genpd_dev_poweroff		NULL
+#define pm_genpd_restore_noirq		NULL
+#define pm_genpd_restore		NULL
+#define pm_genpd_complete		NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
@@ -331,6 +818,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev = dev) {
 			ret = -EINVAL;
@@ -346,6 +838,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -375,6 +868,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -383,6 +881,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -390,6 +889,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -498,7 +998,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
=================================--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 6/10 v6] PM / Domains: System-wide transitions support for generic domains (v4)
@ 2011-06-25 21:28     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:28 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  546 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 543 insertions(+), 15 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -46,7 +46,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -155,7 +156,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -260,6 +261,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -270,7 +292,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -284,17 +305,7 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
-
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
-	}
-
-	if (genpd->start_device)
-		genpd->start_device(dev);
-
+	__pm_genpd_runtime_resume(dev, genpd);
 	mutex_unlock(&genpd->lock);
 
 	return 0;
@@ -303,12 +314,488 @@ static int pm_genpd_runtime_resume(struc
 #else
 
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
 
 #define pm_genpd_runtime_suspend	NULL
 #define pm_genpd_runtime_resume		NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
+#else
+
+#define pm_genpd_prepare		NULL
+#define pm_genpd_suspend		NULL
+#define pm_genpd_suspend_noirq		NULL
+#define pm_genpd_resume_noirq		NULL
+#define pm_genpd_resume			NULL
+#define pm_genpd_freeze			NULL
+#define pm_genpd_freeze_noirq		NULL
+#define pm_genpd_thaw_noirq		NULL
+#define pm_genpd_thaw			NULL
+#define pm_genpd_dev_poweroff_noirq	NULL
+#define pm_genpd_dev_poweroff		NULL
+#define pm_genpd_restore_noirq		NULL
+#define pm_genpd_restore		NULL
+#define pm_genpd_complete		NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
@@ -331,6 +818,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -346,6 +838,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -375,6 +868,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -383,6 +881,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -390,6 +889,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -498,7 +998,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 6/10 v6] PM / Domains: System-wide transitions support for generic domains (v4)
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (10 preceding siblings ...)
  (?)
@ 2011-06-25 21:28   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:28 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |  546 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 543 insertions(+), 15 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -46,7 +46,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -155,7 +156,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -260,6 +261,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -270,7 +292,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -284,17 +305,7 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
-
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
-	}
-
-	if (genpd->start_device)
-		genpd->start_device(dev);
-
+	__pm_genpd_runtime_resume(dev, genpd);
 	mutex_unlock(&genpd->lock);
 
 	return 0;
@@ -303,12 +314,488 @@ static int pm_genpd_runtime_resume(struc
 #else
 
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
 
 #define pm_genpd_runtime_suspend	NULL
 #define pm_genpd_runtime_resume		NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	pm_runtime_disable(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete)
+		pm_generic_complete(dev);
+
+	pm_runtime_enable(dev);
+}
+
+#else
+
+#define pm_genpd_prepare		NULL
+#define pm_genpd_suspend		NULL
+#define pm_genpd_suspend_noirq		NULL
+#define pm_genpd_resume_noirq		NULL
+#define pm_genpd_resume			NULL
+#define pm_genpd_freeze			NULL
+#define pm_genpd_freeze_noirq		NULL
+#define pm_genpd_thaw_noirq		NULL
+#define pm_genpd_thaw			NULL
+#define pm_genpd_dev_poweroff_noirq	NULL
+#define pm_genpd_dev_poweroff		NULL
+#define pm_genpd_restore_noirq		NULL
+#define pm_genpd_restore		NULL
+#define pm_genpd_complete		NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
@@ -331,6 +818,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -346,6 +838,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -375,6 +868,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -383,6 +881,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -390,6 +889,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -498,7 +998,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:29     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:29 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Devices that are set up to wake up the system from sleep states
should not be stopped and power should not be removed from them
when the system goes into a sleep state.  Make the generic PM domain
code respect that limitation.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -445,6 +445,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -665,6 +668,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-25 21:29     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:29 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Devices that are set up to wake up the system from sleep states
should not be stopped and power should not be removed from them
when the system goes into a sleep state.  Make the generic PM domain
code respect that limitation.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -445,6 +445,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -665,6 +668,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (11 preceding siblings ...)
  (?)
@ 2011-06-25 21:29   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:29 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Devices that are set up to wake up the system from sleep states
should not be stopped and power should not be removed from them
when the system goes into a sleep state.  Make the generic PM domain
code respect that limitation.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -445,6 +445,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -665,6 +668,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 8/10 v6] PM: Allow the clocks management code to be used during system suspend
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:30     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:30 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The common clocks management code in drivers/base/power/clock_ops.c
is going to be used during system-wide power transitions as well as
for runtime PM, so it shouldn't depend on CONFIG_PM_RUNTIME.
However, the suspend/resume functions provided by it for
CONFIG_PM_RUNTIME unset, to be used during system-wide power
transitions, should not behave in the same way as their counterparts
defined for CONFIG_PM_RUNTIME set, because in that case the clocks
are managed differently at run time.

The names of the functions still contain the word "runtime" after
this change, but that is going to be modified by a separate patch
later.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/clock_ops.c |   60 ++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_runtime.h     |    2 -
 kernel/power/Kconfig           |    4 +-
 3 files changed, 62 insertions(+), 4 deletions(-)

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
@@ -251,7 +251,7 @@ struct pm_clk_notifier_block {
 	char *con_ids[];
 };
 
-#ifdef CONFIG_PM_RUNTIME_CLK
+#ifdef CONFIG_PM_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);
Index: linux-2.6/kernel/power/Kconfig
=================================--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -224,9 +224,9 @@ config PM_OPP
 	  implementations a ready to use framework to manage OPPs.
 	  For more information, read <file:Documentation/power/opp.txt>
 
-config PM_RUNTIME_CLK
+config PM_CLK
 	def_bool y
-	depends on PM_RUNTIME && HAVE_CLK
+	depends on PM && HAVE_CLK
 
 config PM_GENERIC_DOMAINS
 	bool
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
@@ -15,7 +15,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 
 struct pm_runtime_clk_data {
 	struct list_head clock_list;
@@ -191,6 +191,10 @@ void pm_runtime_clk_destroy(struct devic
 	kfree(prd);
 }
 
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
 /**
  * pm_runtime_clk_acquire - Acquire a device clock.
  * @dev: Device whose clock is to be acquired.
@@ -330,6 +334,60 @@ static int pm_runtime_clk_notify(struct
 
 #else /* !CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's 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 there is no driver, the clocks are already disabled. */
+	if (!prd || !dev->driver)
+		return 0;
+
+	mutex_lock(&prd->lock);
+
+	list_for_each_entry_reverse(ce, &prd->clock_list, node)
+		clk_disable(ce->clk);
+
+	mutex_unlock(&prd->lock);
+
+	return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's 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 there is no driver, the clocks should remain disabled. */
+	if (!prd || !dev->driver)
+		return 0;
+
+	mutex_lock(&prd->lock);
+
+	list_for_each_entry(ce, &prd->clock_list, node)
+		clk_enable(ce->clk);
+
+	mutex_unlock(&prd->lock);
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
 /**
  * enable_clock - Enable a device clock.
  * @dev: Device whose clock is to be enabled.


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 8/10 v6] PM: Allow the clocks management code to be used during system suspend
@ 2011-06-25 21:30     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:30 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The common clocks management code in drivers/base/power/clock_ops.c
is going to be used during system-wide power transitions as well as
for runtime PM, so it shouldn't depend on CONFIG_PM_RUNTIME.
However, the suspend/resume functions provided by it for
CONFIG_PM_RUNTIME unset, to be used during system-wide power
transitions, should not behave in the same way as their counterparts
defined for CONFIG_PM_RUNTIME set, because in that case the clocks
are managed differently at run time.

The names of the functions still contain the word "runtime" after
this change, but that is going to be modified by a separate patch
later.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/clock_ops.c |   60 ++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_runtime.h     |    2 -
 kernel/power/Kconfig           |    4 +-
 3 files changed, 62 insertions(+), 4 deletions(-)

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
@@ -251,7 +251,7 @@ struct pm_clk_notifier_block {
 	char *con_ids[];
 };
 
-#ifdef CONFIG_PM_RUNTIME_CLK
+#ifdef CONFIG_PM_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);
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -224,9 +224,9 @@ config PM_OPP
 	  implementations a ready to use framework to manage OPPs.
 	  For more information, read <file:Documentation/power/opp.txt>
 
-config PM_RUNTIME_CLK
+config PM_CLK
 	def_bool y
-	depends on PM_RUNTIME && HAVE_CLK
+	depends on PM && HAVE_CLK
 
 config PM_GENERIC_DOMAINS
 	bool
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
@@ -15,7 +15,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 
 struct pm_runtime_clk_data {
 	struct list_head clock_list;
@@ -191,6 +191,10 @@ void pm_runtime_clk_destroy(struct devic
 	kfree(prd);
 }
 
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
 /**
  * pm_runtime_clk_acquire - Acquire a device clock.
  * @dev: Device whose clock is to be acquired.
@@ -330,6 +334,60 @@ static int pm_runtime_clk_notify(struct
 
 #else /* !CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's 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 there is no driver, the clocks are already disabled. */
+	if (!prd || !dev->driver)
+		return 0;
+
+	mutex_lock(&prd->lock);
+
+	list_for_each_entry_reverse(ce, &prd->clock_list, node)
+		clk_disable(ce->clk);
+
+	mutex_unlock(&prd->lock);
+
+	return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's 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 there is no driver, the clocks should remain disabled. */
+	if (!prd || !dev->driver)
+		return 0;
+
+	mutex_lock(&prd->lock);
+
+	list_for_each_entry(ce, &prd->clock_list, node)
+		clk_enable(ce->clk);
+
+	mutex_unlock(&prd->lock);
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
 /**
  * enable_clock - Enable a device clock.
  * @dev: Device whose clock is to be enabled.


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 8/10 v6] PM: Allow the clocks management code to be used during system suspend
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (14 preceding siblings ...)
  (?)
@ 2011-06-25 21:30   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:30 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

The common clocks management code in drivers/base/power/clock_ops.c
is going to be used during system-wide power transitions as well as
for runtime PM, so it shouldn't depend on CONFIG_PM_RUNTIME.
However, the suspend/resume functions provided by it for
CONFIG_PM_RUNTIME unset, to be used during system-wide power
transitions, should not behave in the same way as their counterparts
defined for CONFIG_PM_RUNTIME set, because in that case the clocks
are managed differently at run time.

The names of the functions still contain the word "runtime" after
this change, but that is going to be modified by a separate patch
later.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/clock_ops.c |   60 ++++++++++++++++++++++++++++++++++++++++-
 include/linux/pm_runtime.h     |    2 -
 kernel/power/Kconfig           |    4 +-
 3 files changed, 62 insertions(+), 4 deletions(-)

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
@@ -251,7 +251,7 @@ struct pm_clk_notifier_block {
 	char *con_ids[];
 };
 
-#ifdef CONFIG_PM_RUNTIME_CLK
+#ifdef CONFIG_PM_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);
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -224,9 +224,9 @@ config PM_OPP
 	  implementations a ready to use framework to manage OPPs.
 	  For more information, read <file:Documentation/power/opp.txt>
 
-config PM_RUNTIME_CLK
+config PM_CLK
 	def_bool y
-	depends on PM_RUNTIME && HAVE_CLK
+	depends on PM && HAVE_CLK
 
 config PM_GENERIC_DOMAINS
 	bool
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
@@ -15,7 +15,7 @@
 #include <linux/slab.h>
 #include <linux/err.h>
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 
 struct pm_runtime_clk_data {
 	struct list_head clock_list;
@@ -191,6 +191,10 @@ void pm_runtime_clk_destroy(struct devic
 	kfree(prd);
 }
 
+#endif /* CONFIG_PM */
+
+#ifdef CONFIG_PM_RUNTIME
+
 /**
  * pm_runtime_clk_acquire - Acquire a device clock.
  * @dev: Device whose clock is to be acquired.
@@ -330,6 +334,60 @@ static int pm_runtime_clk_notify(struct
 
 #else /* !CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM
+
+/**
+ * pm_runtime_clk_suspend - Disable clocks in a device's 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 there is no driver, the clocks are already disabled. */
+	if (!prd || !dev->driver)
+		return 0;
+
+	mutex_lock(&prd->lock);
+
+	list_for_each_entry_reverse(ce, &prd->clock_list, node)
+		clk_disable(ce->clk);
+
+	mutex_unlock(&prd->lock);
+
+	return 0;
+}
+
+/**
+ * pm_runtime_clk_resume - Enable clocks in a device's 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 there is no driver, the clocks should remain disabled. */
+	if (!prd || !dev->driver)
+		return 0;
+
+	mutex_lock(&prd->lock);
+
+	list_for_each_entry(ce, &prd->clock_list, node)
+		clk_enable(ce->clk);
+
+	mutex_unlock(&prd->lock);
+
+	return 0;
+}
+
+#endif /* CONFIG_PM */
+
 /**
  * enable_clock - Enable a device clock.
  * @dev: Device whose clock is to be enabled.

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 9/10 v6] PM: Rename clock management functions
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:30     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:30 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The common PM clock management functions may be used for system
suspend/resume as well as for runtime PM, so rename them
accordingly.  Modify kerneldoc comments describing these functions
and kernel messages printed by them, so that they refer to power
management in general rather that to runtime PM.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-omap1/pm_bus.c        |    6 -
 arch/arm/mach-shmobile/pm_runtime.c |    6 -
 drivers/base/power/clock_ops.c      |  188 ++++++++++++++++++------------------
 include/linux/pm_runtime.h          |   28 ++---
 4 files changed, 114 insertions(+), 114 deletions(-)

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
@@ -17,7 +17,7 @@
 
 #ifdef CONFIG_PM
 
-struct pm_runtime_clk_data {
+struct pm_clk_data {
 	struct list_head clock_list;
 	struct mutex lock;
 };
@@ -36,25 +36,25 @@ struct pm_clock_entry {
 	enum pce_status status;
 };
 
-static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+static struct pm_clk_data *__to_pcd(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.
+ * pm_clk_add - Start using a device clock for power management.
+ * @dev: Device whose clock is going to be used for power management.
  * @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.
+ * the power management of @dev.
  */
-int pm_runtime_clk_add(struct device *dev, const char *con_id)
+int pm_clk_add(struct device *dev, const char *con_id)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
-	if (!prd)
+	if (!pcd)
 		return -EINVAL;
 
 	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
@@ -73,20 +73,20 @@ int pm_runtime_clk_add(struct device *de
 		}
 	}
 
-	mutex_lock(&prd->lock);
-	list_add_tail(&ce->node, &prd->clock_list);
-	mutex_unlock(&prd->lock);
+	mutex_lock(&pcd->lock);
+	list_add_tail(&ce->node, &pcd->clock_list);
+	mutex_unlock(&pcd->lock);
 	return 0;
 }
 
 /**
- * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
- * @ce: Runtime PM clock entry to destroy.
+ * __pm_clk_remove - Destroy PM clock entry.
+ * @ce: 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.
+ * This routine must be called under the mutex protecting the PM list of clocks
+ * corresponding the the @ce's device.
  */
-static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+static void __pm_clk_remove(struct pm_clock_entry *ce)
 {
 	if (!ce)
 		return;
@@ -108,87 +108,87 @@ static void __pm_runtime_clk_remove(stru
 }
 
 /**
- * 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.
+ * pm_clk_remove - Stop using a device clock for power management.
+ * @dev: Device whose clock should not be used for 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.
+ * the power management of @dev.
  */
-void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+void pm_clk_remove(struct device *dev, const char *con_id)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
-	if (!prd)
+	if (!pcd)
 		return;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node) {
+	list_for_each_entry(ce, &pcd->clock_list, node) {
 		if (!con_id && !ce->con_id) {
-			__pm_runtime_clk_remove(ce);
+			__pm_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);
+			__pm_clk_remove(ce);
 			break;
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->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.
+ * pm_clk_init - Initialize a device's list of power management clocks.
+ * @dev: Device to initialize the list of PM clocks for.
  *
- * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * Allocate a struct pm_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)
+int pm_clk_init(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd;
+	struct pm_clk_data *pcd;
 
-	prd = kzalloc(sizeof(*prd), GFP_KERNEL);
-	if (!prd) {
-		dev_err(dev, "Not enough memory fo runtime PM data.\n");
+	pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
+	if (!pcd) {
+		dev_err(dev, "Not enough memory for PM clock data.\n");
 		return -ENOMEM;
 	}
 
-	INIT_LIST_HEAD(&prd->clock_list);
-	mutex_init(&prd->lock);
-	dev->power.subsys_data = prd;
+	INIT_LIST_HEAD(&pcd->clock_list);
+	mutex_init(&pcd->lock);
+	dev->power.subsys_data = pcd;
 	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.
+ * pm_clk_destroy - Destroy a device's list of power management clocks.
+ * @dev: Device to destroy the list of 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
+ * from the struct pm_clk_data object pointed to by it before and free
  * that object.
  */
-void pm_runtime_clk_destroy(struct device *dev)
+void pm_clk_destroy(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce, *c;
 
-	if (!prd)
+	if (!pcd)
 		return;
 
 	dev->power.subsys_data = NULL;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
-		__pm_runtime_clk_remove(ce);
+	list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node)
+		__pm_clk_remove(ce);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
-	kfree(prd);
+	kfree(pcd);
 }
 
 #endif /* CONFIG_PM */
@@ -196,11 +196,11 @@ void pm_runtime_clk_destroy(struct devic
 #ifdef CONFIG_PM_RUNTIME
 
 /**
- * pm_runtime_clk_acquire - Acquire a device clock.
+ * pm_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,
+static void pm_clk_acquire(struct device *dev,
 				    struct pm_clock_entry *ce)
 {
 	ce->clk = clk_get(dev, ce->con_id);
@@ -213,24 +213,24 @@ static void pm_runtime_clk_acquire(struc
 }
 
 /**
- * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
  * @dev: Device to disable the clocks for.
  */
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (!prd)
+	if (!pcd)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+	list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
 		if (ce->status = PCE_STATUS_NONE)
-			pm_runtime_clk_acquire(dev, ce);
+			pm_clk_acquire(dev, ce);
 
 		if (ce->status < PCE_STATUS_ERROR) {
 			clk_disable(ce->clk);
@@ -238,30 +238,30 @@ int pm_runtime_clk_suspend(struct device
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
  * @dev: Device to enable the clocks for.
  */
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (!prd)
+	if (!pcd)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node) {
+	list_for_each_entry(ce, &pcd->clock_list, node) {
 		if (ce->status = PCE_STATUS_NONE)
-			pm_runtime_clk_acquire(dev, ce);
+			pm_clk_acquire(dev, ce);
 
 		if (ce->status < PCE_STATUS_ERROR) {
 			clk_enable(ce->clk);
@@ -269,13 +269,13 @@ int pm_runtime_clk_resume(struct device
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_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.
@@ -284,13 +284,13 @@ int pm_runtime_clk_resume(struct device
  * struct pm_clk_notifier_block containing all of the requisite data.
  * Specifically, the pm_domain member of that object is copied to the device's
  * pm_domain field and its con_ids member is used to populate the device's list
- * of runtime PM clocks, depending on @action.
+ * of PM clocks, depending on @action.
  *
  * If the device's pm_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,
+static int pm_clk_notify(struct notifier_block *nb,
 				 unsigned long action, void *data)
 {
 	struct pm_clk_notifier_block *clknb;
@@ -307,16 +307,16 @@ static int pm_runtime_clk_notify(struct
 		if (dev->pm_domain)
 			break;
 
-		error = pm_runtime_clk_init(dev);
+		error = pm_clk_init(dev);
 		if (error)
 			break;
 
 		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
-				pm_runtime_clk_add(dev, *con_id);
+				pm_clk_add(dev, *con_id);
 		} else {
-			pm_runtime_clk_add(dev, NULL);
+			pm_clk_add(dev, NULL);
 		}
 
 		break;
@@ -325,7 +325,7 @@ static int pm_runtime_clk_notify(struct
 			break;
 
 		dev->pm_domain = NULL;
-		pm_runtime_clk_destroy(dev);
+		pm_clk_destroy(dev);
 		break;
 	}
 
@@ -337,51 +337,51 @@ static int pm_runtime_clk_notify(struct
 #ifdef CONFIG_PM
 
 /**
- * pm_runtime_clk_suspend - Disable clocks in a device's PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
  * @dev: Device to disable the clocks for.
  */
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
 	/* If there is no driver, the clocks are already disabled. */
-	if (!prd || !dev->driver)
+	if (!pcd || !dev->driver)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_reverse(ce, &prd->clock_list, node)
+	list_for_each_entry_reverse(ce, &pcd->clock_list, node)
 		clk_disable(ce->clk);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_resume - Enable clocks in a device's PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
  * @dev: Device to enable the clocks for.
  */
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
 	/* If there is no driver, the clocks should remain disabled. */
-	if (!prd || !dev->driver)
+	if (!pcd || !dev->driver)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node)
+	list_for_each_entry(ce, &pcd->clock_list, node)
 		clk_enable(ce->clk);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
@@ -423,7 +423,7 @@ static void disable_clock(struct device
 }
 
 /**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_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.
@@ -433,7 +433,7 @@ static void disable_clock(struct device
  * 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,
+static int pm_clk_notify(struct notifier_block *nb,
 				 unsigned long action, void *data)
 {
 	struct pm_clk_notifier_block *clknb;
@@ -469,21 +469,21 @@ static int pm_runtime_clk_notify(struct
 #endif /* !CONFIG_PM_RUNTIME */
 
 /**
- * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * pm_clk_add_notifier - Add bus type notifier for power management 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,
+ * notifier_call member will be replaced with pm_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,
+void pm_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;
+	clknb->nb.notifier_call = pm_clk_notify;
 	bus_register_notifier(bus, &clknb->nb);
 }
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
@@ -252,36 +252,36 @@ struct pm_clk_notifier_block {
 };
 
 #ifdef CONFIG_PM_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);
+extern int pm_clk_init(struct device *dev);
+extern void pm_clk_destroy(struct device *dev);
+extern int pm_clk_add(struct device *dev, const char *con_id);
+extern void pm_clk_remove(struct device *dev, const char *con_id);
+extern int pm_clk_suspend(struct device *dev);
+extern int pm_clk_resume(struct device *dev);
 #else
-static inline int pm_runtime_clk_init(struct device *dev)
+static inline int pm_clk_init(struct device *dev)
 {
 	return -EINVAL;
 }
-static inline void pm_runtime_clk_destroy(struct device *dev)
+static inline void pm_clk_destroy(struct device *dev)
 {
 }
-static inline int pm_runtime_clk_add(struct device *dev, const char *con_id)
+static inline int pm_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)
+static inline void pm_clk_remove(struct device *dev, const char *con_id)
 {
 }
-#define pm_runtime_clock_suspend	NULL
-#define pm_runtime_clock_resume		NULL
+#define pm_clk_suspend	NULL
+#define pm_clk_resume	NULL
 #endif
 
 #ifdef CONFIG_HAVE_CLK
-extern void pm_runtime_clk_add_notifier(struct bus_type *bus,
+extern void pm_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,
+static inline void pm_clk_add_notifier(struct bus_type *bus,
 					struct pm_clk_notifier_block *clknb)
 {
 }
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
@@ -32,7 +32,7 @@ static int omap1_pm_runtime_suspend(stru
 	if (ret)
 		return ret;
 
-	ret = pm_runtime_clk_suspend(dev);
+	ret = pm_clk_suspend(dev);
 	if (ret) {
 		pm_generic_runtime_resume(dev);
 		return ret;
@@ -45,7 +45,7 @@ static int omap1_pm_runtime_resume(struc
 {
 	dev_dbg(dev, "%s\n", __func__);
 
-	pm_runtime_clk_resume(dev);
+	pm_clk_resume(dev);
 	return pm_generic_runtime_resume(dev);
 }
 
@@ -71,7 +71,7 @@ static int __init omap1_pm_runtime_init(
 	if (!cpu_class_is_omap1())
 		return -ENODEV;
 
-	pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
 
 	return 0;
 }
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
@@ -30,8 +30,8 @@ static int default_platform_runtime_idle
 
 static struct dev_pm_domain default_pm_domain = {
 	.ops = {
-		.runtime_suspend = pm_runtime_clk_suspend,
-		.runtime_resume = pm_runtime_clk_resume,
+		.runtime_suspend = pm_clk_suspend,
+		.runtime_resume = pm_clk_resume,
 		.runtime_idle = default_platform_runtime_idle,
 		USE_PLATFORM_PM_SLEEP_OPS
 	},
@@ -52,7 +52,7 @@ static struct pm_clk_notifier_block plat
 
 static int __init sh_pm_runtime_init(void)
 {
-	pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
 	return 0;
 }
 core_initcall(sh_pm_runtime_init);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 9/10 v6] PM: Rename clock management functions
@ 2011-06-25 21:30     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:30 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

The common PM clock management functions may be used for system
suspend/resume as well as for runtime PM, so rename them
accordingly.  Modify kerneldoc comments describing these functions
and kernel messages printed by them, so that they refer to power
management in general rather that to runtime PM.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-omap1/pm_bus.c        |    6 -
 arch/arm/mach-shmobile/pm_runtime.c |    6 -
 drivers/base/power/clock_ops.c      |  188 ++++++++++++++++++------------------
 include/linux/pm_runtime.h          |   28 ++---
 4 files changed, 114 insertions(+), 114 deletions(-)

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
@@ -17,7 +17,7 @@
 
 #ifdef CONFIG_PM
 
-struct pm_runtime_clk_data {
+struct pm_clk_data {
 	struct list_head clock_list;
 	struct mutex lock;
 };
@@ -36,25 +36,25 @@ struct pm_clock_entry {
 	enum pce_status status;
 };
 
-static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+static struct pm_clk_data *__to_pcd(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.
+ * pm_clk_add - Start using a device clock for power management.
+ * @dev: Device whose clock is going to be used for power management.
  * @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.
+ * the power management of @dev.
  */
-int pm_runtime_clk_add(struct device *dev, const char *con_id)
+int pm_clk_add(struct device *dev, const char *con_id)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
-	if (!prd)
+	if (!pcd)
 		return -EINVAL;
 
 	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
@@ -73,20 +73,20 @@ int pm_runtime_clk_add(struct device *de
 		}
 	}
 
-	mutex_lock(&prd->lock);
-	list_add_tail(&ce->node, &prd->clock_list);
-	mutex_unlock(&prd->lock);
+	mutex_lock(&pcd->lock);
+	list_add_tail(&ce->node, &pcd->clock_list);
+	mutex_unlock(&pcd->lock);
 	return 0;
 }
 
 /**
- * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
- * @ce: Runtime PM clock entry to destroy.
+ * __pm_clk_remove - Destroy PM clock entry.
+ * @ce: 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.
+ * This routine must be called under the mutex protecting the PM list of clocks
+ * corresponding the the @ce's device.
  */
-static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+static void __pm_clk_remove(struct pm_clock_entry *ce)
 {
 	if (!ce)
 		return;
@@ -108,87 +108,87 @@ static void __pm_runtime_clk_remove(stru
 }
 
 /**
- * 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.
+ * pm_clk_remove - Stop using a device clock for power management.
+ * @dev: Device whose clock should not be used for 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.
+ * the power management of @dev.
  */
-void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+void pm_clk_remove(struct device *dev, const char *con_id)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
-	if (!prd)
+	if (!pcd)
 		return;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node) {
+	list_for_each_entry(ce, &pcd->clock_list, node) {
 		if (!con_id && !ce->con_id) {
-			__pm_runtime_clk_remove(ce);
+			__pm_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);
+			__pm_clk_remove(ce);
 			break;
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->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.
+ * pm_clk_init - Initialize a device's list of power management clocks.
+ * @dev: Device to initialize the list of PM clocks for.
  *
- * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * Allocate a struct pm_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)
+int pm_clk_init(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd;
+	struct pm_clk_data *pcd;
 
-	prd = kzalloc(sizeof(*prd), GFP_KERNEL);
-	if (!prd) {
-		dev_err(dev, "Not enough memory fo runtime PM data.\n");
+	pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
+	if (!pcd) {
+		dev_err(dev, "Not enough memory for PM clock data.\n");
 		return -ENOMEM;
 	}
 
-	INIT_LIST_HEAD(&prd->clock_list);
-	mutex_init(&prd->lock);
-	dev->power.subsys_data = prd;
+	INIT_LIST_HEAD(&pcd->clock_list);
+	mutex_init(&pcd->lock);
+	dev->power.subsys_data = pcd;
 	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.
+ * pm_clk_destroy - Destroy a device's list of power management clocks.
+ * @dev: Device to destroy the list of 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
+ * from the struct pm_clk_data object pointed to by it before and free
  * that object.
  */
-void pm_runtime_clk_destroy(struct device *dev)
+void pm_clk_destroy(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce, *c;
 
-	if (!prd)
+	if (!pcd)
 		return;
 
 	dev->power.subsys_data = NULL;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
-		__pm_runtime_clk_remove(ce);
+	list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node)
+		__pm_clk_remove(ce);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
-	kfree(prd);
+	kfree(pcd);
 }
 
 #endif /* CONFIG_PM */
@@ -196,11 +196,11 @@ void pm_runtime_clk_destroy(struct devic
 #ifdef CONFIG_PM_RUNTIME
 
 /**
- * pm_runtime_clk_acquire - Acquire a device clock.
+ * pm_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,
+static void pm_clk_acquire(struct device *dev,
 				    struct pm_clock_entry *ce)
 {
 	ce->clk = clk_get(dev, ce->con_id);
@@ -213,24 +213,24 @@ static void pm_runtime_clk_acquire(struc
 }
 
 /**
- * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
  * @dev: Device to disable the clocks for.
  */
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (!prd)
+	if (!pcd)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+	list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
 		if (ce->status == PCE_STATUS_NONE)
-			pm_runtime_clk_acquire(dev, ce);
+			pm_clk_acquire(dev, ce);
 
 		if (ce->status < PCE_STATUS_ERROR) {
 			clk_disable(ce->clk);
@@ -238,30 +238,30 @@ int pm_runtime_clk_suspend(struct device
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
  * @dev: Device to enable the clocks for.
  */
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (!prd)
+	if (!pcd)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node) {
+	list_for_each_entry(ce, &pcd->clock_list, node) {
 		if (ce->status == PCE_STATUS_NONE)
-			pm_runtime_clk_acquire(dev, ce);
+			pm_clk_acquire(dev, ce);
 
 		if (ce->status < PCE_STATUS_ERROR) {
 			clk_enable(ce->clk);
@@ -269,13 +269,13 @@ int pm_runtime_clk_resume(struct device
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_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.
@@ -284,13 +284,13 @@ int pm_runtime_clk_resume(struct device
  * struct pm_clk_notifier_block containing all of the requisite data.
  * Specifically, the pm_domain member of that object is copied to the device's
  * pm_domain field and its con_ids member is used to populate the device's list
- * of runtime PM clocks, depending on @action.
+ * of PM clocks, depending on @action.
  *
  * If the device's pm_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,
+static int pm_clk_notify(struct notifier_block *nb,
 				 unsigned long action, void *data)
 {
 	struct pm_clk_notifier_block *clknb;
@@ -307,16 +307,16 @@ static int pm_runtime_clk_notify(struct
 		if (dev->pm_domain)
 			break;
 
-		error = pm_runtime_clk_init(dev);
+		error = pm_clk_init(dev);
 		if (error)
 			break;
 
 		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
-				pm_runtime_clk_add(dev, *con_id);
+				pm_clk_add(dev, *con_id);
 		} else {
-			pm_runtime_clk_add(dev, NULL);
+			pm_clk_add(dev, NULL);
 		}
 
 		break;
@@ -325,7 +325,7 @@ static int pm_runtime_clk_notify(struct
 			break;
 
 		dev->pm_domain = NULL;
-		pm_runtime_clk_destroy(dev);
+		pm_clk_destroy(dev);
 		break;
 	}
 
@@ -337,51 +337,51 @@ static int pm_runtime_clk_notify(struct
 #ifdef CONFIG_PM
 
 /**
- * pm_runtime_clk_suspend - Disable clocks in a device's PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
  * @dev: Device to disable the clocks for.
  */
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
 	/* If there is no driver, the clocks are already disabled. */
-	if (!prd || !dev->driver)
+	if (!pcd || !dev->driver)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_reverse(ce, &prd->clock_list, node)
+	list_for_each_entry_reverse(ce, &pcd->clock_list, node)
 		clk_disable(ce->clk);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_resume - Enable clocks in a device's PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
  * @dev: Device to enable the clocks for.
  */
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
 	/* If there is no driver, the clocks should remain disabled. */
-	if (!prd || !dev->driver)
+	if (!pcd || !dev->driver)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node)
+	list_for_each_entry(ce, &pcd->clock_list, node)
 		clk_enable(ce->clk);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
@@ -423,7 +423,7 @@ static void disable_clock(struct device
 }
 
 /**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_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.
@@ -433,7 +433,7 @@ static void disable_clock(struct device
  * 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,
+static int pm_clk_notify(struct notifier_block *nb,
 				 unsigned long action, void *data)
 {
 	struct pm_clk_notifier_block *clknb;
@@ -469,21 +469,21 @@ static int pm_runtime_clk_notify(struct
 #endif /* !CONFIG_PM_RUNTIME */
 
 /**
- * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * pm_clk_add_notifier - Add bus type notifier for power management 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,
+ * notifier_call member will be replaced with pm_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,
+void pm_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;
+	clknb->nb.notifier_call = pm_clk_notify;
 	bus_register_notifier(bus, &clknb->nb);
 }
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
@@ -252,36 +252,36 @@ struct pm_clk_notifier_block {
 };
 
 #ifdef CONFIG_PM_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);
+extern int pm_clk_init(struct device *dev);
+extern void pm_clk_destroy(struct device *dev);
+extern int pm_clk_add(struct device *dev, const char *con_id);
+extern void pm_clk_remove(struct device *dev, const char *con_id);
+extern int pm_clk_suspend(struct device *dev);
+extern int pm_clk_resume(struct device *dev);
 #else
-static inline int pm_runtime_clk_init(struct device *dev)
+static inline int pm_clk_init(struct device *dev)
 {
 	return -EINVAL;
 }
-static inline void pm_runtime_clk_destroy(struct device *dev)
+static inline void pm_clk_destroy(struct device *dev)
 {
 }
-static inline int pm_runtime_clk_add(struct device *dev, const char *con_id)
+static inline int pm_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)
+static inline void pm_clk_remove(struct device *dev, const char *con_id)
 {
 }
-#define pm_runtime_clock_suspend	NULL
-#define pm_runtime_clock_resume		NULL
+#define pm_clk_suspend	NULL
+#define pm_clk_resume	NULL
 #endif
 
 #ifdef CONFIG_HAVE_CLK
-extern void pm_runtime_clk_add_notifier(struct bus_type *bus,
+extern void pm_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,
+static inline void pm_clk_add_notifier(struct bus_type *bus,
 					struct pm_clk_notifier_block *clknb)
 {
 }
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
@@ -32,7 +32,7 @@ static int omap1_pm_runtime_suspend(stru
 	if (ret)
 		return ret;
 
-	ret = pm_runtime_clk_suspend(dev);
+	ret = pm_clk_suspend(dev);
 	if (ret) {
 		pm_generic_runtime_resume(dev);
 		return ret;
@@ -45,7 +45,7 @@ static int omap1_pm_runtime_resume(struc
 {
 	dev_dbg(dev, "%s\n", __func__);
 
-	pm_runtime_clk_resume(dev);
+	pm_clk_resume(dev);
 	return pm_generic_runtime_resume(dev);
 }
 
@@ -71,7 +71,7 @@ static int __init omap1_pm_runtime_init(
 	if (!cpu_class_is_omap1())
 		return -ENODEV;
 
-	pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
 
 	return 0;
 }
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
@@ -30,8 +30,8 @@ static int default_platform_runtime_idle
 
 static struct dev_pm_domain default_pm_domain = {
 	.ops = {
-		.runtime_suspend = pm_runtime_clk_suspend,
-		.runtime_resume = pm_runtime_clk_resume,
+		.runtime_suspend = pm_clk_suspend,
+		.runtime_resume = pm_clk_resume,
 		.runtime_idle = default_platform_runtime_idle,
 		USE_PLATFORM_PM_SLEEP_OPS
 	},
@@ -52,7 +52,7 @@ static struct pm_clk_notifier_block plat
 
 static int __init sh_pm_runtime_init(void)
 {
-	pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
 	return 0;
 }
 core_initcall(sh_pm_runtime_init);


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 9/10 v6] PM: Rename clock management functions
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (15 preceding siblings ...)
  (?)
@ 2011-06-25 21:30   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:30 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

The common PM clock management functions may be used for system
suspend/resume as well as for runtime PM, so rename them
accordingly.  Modify kerneldoc comments describing these functions
and kernel messages printed by them, so that they refer to power
management in general rather that to runtime PM.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 arch/arm/mach-omap1/pm_bus.c        |    6 -
 arch/arm/mach-shmobile/pm_runtime.c |    6 -
 drivers/base/power/clock_ops.c      |  188 ++++++++++++++++++------------------
 include/linux/pm_runtime.h          |   28 ++---
 4 files changed, 114 insertions(+), 114 deletions(-)

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
@@ -17,7 +17,7 @@
 
 #ifdef CONFIG_PM
 
-struct pm_runtime_clk_data {
+struct pm_clk_data {
 	struct list_head clock_list;
 	struct mutex lock;
 };
@@ -36,25 +36,25 @@ struct pm_clock_entry {
 	enum pce_status status;
 };
 
-static struct pm_runtime_clk_data *__to_prd(struct device *dev)
+static struct pm_clk_data *__to_pcd(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.
+ * pm_clk_add - Start using a device clock for power management.
+ * @dev: Device whose clock is going to be used for power management.
  * @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.
+ * the power management of @dev.
  */
-int pm_runtime_clk_add(struct device *dev, const char *con_id)
+int pm_clk_add(struct device *dev, const char *con_id)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
-	if (!prd)
+	if (!pcd)
 		return -EINVAL;
 
 	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
@@ -73,20 +73,20 @@ int pm_runtime_clk_add(struct device *de
 		}
 	}
 
-	mutex_lock(&prd->lock);
-	list_add_tail(&ce->node, &prd->clock_list);
-	mutex_unlock(&prd->lock);
+	mutex_lock(&pcd->lock);
+	list_add_tail(&ce->node, &pcd->clock_list);
+	mutex_unlock(&pcd->lock);
 	return 0;
 }
 
 /**
- * __pm_runtime_clk_remove - Destroy runtime PM clock entry.
- * @ce: Runtime PM clock entry to destroy.
+ * __pm_clk_remove - Destroy PM clock entry.
+ * @ce: 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.
+ * This routine must be called under the mutex protecting the PM list of clocks
+ * corresponding the the @ce's device.
  */
-static void __pm_runtime_clk_remove(struct pm_clock_entry *ce)
+static void __pm_clk_remove(struct pm_clock_entry *ce)
 {
 	if (!ce)
 		return;
@@ -108,87 +108,87 @@ static void __pm_runtime_clk_remove(stru
 }
 
 /**
- * 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.
+ * pm_clk_remove - Stop using a device clock for power management.
+ * @dev: Device whose clock should not be used for 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.
+ * the power management of @dev.
  */
-void pm_runtime_clk_remove(struct device *dev, const char *con_id)
+void pm_clk_remove(struct device *dev, const char *con_id)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
-	if (!prd)
+	if (!pcd)
 		return;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node) {
+	list_for_each_entry(ce, &pcd->clock_list, node) {
 		if (!con_id && !ce->con_id) {
-			__pm_runtime_clk_remove(ce);
+			__pm_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);
+			__pm_clk_remove(ce);
 			break;
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->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.
+ * pm_clk_init - Initialize a device's list of power management clocks.
+ * @dev: Device to initialize the list of PM clocks for.
  *
- * Allocate a struct pm_runtime_clk_data object, initialize its lock member and
+ * Allocate a struct pm_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)
+int pm_clk_init(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd;
+	struct pm_clk_data *pcd;
 
-	prd = kzalloc(sizeof(*prd), GFP_KERNEL);
-	if (!prd) {
-		dev_err(dev, "Not enough memory fo runtime PM data.\n");
+	pcd = kzalloc(sizeof(*pcd), GFP_KERNEL);
+	if (!pcd) {
+		dev_err(dev, "Not enough memory for PM clock data.\n");
 		return -ENOMEM;
 	}
 
-	INIT_LIST_HEAD(&prd->clock_list);
-	mutex_init(&prd->lock);
-	dev->power.subsys_data = prd;
+	INIT_LIST_HEAD(&pcd->clock_list);
+	mutex_init(&pcd->lock);
+	dev->power.subsys_data = pcd;
 	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.
+ * pm_clk_destroy - Destroy a device's list of power management clocks.
+ * @dev: Device to destroy the list of 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
+ * from the struct pm_clk_data object pointed to by it before and free
  * that object.
  */
-void pm_runtime_clk_destroy(struct device *dev)
+void pm_clk_destroy(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce, *c;
 
-	if (!prd)
+	if (!pcd)
 		return;
 
 	dev->power.subsys_data = NULL;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_safe_reverse(ce, c, &prd->clock_list, node)
-		__pm_runtime_clk_remove(ce);
+	list_for_each_entry_safe_reverse(ce, c, &pcd->clock_list, node)
+		__pm_clk_remove(ce);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
-	kfree(prd);
+	kfree(pcd);
 }
 
 #endif /* CONFIG_PM */
@@ -196,11 +196,11 @@ void pm_runtime_clk_destroy(struct devic
 #ifdef CONFIG_PM_RUNTIME
 
 /**
- * pm_runtime_clk_acquire - Acquire a device clock.
+ * pm_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,
+static void pm_clk_acquire(struct device *dev,
 				    struct pm_clock_entry *ce)
 {
 	ce->clk = clk_get(dev, ce->con_id);
@@ -213,24 +213,24 @@ static void pm_runtime_clk_acquire(struc
 }
 
 /**
- * pm_runtime_clk_suspend - Disable clocks in a device's runtime PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
  * @dev: Device to disable the clocks for.
  */
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (!prd)
+	if (!pcd)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_reverse(ce, &prd->clock_list, node) {
+	list_for_each_entry_reverse(ce, &pcd->clock_list, node) {
 		if (ce->status == PCE_STATUS_NONE)
-			pm_runtime_clk_acquire(dev, ce);
+			pm_clk_acquire(dev, ce);
 
 		if (ce->status < PCE_STATUS_ERROR) {
 			clk_disable(ce->clk);
@@ -238,30 +238,30 @@ int pm_runtime_clk_suspend(struct device
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_resume - Enable clocks in a device's runtime PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
  * @dev: Device to enable the clocks for.
  */
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
-	if (!prd)
+	if (!pcd)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node) {
+	list_for_each_entry(ce, &pcd->clock_list, node) {
 		if (ce->status == PCE_STATUS_NONE)
-			pm_runtime_clk_acquire(dev, ce);
+			pm_clk_acquire(dev, ce);
 
 		if (ce->status < PCE_STATUS_ERROR) {
 			clk_enable(ce->clk);
@@ -269,13 +269,13 @@ int pm_runtime_clk_resume(struct device
 		}
 	}
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_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.
@@ -284,13 +284,13 @@ int pm_runtime_clk_resume(struct device
  * struct pm_clk_notifier_block containing all of the requisite data.
  * Specifically, the pm_domain member of that object is copied to the device's
  * pm_domain field and its con_ids member is used to populate the device's list
- * of runtime PM clocks, depending on @action.
+ * of PM clocks, depending on @action.
  *
  * If the device's pm_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,
+static int pm_clk_notify(struct notifier_block *nb,
 				 unsigned long action, void *data)
 {
 	struct pm_clk_notifier_block *clknb;
@@ -307,16 +307,16 @@ static int pm_runtime_clk_notify(struct
 		if (dev->pm_domain)
 			break;
 
-		error = pm_runtime_clk_init(dev);
+		error = pm_clk_init(dev);
 		if (error)
 			break;
 
 		dev->pm_domain = clknb->pm_domain;
 		if (clknb->con_ids[0]) {
 			for (con_id = clknb->con_ids; *con_id; con_id++)
-				pm_runtime_clk_add(dev, *con_id);
+				pm_clk_add(dev, *con_id);
 		} else {
-			pm_runtime_clk_add(dev, NULL);
+			pm_clk_add(dev, NULL);
 		}
 
 		break;
@@ -325,7 +325,7 @@ static int pm_runtime_clk_notify(struct
 			break;
 
 		dev->pm_domain = NULL;
-		pm_runtime_clk_destroy(dev);
+		pm_clk_destroy(dev);
 		break;
 	}
 
@@ -337,51 +337,51 @@ static int pm_runtime_clk_notify(struct
 #ifdef CONFIG_PM
 
 /**
- * pm_runtime_clk_suspend - Disable clocks in a device's PM clock list.
+ * pm_clk_suspend - Disable clocks in a device's PM clock list.
  * @dev: Device to disable the clocks for.
  */
-int pm_runtime_clk_suspend(struct device *dev)
+int pm_clk_suspend(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
 	/* If there is no driver, the clocks are already disabled. */
-	if (!prd || !dev->driver)
+	if (!pcd || !dev->driver)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry_reverse(ce, &prd->clock_list, node)
+	list_for_each_entry_reverse(ce, &pcd->clock_list, node)
 		clk_disable(ce->clk);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
 
 /**
- * pm_runtime_clk_resume - Enable clocks in a device's PM clock list.
+ * pm_clk_resume - Enable clocks in a device's PM clock list.
  * @dev: Device to enable the clocks for.
  */
-int pm_runtime_clk_resume(struct device *dev)
+int pm_clk_resume(struct device *dev)
 {
-	struct pm_runtime_clk_data *prd = __to_prd(dev);
+	struct pm_clk_data *pcd = __to_pcd(dev);
 	struct pm_clock_entry *ce;
 
 	dev_dbg(dev, "%s()\n", __func__);
 
 	/* If there is no driver, the clocks should remain disabled. */
-	if (!prd || !dev->driver)
+	if (!pcd || !dev->driver)
 		return 0;
 
-	mutex_lock(&prd->lock);
+	mutex_lock(&pcd->lock);
 
-	list_for_each_entry(ce, &prd->clock_list, node)
+	list_for_each_entry(ce, &pcd->clock_list, node)
 		clk_enable(ce->clk);
 
-	mutex_unlock(&prd->lock);
+	mutex_unlock(&pcd->lock);
 
 	return 0;
 }
@@ -423,7 +423,7 @@ static void disable_clock(struct device
 }
 
 /**
- * pm_runtime_clk_notify - Notify routine for device addition and removal.
+ * pm_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.
@@ -433,7 +433,7 @@ static void disable_clock(struct device
  * 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,
+static int pm_clk_notify(struct notifier_block *nb,
 				 unsigned long action, void *data)
 {
 	struct pm_clk_notifier_block *clknb;
@@ -469,21 +469,21 @@ static int pm_runtime_clk_notify(struct
 #endif /* !CONFIG_PM_RUNTIME */
 
 /**
- * pm_runtime_clk_add_notifier - Add bus type notifier for runtime PM clocks.
+ * pm_clk_add_notifier - Add bus type notifier for power management 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,
+ * notifier_call member will be replaced with pm_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,
+void pm_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;
+	clknb->nb.notifier_call = pm_clk_notify;
 	bus_register_notifier(bus, &clknb->nb);
 }
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
@@ -252,36 +252,36 @@ struct pm_clk_notifier_block {
 };
 
 #ifdef CONFIG_PM_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);
+extern int pm_clk_init(struct device *dev);
+extern void pm_clk_destroy(struct device *dev);
+extern int pm_clk_add(struct device *dev, const char *con_id);
+extern void pm_clk_remove(struct device *dev, const char *con_id);
+extern int pm_clk_suspend(struct device *dev);
+extern int pm_clk_resume(struct device *dev);
 #else
-static inline int pm_runtime_clk_init(struct device *dev)
+static inline int pm_clk_init(struct device *dev)
 {
 	return -EINVAL;
 }
-static inline void pm_runtime_clk_destroy(struct device *dev)
+static inline void pm_clk_destroy(struct device *dev)
 {
 }
-static inline int pm_runtime_clk_add(struct device *dev, const char *con_id)
+static inline int pm_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)
+static inline void pm_clk_remove(struct device *dev, const char *con_id)
 {
 }
-#define pm_runtime_clock_suspend	NULL
-#define pm_runtime_clock_resume		NULL
+#define pm_clk_suspend	NULL
+#define pm_clk_resume	NULL
 #endif
 
 #ifdef CONFIG_HAVE_CLK
-extern void pm_runtime_clk_add_notifier(struct bus_type *bus,
+extern void pm_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,
+static inline void pm_clk_add_notifier(struct bus_type *bus,
 					struct pm_clk_notifier_block *clknb)
 {
 }
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
@@ -32,7 +32,7 @@ static int omap1_pm_runtime_suspend(stru
 	if (ret)
 		return ret;
 
-	ret = pm_runtime_clk_suspend(dev);
+	ret = pm_clk_suspend(dev);
 	if (ret) {
 		pm_generic_runtime_resume(dev);
 		return ret;
@@ -45,7 +45,7 @@ static int omap1_pm_runtime_resume(struc
 {
 	dev_dbg(dev, "%s\n", __func__);
 
-	pm_runtime_clk_resume(dev);
+	pm_clk_resume(dev);
 	return pm_generic_runtime_resume(dev);
 }
 
@@ -71,7 +71,7 @@ static int __init omap1_pm_runtime_init(
 	if (!cpu_class_is_omap1())
 		return -ENODEV;
 
-	pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
 
 	return 0;
 }
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
@@ -30,8 +30,8 @@ static int default_platform_runtime_idle
 
 static struct dev_pm_domain default_pm_domain = {
 	.ops = {
-		.runtime_suspend = pm_runtime_clk_suspend,
-		.runtime_resume = pm_runtime_clk_resume,
+		.runtime_suspend = pm_clk_suspend,
+		.runtime_resume = pm_clk_resume,
 		.runtime_idle = default_platform_runtime_idle,
 		USE_PLATFORM_PM_SLEEP_OPS
 	},
@@ -52,7 +52,7 @@ static struct pm_clk_notifier_block plat
 
 static int __init sh_pm_runtime_init(void)
 {
-	pm_runtime_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
+	pm_clk_add_notifier(&platform_bus_type, &platform_bus_notifier);
 	return 0;
 }
 core_initcall(sh_pm_runtime_init);

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-06-25 21:31     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:31 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 5 files changed, 133 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(SH7372_A4LC);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
=================================--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-25 21:31     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:31 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 5 files changed, 133 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(SH7372_A4LC);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 


^ permalink raw reply	[flat|nested] 261+ messages in thread

* [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (18 preceding siblings ...)
  (?)
@ 2011-06-25 21:31   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-25 21:31 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 5 files changed, 133 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(SH7372_A4LC);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc_domain;
+#define SH7372_A4LC	(&sh7372_a4lc_domain)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/Makefile
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
+++ linux-2.6/arch/arm/mach-shmobile/Makefile
@@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)	+= board-ap4ev
 obj-$(CONFIG_MACH_AG5EVM)	+= board-ag5evm.o
 obj-$(CONFIG_MACH_MACKEREL)	+= board-mackerel.o
 
+# PM objects
+pm-$(CONFIG_ARCH_SH7372)	+= pm-sh7372.o
+
 # Framework support
 obj-$(CONFIG_SMP)		+= $(smp-y)
 obj-$(CONFIG_GENERIC_GPIO)	+= $(pfc-y)
+obj-$(CONFIG_PM)		+= $(pm-y)
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc_domain = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains
  2011-06-25 21:31     ` Rafael J. Wysocki
  (?)
@ 2011-06-27  4:07       ` Magnus Damm
  -1 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-27  4:07 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Use the generic power domains support introduced by the previous
> patch to implement support for power domains on SH7372.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Paul Mundt <lethal@linux-sh.org>
> ---

Hi Rafael,

Thanks for your work on this. I've been working on up-porting my A3RV
prototype, and I came across these minor details:

> --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
>
>        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
>
> +       sh7372_init_pm_domain(SH7372_A4LC);
> +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> +
>        hdmi_init_pm_clock();
>        sh7372_pm_init();
>  }
> Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> =================================> --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> @@ -12,6 +12,7 @@
>  #define __ASM_SH7372_H__
>
>  #include <linux/sh_clk.h>
> +#include <linux/pm_domain.h>
>
>  /*
>  * Pin Function Controller:
> @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
>  extern struct clk sh7372_fsidiva_clk;
>  extern struct clk sh7372_fsidivb_clk;
>
> +struct platform_device;
> +
> +struct sh7372_pm_domain {
> +       struct generic_pm_domain genpd;
> +       unsigned int bit_shift;
> +};
> +
> +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> +{
> +       return container_of(d, struct sh7372_pm_domain, genpd);
> +}
> +
> +#ifdef CONFIG_PM
> +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> +
> +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> +                                       struct platform_device *pdev);
> +#else
> +#define SH7372_A4LC    NULL
> +
> +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> +                                              struct platform_device *pdev) {}
> +#endif /* CONFIG_PM */
> +
>  #endif /* __ASM_SH7372_H__ */

Wouldn't it be easier to simply get rid of SH7372_A4LC? Perhaps you
have some special intention behind your #define, but for me the
following change is working just fine:

--- 0001/arch/arm/mach-shmobile/board-mackerel.c
+++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-06-27
13:04:22.000000000 +0900
@@ -1582,9 +1582,9 @@ static void __init mackerel_init(void)

 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));

-	sh7372_init_pm_domain(SH7372_A4LC);
-	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
-	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+	sh7372_init_pm_domain(&sh7372_a4lc);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);

 	hdmi_init_pm_clock();
 	sh7372_pm_init();
--- 0001/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ work/arch/arm/mach-shmobile/include/mach/sh7372.h	2011-06-27
13:03:46.000000000 +0900
@@ -484,15 +484,12 @@ static inline struct sh7372_pm_domain *t
 }

 #ifdef CONFIG_PM
-extern struct sh7372_pm_domain sh7372_a4lc_domain;
-#define SH7372_A4LC	(&sh7372_a4lc_domain)
+extern struct sh7372_pm_domain sh7372_a4lc;

 extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
 extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
 					struct platform_device *pdev);
 #else
-#define SH7372_A4LC	NULL
-
 static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
 static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
 					       struct platform_device *pdev) {}
--- 0001/arch/arm/mach-shmobile/pm-sh7372.c
+++ work/arch/arm/mach-shmobile/pm-sh7372.c	2011-06-27 13:04:02.000000000 +0900
@@ -115,7 +115,7 @@ void sh7372_add_device_to_domain(struct
 	pm_genpd_add_device(&sh7372_pd->genpd, dev);
 }

-struct sh7372_pm_domain sh7372_a4lc_domain = {
+struct sh7372_pm_domain sh7372_a4lc = {
 	.bit_shift = 1,
 };

Also, one more thing:

> --- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
> +++ linux-2.6/arch/arm/mach-shmobile/Makefile
> @@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)    += board-ap4ev
>  obj-$(CONFIG_MACH_AG5EVM)      += board-ag5evm.o
>  obj-$(CONFIG_MACH_MACKEREL)    += board-mackerel.o
>
> +# PM objects
> +pm-$(CONFIG_ARCH_SH7372)       += pm-sh7372.o
> +
>  # Framework support
>  obj-$(CONFIG_SMP)              += $(smp-y)
>  obj-$(CONFIG_GENERIC_GPIO)     += $(pfc-y)
> +obj-$(CONFIG_PM)               += $(pm-y)

I don't think this hunk is needed. It must be a left over from some
older version of the patch when pm-sh7372.c didn't exist.

Would you like me to submit incremental patches, or do you prefer to
fix up your current patch?

Thanks!

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-27  4:07       ` Magnus Damm
  0 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-27  4:07 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh, Paul Mundt

On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Use the generic power domains support introduced by the previous
> patch to implement support for power domains on SH7372.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Paul Mundt <lethal@linux-sh.org>
> ---

Hi Rafael,

Thanks for your work on this. I've been working on up-porting my A3RV
prototype, and I came across these minor details:

> --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
>
>        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
>
> +       sh7372_init_pm_domain(SH7372_A4LC);
> +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> +
>        hdmi_init_pm_clock();
>        sh7372_pm_init();
>  }
> Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> ===================================================================
> --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> @@ -12,6 +12,7 @@
>  #define __ASM_SH7372_H__
>
>  #include <linux/sh_clk.h>
> +#include <linux/pm_domain.h>
>
>  /*
>  * Pin Function Controller:
> @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
>  extern struct clk sh7372_fsidiva_clk;
>  extern struct clk sh7372_fsidivb_clk;
>
> +struct platform_device;
> +
> +struct sh7372_pm_domain {
> +       struct generic_pm_domain genpd;
> +       unsigned int bit_shift;
> +};
> +
> +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> +{
> +       return container_of(d, struct sh7372_pm_domain, genpd);
> +}
> +
> +#ifdef CONFIG_PM
> +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> +
> +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> +                                       struct platform_device *pdev);
> +#else
> +#define SH7372_A4LC    NULL
> +
> +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> +                                              struct platform_device *pdev) {}
> +#endif /* CONFIG_PM */
> +
>  #endif /* __ASM_SH7372_H__ */

Wouldn't it be easier to simply get rid of SH7372_A4LC? Perhaps you
have some special intention behind your #define, but for me the
following change is working just fine:

--- 0001/arch/arm/mach-shmobile/board-mackerel.c
+++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-06-27
13:04:22.000000000 +0900
@@ -1582,9 +1582,9 @@ static void __init mackerel_init(void)

 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));

-	sh7372_init_pm_domain(SH7372_A4LC);
-	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
-	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+	sh7372_init_pm_domain(&sh7372_a4lc);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);

 	hdmi_init_pm_clock();
 	sh7372_pm_init();
--- 0001/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ work/arch/arm/mach-shmobile/include/mach/sh7372.h	2011-06-27
13:03:46.000000000 +0900
@@ -484,15 +484,12 @@ static inline struct sh7372_pm_domain *t
 }

 #ifdef CONFIG_PM
-extern struct sh7372_pm_domain sh7372_a4lc_domain;
-#define SH7372_A4LC	(&sh7372_a4lc_domain)
+extern struct sh7372_pm_domain sh7372_a4lc;

 extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
 extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
 					struct platform_device *pdev);
 #else
-#define SH7372_A4LC	NULL
-
 static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
 static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
 					       struct platform_device *pdev) {}
--- 0001/arch/arm/mach-shmobile/pm-sh7372.c
+++ work/arch/arm/mach-shmobile/pm-sh7372.c	2011-06-27 13:04:02.000000000 +0900
@@ -115,7 +115,7 @@ void sh7372_add_device_to_domain(struct
 	pm_genpd_add_device(&sh7372_pd->genpd, dev);
 }

-struct sh7372_pm_domain sh7372_a4lc_domain = {
+struct sh7372_pm_domain sh7372_a4lc = {
 	.bit_shift = 1,
 };

Also, one more thing:

> --- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
> +++ linux-2.6/arch/arm/mach-shmobile/Makefile
> @@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)    += board-ap4ev
>  obj-$(CONFIG_MACH_AG5EVM)      += board-ag5evm.o
>  obj-$(CONFIG_MACH_MACKEREL)    += board-mackerel.o
>
> +# PM objects
> +pm-$(CONFIG_ARCH_SH7372)       += pm-sh7372.o
> +
>  # Framework support
>  obj-$(CONFIG_SMP)              += $(smp-y)
>  obj-$(CONFIG_GENERIC_GPIO)     += $(pfc-y)
> +obj-$(CONFIG_PM)               += $(pm-y)

I don't think this hunk is needed. It must be a left over from some
older version of the patch when pm-sh7372.c didn't exist.

Would you like me to submit incremental patches, or do you prefer to
fix up your current patch?

Thanks!

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-27  4:07       ` Magnus Damm
  0 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-27  4:07 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Use the generic power domains support introduced by the previous
> patch to implement support for power domains on SH7372.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Paul Mundt <lethal@linux-sh.org>
> ---

Hi Rafael,

Thanks for your work on this. I've been working on up-porting my A3RV
prototype, and I came across these minor details:

> --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
>
>        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
>
> +       sh7372_init_pm_domain(SH7372_A4LC);
> +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> +
>        hdmi_init_pm_clock();
>        sh7372_pm_init();
>  }
> Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> ===================================================================
> --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> @@ -12,6 +12,7 @@
>  #define __ASM_SH7372_H__
>
>  #include <linux/sh_clk.h>
> +#include <linux/pm_domain.h>
>
>  /*
>  * Pin Function Controller:
> @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
>  extern struct clk sh7372_fsidiva_clk;
>  extern struct clk sh7372_fsidivb_clk;
>
> +struct platform_device;
> +
> +struct sh7372_pm_domain {
> +       struct generic_pm_domain genpd;
> +       unsigned int bit_shift;
> +};
> +
> +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> +{
> +       return container_of(d, struct sh7372_pm_domain, genpd);
> +}
> +
> +#ifdef CONFIG_PM
> +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> +
> +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> +                                       struct platform_device *pdev);
> +#else
> +#define SH7372_A4LC    NULL
> +
> +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> +                                              struct platform_device *pdev) {}
> +#endif /* CONFIG_PM */
> +
>  #endif /* __ASM_SH7372_H__ */

Wouldn't it be easier to simply get rid of SH7372_A4LC? Perhaps you
have some special intention behind your #define, but for me the
following change is working just fine:

--- 0001/arch/arm/mach-shmobile/board-mackerel.c
+++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-06-27
13:04:22.000000000 +0900
@@ -1582,9 +1582,9 @@ static void __init mackerel_init(void)

 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));

-	sh7372_init_pm_domain(SH7372_A4LC);
-	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
-	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+	sh7372_init_pm_domain(&sh7372_a4lc);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);

 	hdmi_init_pm_clock();
 	sh7372_pm_init();
--- 0001/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ work/arch/arm/mach-shmobile/include/mach/sh7372.h	2011-06-27
13:03:46.000000000 +0900
@@ -484,15 +484,12 @@ static inline struct sh7372_pm_domain *t
 }

 #ifdef CONFIG_PM
-extern struct sh7372_pm_domain sh7372_a4lc_domain;
-#define SH7372_A4LC	(&sh7372_a4lc_domain)
+extern struct sh7372_pm_domain sh7372_a4lc;

 extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
 extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
 					struct platform_device *pdev);
 #else
-#define SH7372_A4LC	NULL
-
 static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
 static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
 					       struct platform_device *pdev) {}
--- 0001/arch/arm/mach-shmobile/pm-sh7372.c
+++ work/arch/arm/mach-shmobile/pm-sh7372.c	2011-06-27 13:04:02.000000000 +0900
@@ -115,7 +115,7 @@ void sh7372_add_device_to_domain(struct
 	pm_genpd_add_device(&sh7372_pd->genpd, dev);
 }

-struct sh7372_pm_domain sh7372_a4lc_domain = {
+struct sh7372_pm_domain sh7372_a4lc = {
 	.bit_shift = 1,
 };

Also, one more thing:

> --- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
> +++ linux-2.6/arch/arm/mach-shmobile/Makefile
> @@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)    += board-ap4ev
>  obj-$(CONFIG_MACH_AG5EVM)      += board-ag5evm.o
>  obj-$(CONFIG_MACH_MACKEREL)    += board-mackerel.o
>
> +# PM objects
> +pm-$(CONFIG_ARCH_SH7372)       += pm-sh7372.o
> +
>  # Framework support
>  obj-$(CONFIG_SMP)              += $(smp-y)
>  obj-$(CONFIG_GENERIC_GPIO)     += $(pfc-y)
> +obj-$(CONFIG_PM)               += $(pm-y)

I don't think this hunk is needed. It must be a left over from some
older version of the patch when pm-sh7372.c didn't exist.

Would you like me to submit incremental patches, or do you prefer to
fix up your current patch?

Thanks!

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
  2011-06-27  4:07       ` Magnus Damm
@ 2011-06-27 19:25         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-27 19:25 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh, Paul Mundt

On Monday, June 27, 2011, Magnus Damm wrote:
> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Use the generic power domains support introduced by the previous
> > patch to implement support for power domains on SH7372.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Paul Mundt <lethal@linux-sh.org>
> > ---
> 
> Hi Rafael,
> 
> Thanks for your work on this. I've been working on up-porting my A3RV
> prototype, and I came across these minor details:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
> >
> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> >
> > +       sh7372_init_pm_domain(SH7372_A4LC);
> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> > +
> >        hdmi_init_pm_clock();
> >        sh7372_pm_init();
> >  }
> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> > =================================> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> > @@ -12,6 +12,7 @@
> >  #define __ASM_SH7372_H__
> >
> >  #include <linux/sh_clk.h>
> > +#include <linux/pm_domain.h>
> >
> >  /*
> >  * Pin Function Controller:
> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
> >  extern struct clk sh7372_fsidiva_clk;
> >  extern struct clk sh7372_fsidivb_clk;
> >
> > +struct platform_device;
> > +
> > +struct sh7372_pm_domain {
> > +       struct generic_pm_domain genpd;
> > +       unsigned int bit_shift;
> > +};
> > +
> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> > +{
> > +       return container_of(d, struct sh7372_pm_domain, genpd);
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> > +
> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> > +                                       struct platform_device *pdev);
> > +#else
> > +#define SH7372_A4LC    NULL
> > +
> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> > +                                              struct platform_device *pdev) {}
> > +#endif /* CONFIG_PM */
> > +
> >  #endif /* __ASM_SH7372_H__ */
> 
> Wouldn't it be easier to simply get rid of SH7372_A4LC?

Not really, because the code won't build for both CONFIG_PM_RUNTIME and
CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).

> Perhaps you have some special intention behind your #define, but for me the
> following change is working just fine:

Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.

> --- 0001/arch/arm/mach-shmobile/board-mackerel.c
> +++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-06-27
> 13:04:22.000000000 +0900
> @@ -1582,9 +1582,9 @@ static void __init mackerel_init(void)
> 
>  	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> 
> -	sh7372_init_pm_domain(SH7372_A4LC);
> -	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> -	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> +	sh7372_init_pm_domain(&sh7372_a4lc);
> +	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
> +	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
> 
>  	hdmi_init_pm_clock();
>  	sh7372_pm_init();
> --- 0001/arch/arm/mach-shmobile/include/mach/sh7372.h
> +++ work/arch/arm/mach-shmobile/include/mach/sh7372.h	2011-06-27
> 13:03:46.000000000 +0900
> @@ -484,15 +484,12 @@ static inline struct sh7372_pm_domain *t
>  }
> 
>  #ifdef CONFIG_PM
> -extern struct sh7372_pm_domain sh7372_a4lc_domain;
> -#define SH7372_A4LC	(&sh7372_a4lc_domain)
> +extern struct sh7372_pm_domain sh7372_a4lc;
> 
>  extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
>  extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
>  					struct platform_device *pdev);
>  #else
> -#define SH7372_A4LC	NULL
> -
>  static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
>  static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
>  					       struct platform_device *pdev) {}
> --- 0001/arch/arm/mach-shmobile/pm-sh7372.c
> +++ work/arch/arm/mach-shmobile/pm-sh7372.c	2011-06-27 13:04:02.000000000 +0900
> @@ -115,7 +115,7 @@ void sh7372_add_device_to_domain(struct
>  	pm_genpd_add_device(&sh7372_pd->genpd, dev);
>  }
> 
> -struct sh7372_pm_domain sh7372_a4lc_domain = {
> +struct sh7372_pm_domain sh7372_a4lc = {
>  	.bit_shift = 1,
>  };

Of course, I can remove the "_domain" part from the variable name. :-)

> Also, one more thing:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
> > +++ linux-2.6/arch/arm/mach-shmobile/Makefile
> > @@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)    += board-ap4ev
> >  obj-$(CONFIG_MACH_AG5EVM)      += board-ag5evm.o
> >  obj-$(CONFIG_MACH_MACKEREL)    += board-mackerel.o
> >
> > +# PM objects
> > +pm-$(CONFIG_ARCH_SH7372)       += pm-sh7372.o
> > +
> >  # Framework support
> >  obj-$(CONFIG_SMP)              += $(smp-y)
> >  obj-$(CONFIG_GENERIC_GPIO)     += $(pfc-y)
> > +obj-$(CONFIG_PM)               += $(pm-y)
> 
> I don't think this hunk is needed. It must be a left over from some
> older version of the patch when pm-sh7372.c didn't exist.

You're right, this hunk is not necessary any more.

Updated patch is appended, I believe it's correct now.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v9)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 5 files changed, 133 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(SH7372_A4LC);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc;
+#define SH7372_A4LC	(&sh7372_a4lc)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
=================================--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-27 19:25         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-27 19:25 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh, Paul Mundt

On Monday, June 27, 2011, Magnus Damm wrote:
> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Use the generic power domains support introduced by the previous
> > patch to implement support for power domains on SH7372.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Paul Mundt <lethal@linux-sh.org>
> > ---
> 
> Hi Rafael,
> 
> Thanks for your work on this. I've been working on up-porting my A3RV
> prototype, and I came across these minor details:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
> >
> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> >
> > +       sh7372_init_pm_domain(SH7372_A4LC);
> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> > +
> >        hdmi_init_pm_clock();
> >        sh7372_pm_init();
> >  }
> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> > ===================================================================
> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> > @@ -12,6 +12,7 @@
> >  #define __ASM_SH7372_H__
> >
> >  #include <linux/sh_clk.h>
> > +#include <linux/pm_domain.h>
> >
> >  /*
> >  * Pin Function Controller:
> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
> >  extern struct clk sh7372_fsidiva_clk;
> >  extern struct clk sh7372_fsidivb_clk;
> >
> > +struct platform_device;
> > +
> > +struct sh7372_pm_domain {
> > +       struct generic_pm_domain genpd;
> > +       unsigned int bit_shift;
> > +};
> > +
> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> > +{
> > +       return container_of(d, struct sh7372_pm_domain, genpd);
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> > +
> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> > +                                       struct platform_device *pdev);
> > +#else
> > +#define SH7372_A4LC    NULL
> > +
> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> > +                                              struct platform_device *pdev) {}
> > +#endif /* CONFIG_PM */
> > +
> >  #endif /* __ASM_SH7372_H__ */
> 
> Wouldn't it be easier to simply get rid of SH7372_A4LC?

Not really, because the code won't build for both CONFIG_PM_RUNTIME and
CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).

> Perhaps you have some special intention behind your #define, but for me the
> following change is working just fine:

Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.

> --- 0001/arch/arm/mach-shmobile/board-mackerel.c
> +++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-06-27
> 13:04:22.000000000 +0900
> @@ -1582,9 +1582,9 @@ static void __init mackerel_init(void)
> 
>  	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> 
> -	sh7372_init_pm_domain(SH7372_A4LC);
> -	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> -	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> +	sh7372_init_pm_domain(&sh7372_a4lc);
> +	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
> +	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
> 
>  	hdmi_init_pm_clock();
>  	sh7372_pm_init();
> --- 0001/arch/arm/mach-shmobile/include/mach/sh7372.h
> +++ work/arch/arm/mach-shmobile/include/mach/sh7372.h	2011-06-27
> 13:03:46.000000000 +0900
> @@ -484,15 +484,12 @@ static inline struct sh7372_pm_domain *t
>  }
> 
>  #ifdef CONFIG_PM
> -extern struct sh7372_pm_domain sh7372_a4lc_domain;
> -#define SH7372_A4LC	(&sh7372_a4lc_domain)
> +extern struct sh7372_pm_domain sh7372_a4lc;
> 
>  extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
>  extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
>  					struct platform_device *pdev);
>  #else
> -#define SH7372_A4LC	NULL
> -
>  static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
>  static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
>  					       struct platform_device *pdev) {}
> --- 0001/arch/arm/mach-shmobile/pm-sh7372.c
> +++ work/arch/arm/mach-shmobile/pm-sh7372.c	2011-06-27 13:04:02.000000000 +0900
> @@ -115,7 +115,7 @@ void sh7372_add_device_to_domain(struct
>  	pm_genpd_add_device(&sh7372_pd->genpd, dev);
>  }
> 
> -struct sh7372_pm_domain sh7372_a4lc_domain = {
> +struct sh7372_pm_domain sh7372_a4lc = {
>  	.bit_shift = 1,
>  };

Of course, I can remove the "_domain" part from the variable name. :-)

> Also, one more thing:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
> > +++ linux-2.6/arch/arm/mach-shmobile/Makefile
> > @@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)    += board-ap4ev
> >  obj-$(CONFIG_MACH_AG5EVM)      += board-ag5evm.o
> >  obj-$(CONFIG_MACH_MACKEREL)    += board-mackerel.o
> >
> > +# PM objects
> > +pm-$(CONFIG_ARCH_SH7372)       += pm-sh7372.o
> > +
> >  # Framework support
> >  obj-$(CONFIG_SMP)              += $(smp-y)
> >  obj-$(CONFIG_GENERIC_GPIO)     += $(pfc-y)
> > +obj-$(CONFIG_PM)               += $(pm-y)
> 
> I don't think this hunk is needed. It must be a left over from some
> older version of the patch when pm-sh7372.c didn't exist.

You're right, this hunk is not necessary any more.

Updated patch is appended, I believe it's correct now.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v9)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 5 files changed, 133 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(SH7372_A4LC);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc;
+#define SH7372_A4LC	(&sh7372_a4lc)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
  2011-06-27  4:07       ` Magnus Damm
  (?)
  (?)
@ 2011-06-27 19:25       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-27 19:25 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Monday, June 27, 2011, Magnus Damm wrote:
> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Use the generic power domains support introduced by the previous
> > patch to implement support for power domains on SH7372.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Paul Mundt <lethal@linux-sh.org>
> > ---
> 
> Hi Rafael,
> 
> Thanks for your work on this. I've been working on up-porting my A3RV
> prototype, and I came across these minor details:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
> >
> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> >
> > +       sh7372_init_pm_domain(SH7372_A4LC);
> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> > +
> >        hdmi_init_pm_clock();
> >        sh7372_pm_init();
> >  }
> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> > ===================================================================
> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> > @@ -12,6 +12,7 @@
> >  #define __ASM_SH7372_H__
> >
> >  #include <linux/sh_clk.h>
> > +#include <linux/pm_domain.h>
> >
> >  /*
> >  * Pin Function Controller:
> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
> >  extern struct clk sh7372_fsidiva_clk;
> >  extern struct clk sh7372_fsidivb_clk;
> >
> > +struct platform_device;
> > +
> > +struct sh7372_pm_domain {
> > +       struct generic_pm_domain genpd;
> > +       unsigned int bit_shift;
> > +};
> > +
> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> > +{
> > +       return container_of(d, struct sh7372_pm_domain, genpd);
> > +}
> > +
> > +#ifdef CONFIG_PM
> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> > +
> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> > +                                       struct platform_device *pdev);
> > +#else
> > +#define SH7372_A4LC    NULL
> > +
> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> > +                                              struct platform_device *pdev) {}
> > +#endif /* CONFIG_PM */
> > +
> >  #endif /* __ASM_SH7372_H__ */
> 
> Wouldn't it be easier to simply get rid of SH7372_A4LC?

Not really, because the code won't build for both CONFIG_PM_RUNTIME and
CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).

> Perhaps you have some special intention behind your #define, but for me the
> following change is working just fine:

Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.

> --- 0001/arch/arm/mach-shmobile/board-mackerel.c
> +++ work/arch/arm/mach-shmobile/board-mackerel.c	2011-06-27
> 13:04:22.000000000 +0900
> @@ -1582,9 +1582,9 @@ static void __init mackerel_init(void)
> 
>  	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> 
> -	sh7372_init_pm_domain(SH7372_A4LC);
> -	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> -	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> +	sh7372_init_pm_domain(&sh7372_a4lc);
> +	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
> +	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
> 
>  	hdmi_init_pm_clock();
>  	sh7372_pm_init();
> --- 0001/arch/arm/mach-shmobile/include/mach/sh7372.h
> +++ work/arch/arm/mach-shmobile/include/mach/sh7372.h	2011-06-27
> 13:03:46.000000000 +0900
> @@ -484,15 +484,12 @@ static inline struct sh7372_pm_domain *t
>  }
> 
>  #ifdef CONFIG_PM
> -extern struct sh7372_pm_domain sh7372_a4lc_domain;
> -#define SH7372_A4LC	(&sh7372_a4lc_domain)
> +extern struct sh7372_pm_domain sh7372_a4lc;
> 
>  extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
>  extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
>  					struct platform_device *pdev);
>  #else
> -#define SH7372_A4LC	NULL
> -
>  static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
>  static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
>  					       struct platform_device *pdev) {}
> --- 0001/arch/arm/mach-shmobile/pm-sh7372.c
> +++ work/arch/arm/mach-shmobile/pm-sh7372.c	2011-06-27 13:04:02.000000000 +0900
> @@ -115,7 +115,7 @@ void sh7372_add_device_to_domain(struct
>  	pm_genpd_add_device(&sh7372_pd->genpd, dev);
>  }
> 
> -struct sh7372_pm_domain sh7372_a4lc_domain = {
> +struct sh7372_pm_domain sh7372_a4lc = {
>  	.bit_shift = 1,
>  };

Of course, I can remove the "_domain" part from the variable name. :-)

> Also, one more thing:
> 
> > --- linux-2.6.orig/arch/arm/mach-shmobile/Makefile
> > +++ linux-2.6/arch/arm/mach-shmobile/Makefile
> > @@ -42,6 +42,10 @@ obj-$(CONFIG_MACH_AP4EVB)    += board-ap4ev
> >  obj-$(CONFIG_MACH_AG5EVM)      += board-ag5evm.o
> >  obj-$(CONFIG_MACH_MACKEREL)    += board-mackerel.o
> >
> > +# PM objects
> > +pm-$(CONFIG_ARCH_SH7372)       += pm-sh7372.o
> > +
> >  # Framework support
> >  obj-$(CONFIG_SMP)              += $(smp-y)
> >  obj-$(CONFIG_GENERIC_GPIO)     += $(pfc-y)
> > +obj-$(CONFIG_PM)               += $(pm-y)
> 
> I don't think this hunk is needed. It must be a left over from some
> older version of the patch when pm-sh7372.c didn't exist.

You're right, this hunk is not necessary any more.

Updated patch is appended, I believe it's correct now.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v9)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/Makefile              |    4 +
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   28 +++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 5 files changed, 133 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(SH7372_A4LC);
+	sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
+	sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc;
+#define SH7372_A4LC	(&sh7372_a4lc)
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define SH7372_A4LC	NULL
+
+static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
+static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
+					       struct platform_device *pdev) {}
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains
  2011-06-27 19:25         ` Rafael J. Wysocki
  (?)
@ 2011-06-27 23:21           ` Magnus Damm
  -1 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-27 23:21 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tue, Jun 28, 2011 at 4:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Monday, June 27, 2011, Magnus Damm wrote:
>> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Use the generic power domains support introduced by the previous
>> > patch to implement support for power domains on SH7372.
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > Acked-by: Paul Mundt <lethal@linux-sh.org>
>> > ---
>>
>> Hi Rafael,
>>
>> Thanks for your work on this. I've been working on up-porting my A3RV
>> prototype, and I came across these minor details:
>>
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
>> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
>> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
>> >
>> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
>> >
>> > +       sh7372_init_pm_domain(SH7372_A4LC);
>> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
>> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
>> > +
>> >        hdmi_init_pm_clock();
>> >        sh7372_pm_init();
>> >  }
>> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > =================================>> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > @@ -12,6 +12,7 @@
>> >  #define __ASM_SH7372_H__
>> >
>> >  #include <linux/sh_clk.h>
>> > +#include <linux/pm_domain.h>
>> >
>> >  /*
>> >  * Pin Function Controller:
>> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
>> >  extern struct clk sh7372_fsidiva_clk;
>> >  extern struct clk sh7372_fsidivb_clk;
>> >
>> > +struct platform_device;
>> > +
>> > +struct sh7372_pm_domain {
>> > +       struct generic_pm_domain genpd;
>> > +       unsigned int bit_shift;
>> > +};
>> > +
>> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
>> > +{
>> > +       return container_of(d, struct sh7372_pm_domain, genpd);
>> > +}
>> > +
>> > +#ifdef CONFIG_PM
>> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
>> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
>> > +
>> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
>> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
>> > +                                       struct platform_device *pdev);
>> > +#else
>> > +#define SH7372_A4LC    NULL
>> > +
>> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
>> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
>> > +                                              struct platform_device *pdev) {}
>> > +#endif /* CONFIG_PM */
>> > +
>> >  #endif /* __ASM_SH7372_H__ */
>>
>> Wouldn't it be easier to simply get rid of SH7372_A4LC?
>
> Not really, because the code won't build for both CONFIG_PM_RUNTIME and
> CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).
>
>> Perhaps you have some special intention behind your #define, but for me the
>> following change is working just fine:
>
> Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.

True, but in the CONFIG_PM=n case sh7372_a4lc is never used by
sh7372_init_pm_domain() or sh7372_add_device_to_domain().

How about letting the preprocessor do the work for us instead? This
certainly builds without SH7372_A4LC in case of CONFIG_PM=n:

#else
#define sh7372_init_pm_domain(pd) do { } while(0)
#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
#endif /* CONFIG_PM */

Cheers,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-27 23:21           ` Magnus Damm
  0 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-27 23:21 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh, Paul Mundt

On Tue, Jun 28, 2011 at 4:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Monday, June 27, 2011, Magnus Damm wrote:
>> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Use the generic power domains support introduced by the previous
>> > patch to implement support for power domains on SH7372.
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > Acked-by: Paul Mundt <lethal@linux-sh.org>
>> > ---
>>
>> Hi Rafael,
>>
>> Thanks for your work on this. I've been working on up-porting my A3RV
>> prototype, and I came across these minor details:
>>
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
>> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
>> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
>> >
>> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
>> >
>> > +       sh7372_init_pm_domain(SH7372_A4LC);
>> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
>> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
>> > +
>> >        hdmi_init_pm_clock();
>> >        sh7372_pm_init();
>> >  }
>> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > ===================================================================
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > @@ -12,6 +12,7 @@
>> >  #define __ASM_SH7372_H__
>> >
>> >  #include <linux/sh_clk.h>
>> > +#include <linux/pm_domain.h>
>> >
>> >  /*
>> >  * Pin Function Controller:
>> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
>> >  extern struct clk sh7372_fsidiva_clk;
>> >  extern struct clk sh7372_fsidivb_clk;
>> >
>> > +struct platform_device;
>> > +
>> > +struct sh7372_pm_domain {
>> > +       struct generic_pm_domain genpd;
>> > +       unsigned int bit_shift;
>> > +};
>> > +
>> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
>> > +{
>> > +       return container_of(d, struct sh7372_pm_domain, genpd);
>> > +}
>> > +
>> > +#ifdef CONFIG_PM
>> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
>> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
>> > +
>> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
>> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
>> > +                                       struct platform_device *pdev);
>> > +#else
>> > +#define SH7372_A4LC    NULL
>> > +
>> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
>> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
>> > +                                              struct platform_device *pdev) {}
>> > +#endif /* CONFIG_PM */
>> > +
>> >  #endif /* __ASM_SH7372_H__ */
>>
>> Wouldn't it be easier to simply get rid of SH7372_A4LC?
>
> Not really, because the code won't build for both CONFIG_PM_RUNTIME and
> CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).
>
>> Perhaps you have some special intention behind your #define, but for me the
>> following change is working just fine:
>
> Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.

True, but in the CONFIG_PM=n case sh7372_a4lc is never used by
sh7372_init_pm_domain() or sh7372_add_device_to_domain().

How about letting the preprocessor do the work for us instead? This
certainly builds without SH7372_A4LC in case of CONFIG_PM=n:

#else
#define sh7372_init_pm_domain(pd) do { } while(0)
#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
#endif /* CONFIG_PM */

Cheers,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-27 23:21           ` Magnus Damm
  0 siblings, 0 replies; 261+ messages in thread
From: Magnus Damm @ 2011-06-27 23:21 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tue, Jun 28, 2011 at 4:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> On Monday, June 27, 2011, Magnus Damm wrote:
>> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Use the generic power domains support introduced by the previous
>> > patch to implement support for power domains on SH7372.
>> >
>> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
>> > Acked-by: Paul Mundt <lethal@linux-sh.org>
>> > ---
>>
>> Hi Rafael,
>>
>> Thanks for your work on this. I've been working on up-porting my A3RV
>> prototype, and I came across these minor details:
>>
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
>> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
>> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
>> >
>> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
>> >
>> > +       sh7372_init_pm_domain(SH7372_A4LC);
>> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
>> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
>> > +
>> >        hdmi_init_pm_clock();
>> >        sh7372_pm_init();
>> >  }
>> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > ===================================================================
>> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
>> > @@ -12,6 +12,7 @@
>> >  #define __ASM_SH7372_H__
>> >
>> >  #include <linux/sh_clk.h>
>> > +#include <linux/pm_domain.h>
>> >
>> >  /*
>> >  * Pin Function Controller:
>> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
>> >  extern struct clk sh7372_fsidiva_clk;
>> >  extern struct clk sh7372_fsidivb_clk;
>> >
>> > +struct platform_device;
>> > +
>> > +struct sh7372_pm_domain {
>> > +       struct generic_pm_domain genpd;
>> > +       unsigned int bit_shift;
>> > +};
>> > +
>> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
>> > +{
>> > +       return container_of(d, struct sh7372_pm_domain, genpd);
>> > +}
>> > +
>> > +#ifdef CONFIG_PM
>> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
>> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
>> > +
>> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
>> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
>> > +                                       struct platform_device *pdev);
>> > +#else
>> > +#define SH7372_A4LC    NULL
>> > +
>> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
>> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
>> > +                                              struct platform_device *pdev) {}
>> > +#endif /* CONFIG_PM */
>> > +
>> >  #endif /* __ASM_SH7372_H__ */
>>
>> Wouldn't it be easier to simply get rid of SH7372_A4LC?
>
> Not really, because the code won't build for both CONFIG_PM_RUNTIME and
> CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).
>
>> Perhaps you have some special intention behind your #define, but for me the
>> following change is working just fine:
>
> Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.

True, but in the CONFIG_PM=n case sh7372_a4lc is never used by
sh7372_init_pm_domain() or sh7372_add_device_to_domain().

How about letting the preprocessor do the work for us instead? This
certainly builds without SH7372_A4LC in case of CONFIG_PM=n:

#else
#define sh7372_init_pm_domain(pd) do { } while(0)
#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
#endif /* CONFIG_PM */

Cheers,

/ magnus

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
  2011-06-27 23:21           ` Magnus Damm
@ 2011-06-28 10:08             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-28 10:08 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh, Paul Mundt

On Tuesday, June 28, 2011, Magnus Damm wrote:
> On Tue, Jun 28, 2011 at 4:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Monday, June 27, 2011, Magnus Damm wrote:
> >> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > Acked-by: Paul Mundt <lethal@linux-sh.org>
> >> > ---
> >>
> >> Hi Rafael,
> >>
> >> Thanks for your work on this. I've been working on up-porting my A3RV
> >> prototype, and I came across these minor details:
> >>
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> >> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> >> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
> >> >
> >> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> >> >
> >> > +       sh7372_init_pm_domain(SH7372_A4LC);
> >> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> >> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> >> > +
> >> >        hdmi_init_pm_clock();
> >> >        sh7372_pm_init();
> >> >  }
> >> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > =================================> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > @@ -12,6 +12,7 @@
> >> >  #define __ASM_SH7372_H__
> >> >
> >> >  #include <linux/sh_clk.h>
> >> > +#include <linux/pm_domain.h>
> >> >
> >> >  /*
> >> >  * Pin Function Controller:
> >> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
> >> >  extern struct clk sh7372_fsidiva_clk;
> >> >  extern struct clk sh7372_fsidivb_clk;
> >> >
> >> > +struct platform_device;
> >> > +
> >> > +struct sh7372_pm_domain {
> >> > +       struct generic_pm_domain genpd;
> >> > +       unsigned int bit_shift;
> >> > +};
> >> > +
> >> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> >> > +{
> >> > +       return container_of(d, struct sh7372_pm_domain, genpd);
> >> > +}
> >> > +
> >> > +#ifdef CONFIG_PM
> >> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> >> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> >> > +
> >> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> >> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> >> > +                                       struct platform_device *pdev);
> >> > +#else
> >> > +#define SH7372_A4LC    NULL
> >> > +
> >> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> >> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> >> > +                                              struct platform_device *pdev) {}
> >> > +#endif /* CONFIG_PM */
> >> > +
> >> >  #endif /* __ASM_SH7372_H__ */
> >>
> >> Wouldn't it be easier to simply get rid of SH7372_A4LC?
> >
> > Not really, because the code won't build for both CONFIG_PM_RUNTIME and
> > CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).
> >
> >> Perhaps you have some special intention behind your #define, but for me the
> >> following change is working just fine:
> >
> > Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.
> 
> True, but in the CONFIG_PM=n case sh7372_a4lc is never used by
> sh7372_init_pm_domain() or sh7372_add_device_to_domain().
> 
> How about letting the preprocessor do the work for us instead? This
> certainly builds without SH7372_A4LC in case of CONFIG_PM=n:
> 
> #else
> #define sh7372_init_pm_domain(pd) do { } while(0)
> #define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
> #endif /* CONFIG_PM */

Yes, we can do that too, if you prefer it.

Update patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v9)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   24 ++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 4 files changed, 125 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,27 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc;
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define sh7372_init_pm_domain(pd) do { } while(0)
+#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
=================================--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
=================================--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
@ 2011-06-28 10:08             ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-28 10:08 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Paul Walmsley,
	Kevin Hilman, Alan Stern, LKML, linux-sh, Paul Mundt

On Tuesday, June 28, 2011, Magnus Damm wrote:
> On Tue, Jun 28, 2011 at 4:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Monday, June 27, 2011, Magnus Damm wrote:
> >> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > Acked-by: Paul Mundt <lethal@linux-sh.org>
> >> > ---
> >>
> >> Hi Rafael,
> >>
> >> Thanks for your work on this. I've been working on up-porting my A3RV
> >> prototype, and I came across these minor details:
> >>
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> >> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> >> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
> >> >
> >> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> >> >
> >> > +       sh7372_init_pm_domain(SH7372_A4LC);
> >> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> >> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> >> > +
> >> >        hdmi_init_pm_clock();
> >> >        sh7372_pm_init();
> >> >  }
> >> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > ===================================================================
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > @@ -12,6 +12,7 @@
> >> >  #define __ASM_SH7372_H__
> >> >
> >> >  #include <linux/sh_clk.h>
> >> > +#include <linux/pm_domain.h>
> >> >
> >> >  /*
> >> >  * Pin Function Controller:
> >> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
> >> >  extern struct clk sh7372_fsidiva_clk;
> >> >  extern struct clk sh7372_fsidivb_clk;
> >> >
> >> > +struct platform_device;
> >> > +
> >> > +struct sh7372_pm_domain {
> >> > +       struct generic_pm_domain genpd;
> >> > +       unsigned int bit_shift;
> >> > +};
> >> > +
> >> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> >> > +{
> >> > +       return container_of(d, struct sh7372_pm_domain, genpd);
> >> > +}
> >> > +
> >> > +#ifdef CONFIG_PM
> >> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> >> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> >> > +
> >> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> >> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> >> > +                                       struct platform_device *pdev);
> >> > +#else
> >> > +#define SH7372_A4LC    NULL
> >> > +
> >> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> >> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> >> > +                                              struct platform_device *pdev) {}
> >> > +#endif /* CONFIG_PM */
> >> > +
> >> >  #endif /* __ASM_SH7372_H__ */
> >>
> >> Wouldn't it be easier to simply get rid of SH7372_A4LC?
> >
> > Not really, because the code won't build for both CONFIG_PM_RUNTIME and
> > CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).
> >
> >> Perhaps you have some special intention behind your #define, but for me the
> >> following change is working just fine:
> >
> > Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.
> 
> True, but in the CONFIG_PM=n case sh7372_a4lc is never used by
> sh7372_init_pm_domain() or sh7372_add_device_to_domain().
> 
> How about letting the preprocessor do the work for us instead? This
> certainly builds without SH7372_A4LC in case of CONFIG_PM=n:
> 
> #else
> #define sh7372_init_pm_domain(pd) do { } while(0)
> #define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
> #endif /* CONFIG_PM */

Yes, we can do that too, if you prefer it.

Update patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v9)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   24 ++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 4 files changed, 125 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,27 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc;
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define sh7372_init_pm_domain(pd) do { } while(0)
+#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8)
  2011-06-27 23:21           ` Magnus Damm
                             ` (2 preceding siblings ...)
  (?)
@ 2011-06-28 10:08           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-28 10:08 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Tuesday, June 28, 2011, Magnus Damm wrote:
> On Tue, Jun 28, 2011 at 4:25 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Monday, June 27, 2011, Magnus Damm wrote:
> >> On Sun, Jun 26, 2011 at 6:31 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> >
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > Acked-by: Paul Mundt <lethal@linux-sh.org>
> >> > ---
> >>
> >> Hi Rafael,
> >>
> >> Thanks for your work on this. I've been working on up-porting my A3RV
> >> prototype, and I came across these minor details:
> >>
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
> >> > +++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
> >> > @@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
> >> >
> >> >        platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
> >> >
> >> > +       sh7372_init_pm_domain(SH7372_A4LC);
> >> > +       sh7372_add_device_to_domain(SH7372_A4LC, &lcdc_device);
> >> > +       sh7372_add_device_to_domain(SH7372_A4LC, &hdmi_lcdc_device);
> >> > +
> >> >        hdmi_init_pm_clock();
> >> >        sh7372_pm_init();
> >> >  }
> >> > Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > ===================================================================
> >> > --- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > +++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
> >> > @@ -12,6 +12,7 @@
> >> >  #define __ASM_SH7372_H__
> >> >
> >> >  #include <linux/sh_clk.h>
> >> > +#include <linux/pm_domain.h>
> >> >
> >> >  /*
> >> >  * Pin Function Controller:
> >> > @@ -470,4 +471,31 @@ extern struct clk sh7372_fsibck_clk;
> >> >  extern struct clk sh7372_fsidiva_clk;
> >> >  extern struct clk sh7372_fsidivb_clk;
> >> >
> >> > +struct platform_device;
> >> > +
> >> > +struct sh7372_pm_domain {
> >> > +       struct generic_pm_domain genpd;
> >> > +       unsigned int bit_shift;
> >> > +};
> >> > +
> >> > +static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
> >> > +{
> >> > +       return container_of(d, struct sh7372_pm_domain, genpd);
> >> > +}
> >> > +
> >> > +#ifdef CONFIG_PM
> >> > +extern struct sh7372_pm_domain sh7372_a4lc_domain;
> >> > +#define SH7372_A4LC    (&sh7372_a4lc_domain)
> >> > +
> >> > +extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
> >> > +extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
> >> > +                                       struct platform_device *pdev);
> >> > +#else
> >> > +#define SH7372_A4LC    NULL
> >> > +
> >> > +static inline void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd) {}
> >> > +static inline void sh7372_add_device_to_domain(struct sh7372_pm_domain *pd,
> >> > +                                              struct platform_device *pdev) {}
> >> > +#endif /* CONFIG_PM */
> >> > +
> >> >  #endif /* __ASM_SH7372_H__ */
> >>
> >> Wouldn't it be easier to simply get rid of SH7372_A4LC?
> >
> > Not really, because the code won't build for both CONFIG_PM_RUNTIME and
> > CONFIG_SUSPEND unset (resulting in CONFIG_PM unset).
> >
> >> Perhaps you have some special intention behind your #define, but for me the
> >> following change is working just fine:
> >
> > Well, if CONFIG_PM is unset, sh7372_a4lc is not defined.
> 
> True, but in the CONFIG_PM=n case sh7372_a4lc is never used by
> sh7372_init_pm_domain() or sh7372_add_device_to_domain().
> 
> How about letting the preprocessor do the work for us instead? This
> certainly builds without SH7372_A4LC in case of CONFIG_PM=n:
> 
> #else
> #define sh7372_init_pm_domain(pd) do { } while(0)
> #define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
> #endif /* CONFIG_PM */

Yes, we can do that too, if you prefer it.

Update patch is appended.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: ARM / shmobile: Support for I/O power domains for SH7372 (v9)

Use the generic power domains support introduced by the previous
patch to implement support for power domains on SH7372.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Paul Mundt <lethal@linux-sh.org>
---
 arch/arm/Kconfig                             |    1 
 arch/arm/mach-shmobile/board-mackerel.c      |    4 +
 arch/arm/mach-shmobile/include/mach/sh7372.h |   24 ++++++
 arch/arm/mach-shmobile/pm-sh7372.c           |   96 +++++++++++++++++++++++++++
 4 files changed, 125 insertions(+)

Index: linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/board-mackerel.c
+++ linux-2.6/arch/arm/mach-shmobile/board-mackerel.c
@@ -1582,6 +1582,10 @@ static void __init mackerel_init(void)
 
 	platform_add_devices(mackerel_devices, ARRAY_SIZE(mackerel_devices));
 
+	sh7372_init_pm_domain(&sh7372_a4lc);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &lcdc_device);
+	sh7372_add_device_to_domain(&sh7372_a4lc, &hdmi_lcdc_device);
+
 	hdmi_init_pm_clock();
 	sh7372_pm_init();
 }
Index: linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/include/mach/sh7372.h
+++ linux-2.6/arch/arm/mach-shmobile/include/mach/sh7372.h
@@ -12,6 +12,7 @@
 #define __ASM_SH7372_H__
 
 #include <linux/sh_clk.h>
+#include <linux/pm_domain.h>
 
 /*
  * Pin Function Controller:
@@ -470,4 +471,27 @@ extern struct clk sh7372_fsibck_clk;
 extern struct clk sh7372_fsidiva_clk;
 extern struct clk sh7372_fsidivb_clk;
 
+struct platform_device;
+
+struct sh7372_pm_domain {
+	struct generic_pm_domain genpd;
+	unsigned int bit_shift;
+};
+
+static inline struct sh7372_pm_domain *to_sh7372_pd(struct generic_pm_domain *d)
+{
+	return container_of(d, struct sh7372_pm_domain, genpd);
+}
+
+#ifdef CONFIG_PM
+extern struct sh7372_pm_domain sh7372_a4lc;
+
+extern void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd);
+extern void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+					struct platform_device *pdev);
+#else
+#define sh7372_init_pm_domain(pd) do { } while(0)
+#define sh7372_add_device_to_domain(pd, pdev) do { } while(0)
+#endif /* CONFIG_PM */
+
 #endif /* __ASM_SH7372_H__ */
Index: linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
===================================================================
--- linux-2.6.orig/arch/arm/mach-shmobile/pm-sh7372.c
+++ linux-2.6/arch/arm/mach-shmobile/pm-sh7372.c
@@ -15,16 +15,112 @@
 #include <linux/list.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
 #include <asm/system.h>
 #include <asm/io.h>
 #include <asm/tlbflush.h>
 #include <mach/common.h>
+#include <mach/sh7372.h>
 
 #define SMFRAM 0xe6a70000
 #define SYSTBCR 0xe6150024
 #define SBAR 0xe6180020
 #define APARMBAREA 0xe6f10020
 
+#define SPDCR 0xe6180008
+#define SWUCR 0xe6180014
+#define PSTR 0xe6180080
+
+#define PSTR_RETRIES 100
+#define PSTR_DELAY_US 10
+
+#ifdef CONFIG_PM
+
+static int pd_power_down(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+
+	if (__raw_readl(PSTR) & mask) {
+		unsigned int retry_count;
+
+		__raw_writel(mask, SPDCR);
+
+		for (retry_count = PSTR_RETRIES; retry_count; retry_count--) {
+			if (!(__raw_readl(SPDCR) & mask))
+				break;
+			cpu_relax();
+		}
+	}
+
+	pr_debug("sh7372 power domain down 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return 0;
+}
+
+static int pd_power_up(struct generic_pm_domain *genpd)
+{
+	struct sh7372_pm_domain *sh7372_pd = to_sh7372_pd(genpd);
+	unsigned int mask = 1 << sh7372_pd->bit_shift;
+	unsigned int retry_count;
+	int ret = 0;
+
+	if (__raw_readl(PSTR) & mask)
+		goto out;
+
+	__raw_writel(mask, SWUCR);
+
+	for (retry_count = 2 * PSTR_RETRIES; retry_count; retry_count--) {
+		if (!(__raw_readl(SWUCR) & mask))
+			goto out;
+		if (retry_count > PSTR_RETRIES)
+			udelay(PSTR_DELAY_US);
+		else
+			cpu_relax();
+	}
+	if (__raw_readl(SWUCR) & mask)
+		ret = -EIO;
+
+ out:
+	pr_debug("sh7372 power domain up 0x%08x -> PSTR = 0x%08x\n",
+		 mask, __raw_readl(PSTR));
+
+	return ret;
+}
+
+void sh7372_init_pm_domain(struct sh7372_pm_domain *sh7372_pd)
+{
+	struct generic_pm_domain *genpd = &sh7372_pd->genpd;
+
+	pm_genpd_init(genpd, NULL, false);
+	genpd->stop_device = pm_clk_suspend;
+	genpd->start_device = pm_clk_resume;
+	genpd->power_off = pd_power_down;
+	genpd->power_on = pd_power_up;
+	pd_power_up(&sh7372_pd->genpd);
+}
+
+void sh7372_add_device_to_domain(struct sh7372_pm_domain *sh7372_pd,
+				 struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+
+	if (!dev->power.subsys_data) {
+		pm_clk_init(dev);
+		pm_clk_add(dev, NULL);
+	}
+	pm_genpd_add_device(&sh7372_pd->genpd, dev);
+}
+
+struct sh7372_pm_domain sh7372_a4lc = {
+	.bit_shift = 1,
+};
+
+#endif /* CONFIG_PM */
+
 static void sh7372_enter_core_standby(void)
 {
 	void __iomem *smfram = (void __iomem *)SMFRAM;
Index: linux-2.6/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig/arch/arm/Kconfig
+++ linux-2.6/arch/arm/Kconfig
@@ -642,6 +642,7 @@ config ARCH_SHMOBILE
 	select NO_IOPORT
 	select SPARSE_IRQ
 	select MULTI_IRQ_HANDLER
+	select PM_GENERIC_DOMAINS if PM
 	help
 	  Support for Renesas's SH-Mobile and R-Mobile ARM platforms.
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-06-25 21:28     ` Rafael J. Wysocki
@ 2011-06-28 23:44       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-28 23:44 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

This update contains two fixes for the following problems:

* The PM runtime barrier in pm_genpd_prepare() should not run rpm_resume(),
  because it may deadlock (the barrier is under the genpd lock).  For this
  reason, it's better to use __pm_runtime_barrier(dev, false) in there.

* The pm_runtime_enable() in pm_genpd_complete() should only be called if
  run_complete is ture (otherwise it will be unbalanced).  Thanks to Magnus
  for reporting this one.

Thanks,
Rafael

---
 drivers/base/power/domain.c |  551 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 548 insertions(+), 15 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -46,7 +46,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -155,7 +156,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -260,6 +261,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -270,7 +292,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -284,17 +305,7 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
-
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev = dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
-	}
-
-	if (genpd->start_device)
-		genpd->start_device(dev);
-
+	__pm_genpd_runtime_resume(dev, genpd);
 	mutex_unlock(&genpd->lock);
 
 	return 0;
@@ -303,12 +314,493 @@ static int pm_genpd_runtime_resume(struc
 #else
 
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
 
 #define pm_genpd_runtime_suspend	NULL
 #define pm_genpd_runtime_resume		NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ = 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	/*
+	 * Do not check if runtime resume is pending at this point, because it
+	 * has been taken care of already and if pm_genpd_poweron() ran at this
+	 * point as a result of the check, it would deadlock.
+	 */
+	__pm_runtime_disable(dev, false);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count = 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete) {
+		pm_generic_complete(dev);
+		pm_runtime_enable(dev);
+	}
+}
+
+#else
+
+#define pm_genpd_prepare		NULL
+#define pm_genpd_suspend		NULL
+#define pm_genpd_suspend_noirq		NULL
+#define pm_genpd_resume_noirq		NULL
+#define pm_genpd_resume			NULL
+#define pm_genpd_freeze			NULL
+#define pm_genpd_freeze_noirq		NULL
+#define pm_genpd_thaw_noirq		NULL
+#define pm_genpd_thaw			NULL
+#define pm_genpd_dev_poweroff_noirq	NULL
+#define pm_genpd_dev_poweroff		NULL
+#define pm_genpd_restore_noirq		NULL
+#define pm_genpd_restore		NULL
+#define pm_genpd_complete		NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
@@ -331,6 +823,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev = dev) {
 			ret = -EINVAL;
@@ -346,6 +843,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -375,6 +873,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -383,6 +886,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -390,6 +894,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -498,7 +1003,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
=================================--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-06-28 23:44       ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-28 23:44 UTC (permalink / raw)
  To: Linux PM mailing list
  Cc: Greg Kroah-Hartman, Magnus Damm, Paul Walmsley, Kevin Hilman,
	Alan Stern, LKML, linux-sh, Paul Mundt

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

This update contains two fixes for the following problems:

* The PM runtime barrier in pm_genpd_prepare() should not run rpm_resume(),
  because it may deadlock (the barrier is under the genpd lock).  For this
  reason, it's better to use __pm_runtime_barrier(dev, false) in there.

* The pm_runtime_enable() in pm_genpd_complete() should only be called if
  run_complete is ture (otherwise it will be unbalanced).  Thanks to Magnus
  for reporting this one.

Thanks,
Rafael

---
 drivers/base/power/domain.c |  551 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 548 insertions(+), 15 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -46,7 +46,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -155,7 +156,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -260,6 +261,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -270,7 +292,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -284,17 +305,7 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
-
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
-	}
-
-	if (genpd->start_device)
-		genpd->start_device(dev);
-
+	__pm_genpd_runtime_resume(dev, genpd);
 	mutex_unlock(&genpd->lock);
 
 	return 0;
@@ -303,12 +314,493 @@ static int pm_genpd_runtime_resume(struc
 #else
 
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
 
 #define pm_genpd_runtime_suspend	NULL
 #define pm_genpd_runtime_resume		NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	/*
+	 * Do not check if runtime resume is pending at this point, because it
+	 * has been taken care of already and if pm_genpd_poweron() ran at this
+	 * point as a result of the check, it would deadlock.
+	 */
+	__pm_runtime_disable(dev, false);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete) {
+		pm_generic_complete(dev);
+		pm_runtime_enable(dev);
+	}
+}
+
+#else
+
+#define pm_genpd_prepare		NULL
+#define pm_genpd_suspend		NULL
+#define pm_genpd_suspend_noirq		NULL
+#define pm_genpd_resume_noirq		NULL
+#define pm_genpd_resume			NULL
+#define pm_genpd_freeze			NULL
+#define pm_genpd_freeze_noirq		NULL
+#define pm_genpd_thaw_noirq		NULL
+#define pm_genpd_thaw			NULL
+#define pm_genpd_dev_poweroff_noirq	NULL
+#define pm_genpd_dev_poweroff		NULL
+#define pm_genpd_restore_noirq		NULL
+#define pm_genpd_restore		NULL
+#define pm_genpd_complete		NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
@@ -331,6 +823,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -346,6 +843,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -375,6 +873,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -383,6 +886,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -390,6 +894,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -498,7 +1003,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-06-25 21:28     ` Rafael J. Wysocki
  (?)
@ 2011-06-28 23:44     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-28 23:44 UTC (permalink / raw)
  To: Linux PM mailing list; +Cc: linux-sh, Greg Kroah-Hartman, LKML

From: Rafael J. Wysocki <rjw@sisk.pl>

Make generic PM domains support system-wide power transitions
(system suspend and hibernation).  Add suspend, resume, freeze, thaw,
poweroff and restore callbacks to be associated with struct
generic_pm_domain objects and make pm_genpd_init() use them as
appropriate.

The new callbacks do nothing for devices belonging to power domains
that were powered down at run time (before the transition).  For the
other devices the action carried out depends on the type of the
transition.  During system suspend the power domain .suspend()
callback executes pm_generic_suspend() for the device, while the
PM domain .suspend_noirq() callback runs pm_generic_suspend_noirq()
for it, stops it and eventually removes power from the PM domain it
belongs to (after all devices in the domain have been stopped and its
subdomains have been powered off).

During system resume the PM domain .resume_noirq() callback
restores power to the PM domain (when executed for it first time),
starts the device and executes pm_generic_resume_noirq() for it,
while the .resume() callback executes pm_generic_resume() for the
device.  Finally, the .complete() callback executes pm_runtime_idle()
for the device which should put it back into the suspended state if
its runtime PM usage count is equal to zero at that time.

The actions carried out during hibernation and resume from it are
analogous to the ones described above.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---

Hi,

This update contains two fixes for the following problems:

* The PM runtime barrier in pm_genpd_prepare() should not run rpm_resume(),
  because it may deadlock (the barrier is under the genpd lock).  For this
  reason, it's better to use __pm_runtime_barrier(dev, false) in there.

* The pm_runtime_enable() in pm_genpd_complete() should only be called if
  run_complete is ture (otherwise it will be unbalanced).  Thanks to Magnus
  for reporting this one.

Thanks,
Rafael

---
 drivers/base/power/domain.c |  551 ++++++++++++++++++++++++++++++++++++++++++--
 include/linux/pm_domain.h   |   12 
 2 files changed, 548 insertions(+), 15 deletions(-)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -21,7 +21,7 @@ static struct generic_pm_domain *dev_to_
 	if (IS_ERR_OR_NULL(dev->pm_domain))
 		return ERR_PTR(-EINVAL);
 
-	return container_of(dev->pm_domain, struct generic_pm_domain, domain);
+	return pd_to_genpd(dev->pm_domain);
 }
 
 static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
@@ -46,7 +46,8 @@ static int pm_genpd_poweron(struct gener
 		mutex_lock(&genpd->parent->lock);
 	mutex_lock(&genpd->lock);
 
-	if (!genpd->power_is_off)
+	if (!genpd->power_is_off
+	    || (genpd->prepared_count > 0 && genpd->suspend_power_off))
 		goto out;
 
 	if (genpd->parent && genpd->parent->power_is_off) {
@@ -155,7 +156,7 @@ static int pm_genpd_poweroff(struct gene
 	unsigned int not_suspended;
 	int ret;
 
-	if (genpd->power_is_off)
+	if (genpd->power_is_off || genpd->prepared_count > 0)
 		return 0;
 
 	if (genpd->sd_count > 0)
@@ -260,6 +261,27 @@ static int pm_genpd_runtime_suspend(stru
 }
 
 /**
+ * __pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_runtime_resume(struct device *dev,
+				      struct generic_pm_domain *genpd)
+{
+	struct dev_list_entry *dle;
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+}
+
+/**
  * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
  * @dev: Device to resume.
  *
@@ -270,7 +292,6 @@ static int pm_genpd_runtime_suspend(stru
 static int pm_genpd_runtime_resume(struct device *dev)
 {
 	struct generic_pm_domain *genpd;
-	struct dev_list_entry *dle;
 	int ret;
 
 	dev_dbg(dev, "%s()\n", __func__);
@@ -284,17 +305,7 @@ static int pm_genpd_runtime_resume(struc
 		return ret;
 
 	mutex_lock(&genpd->lock);
-
-	list_for_each_entry(dle, &genpd->dev_list, node) {
-		if (dle->dev == dev) {
-			__pm_genpd_restore_device(dle, genpd);
-			break;
-		}
-	}
-
-	if (genpd->start_device)
-		genpd->start_device(dev);
-
+	__pm_genpd_runtime_resume(dev, genpd);
 	mutex_unlock(&genpd->lock);
 
 	return 0;
@@ -303,12 +314,493 @@ static int pm_genpd_runtime_resume(struc
 #else
 
 static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+static inline void __pm_genpd_runtime_resume(struct device *dev,
+					     struct generic_pm_domain *genpd) {}
 
 #define pm_genpd_runtime_suspend	NULL
 #define pm_genpd_runtime_resume		NULL
 
 #endif /* CONFIG_PM_RUNTIME */
 
+#ifdef CONFIG_PM_SLEEP
+
+/**
+ * pm_genpd_sync_poweroff - Synchronously power off a PM domain and its parents.
+ * @genpd: PM domain to power off, if possible.
+ *
+ * Check if the given PM domain can be powered off (during system suspend or
+ * hibernation) and do that if so.  Also, in that case propagate to its parent.
+ *
+ * This function is only called in "noirq" stages of system power transitions,
+ * so it need not acquire locks (all of the "noirq" callbacks are executed
+ * sequentially, so it is guaranteed that it will never run twice in parallel).
+ */
+static void pm_genpd_sync_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent = genpd->parent;
+
+	if (genpd->power_is_off)
+		return;
+
+	if (genpd->suspended_count != genpd->device_count || genpd->sd_count > 0)
+		return;
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		pm_genpd_sync_poweroff(parent);
+	}
+}
+
+/**
+ * pm_genpd_prepare - Start power transition of a device in a PM domain.
+ * @dev: Device to start the transition of.
+ *
+ * Start a power transition of a device (during a system-wide power transition)
+ * under the assumption that its pm_domain field points to the domain member of
+ * an object of type struct generic_pm_domain representing a PM domain
+ * consisting of I/O devices.
+ */
+static int pm_genpd_prepare(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->prepared_count++ == 0)
+		genpd->suspend_power_off = genpd->power_is_off;
+
+	if (genpd->suspend_power_off) {
+		mutex_unlock(&genpd->lock);
+		return 0;
+	}
+
+	/*
+	 * If the device is in the (runtime) "suspended" state, call
+	 * .start_device() for it, if defined.
+	 */
+	if (pm_runtime_suspended(dev))
+		__pm_genpd_runtime_resume(dev, genpd);
+
+	/*
+	 * Do not check if runtime resume is pending at this point, because it
+	 * has been taken care of already and if pm_genpd_poweron() ran at this
+	 * point as a result of the check, it would deadlock.
+	 */
+	__pm_runtime_disable(dev, false);
+
+	mutex_unlock(&genpd->lock);
+
+	return pm_generic_prepare(dev);
+}
+
+/**
+ * pm_genpd_suspend - Suspend a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Suspend a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_suspend(dev);
+}
+
+/**
+ * pm_genpd_suspend_noirq - Late suspend of a device from an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_suspend_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_suspend_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_resume_noirq - Early resume of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_resume_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_resume_noirq(dev);
+}
+
+/**
+ * pm_genpd_resume - Resume a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Resume a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_resume(dev);
+}
+
+/**
+ * pm_genpd_freeze - Freeze a device belonging to an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Freeze a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_freeze(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_freeze(dev);
+}
+
+/**
+ * pm_genpd_freeze_noirq - Late freeze of a device from an I/O power domain.
+ * @dev: Device to freeze.
+ *
+ * Carry out a late freeze of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_freeze_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_freeze_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_thaw_noirq - Early thaw of a device from an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Carry out an early thaw of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_thaw_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_thaw_noirq(dev);
+}
+
+/**
+ * pm_genpd_thaw - Thaw a device belonging to an I/O power domain.
+ * @dev: Device to thaw.
+ *
+ * Thaw a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_thaw(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_thaw(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff - Power off a device belonging to an I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Power off a device under the assumption that its pm_domain field points to
+ * the domain member of an object of type struct generic_pm_domain representing
+ * a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_poweroff(dev);
+}
+
+/**
+ * pm_genpd_dev_poweroff_noirq - Late power off of a device from a PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a late powering off of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_dev_poweroff_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	if (genpd->suspend_power_off)
+		return 0;
+
+	ret = pm_generic_poweroff_noirq(dev);
+	if (ret)
+		return ret;
+
+	if (genpd->stop_device)
+		genpd->stop_device(dev);
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->suspended_count++;
+	pm_genpd_sync_poweroff(genpd);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_restore_noirq - Early restore of a device from an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Carry out an early restore of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a power domain consisting of I/O
+ * devices.
+ */
+static int pm_genpd_restore_noirq(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	/*
+	 * Since all of the "noirq" callbacks are executed sequentially, it is
+	 * guaranteed that this function will never run twice in parallel for
+	 * the same PM domain, so it is not necessary to use locking here.
+	 */
+	genpd->power_is_off = true;
+	if (genpd->suspend_power_off) {
+		/*
+		 * The boot kernel might put the domain into the power on state,
+		 * so make sure it really is powered off.
+		 */
+		if (genpd->power_off)
+			genpd->power_off(genpd);
+		return 0;
+	}
+
+	pm_genpd_poweron(genpd);
+	genpd->suspended_count--;
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	return pm_generic_restore_noirq(dev);
+}
+
+/**
+ * pm_genpd_restore - Restore a device belonging to an I/O power domain.
+ * @dev: Device to resume.
+ *
+ * Restore a device under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static int pm_genpd_restore(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return -EINVAL;
+
+	return genpd->suspend_power_off ? 0 : pm_generic_restore(dev);
+}
+
+/**
+ * pm_genpd_complete - Complete power transition of a device in a power domain.
+ * @dev: Device to complete the transition of.
+ *
+ * Complete a power transition of a device (during a system-wide power
+ * transition) under the assumption that its pm_domain field points to the
+ * domain member of an object of type struct generic_pm_domain representing
+ * a power domain consisting of I/O devices.
+ */
+static void pm_genpd_complete(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	bool run_complete;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	genpd = dev_to_genpd(dev);
+	if (IS_ERR(genpd))
+		return;
+
+	mutex_lock(&genpd->lock);
+
+	run_complete = !genpd->suspend_power_off;
+	if (--genpd->prepared_count == 0)
+		genpd->suspend_power_off = false;
+
+	mutex_unlock(&genpd->lock);
+
+	if (run_complete) {
+		pm_generic_complete(dev);
+		pm_runtime_enable(dev);
+	}
+}
+
+#else
+
+#define pm_genpd_prepare		NULL
+#define pm_genpd_suspend		NULL
+#define pm_genpd_suspend_noirq		NULL
+#define pm_genpd_resume_noirq		NULL
+#define pm_genpd_resume			NULL
+#define pm_genpd_freeze			NULL
+#define pm_genpd_freeze_noirq		NULL
+#define pm_genpd_thaw_noirq		NULL
+#define pm_genpd_thaw			NULL
+#define pm_genpd_dev_poweroff_noirq	NULL
+#define pm_genpd_dev_poweroff		NULL
+#define pm_genpd_restore_noirq		NULL
+#define pm_genpd_restore		NULL
+#define pm_genpd_complete		NULL
+
+#endif /* CONFIG_PM_SLEEP */
+
 /**
  * pm_genpd_add_device - Add a device to an I/O PM domain.
  * @genpd: PM domain to add the device to.
@@ -331,6 +823,11 @@ int pm_genpd_add_device(struct generic_p
 		goto out;
 	}
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node)
 		if (dle->dev == dev) {
 			ret = -EINVAL;
@@ -346,6 +843,7 @@ int pm_genpd_add_device(struct generic_p
 	dle->dev = dev;
 	dle->need_restore = false;
 	list_add_tail(&dle->node, &genpd->dev_list);
+	genpd->device_count++;
 
 	spin_lock_irq(&dev->power.lock);
 	dev->pm_domain = &genpd->domain;
@@ -375,6 +873,11 @@ int pm_genpd_remove_device(struct generi
 
 	mutex_lock(&genpd->lock);
 
+	if (genpd->prepared_count > 0) {
+		ret = -EAGAIN;
+		goto out;
+	}
+
 	list_for_each_entry(dle, &genpd->dev_list, node) {
 		if (dle->dev != dev)
 			continue;
@@ -383,6 +886,7 @@ int pm_genpd_remove_device(struct generi
 		dev->pm_domain = NULL;
 		spin_unlock_irq(&dev->power.lock);
 
+		genpd->device_count--;
 		list_del(&dle->node);
 		kfree(dle);
 
@@ -390,6 +894,7 @@ int pm_genpd_remove_device(struct generi
 		break;
 	}
 
+ out:
 	mutex_unlock(&genpd->lock);
 
 	return ret;
@@ -498,7 +1003,23 @@ void pm_genpd_init(struct generic_pm_dom
 	genpd->in_progress = 0;
 	genpd->sd_count = 0;
 	genpd->power_is_off = is_off;
+	genpd->device_count = 0;
+	genpd->suspended_count = 0;
 	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
 	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
 	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+	genpd->domain.ops.prepare = pm_genpd_prepare;
+	genpd->domain.ops.suspend = pm_genpd_suspend;
+	genpd->domain.ops.suspend_noirq = pm_genpd_suspend_noirq;
+	genpd->domain.ops.resume_noirq = pm_genpd_resume_noirq;
+	genpd->domain.ops.resume = pm_genpd_resume;
+	genpd->domain.ops.freeze = pm_genpd_freeze;
+	genpd->domain.ops.freeze_noirq = pm_genpd_freeze_noirq;
+	genpd->domain.ops.thaw_noirq = pm_genpd_thaw_noirq;
+	genpd->domain.ops.thaw = pm_genpd_thaw;
+	genpd->domain.ops.poweroff = pm_genpd_dev_poweroff;
+	genpd->domain.ops.poweroff_noirq = pm_genpd_dev_poweroff_noirq;
+	genpd->domain.ops.restore_noirq = pm_genpd_restore_noirq;
+	genpd->domain.ops.restore = pm_genpd_restore;
+	genpd->domain.ops.complete = pm_genpd_complete;
 }
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -11,6 +11,9 @@
 
 #include <linux/device.h>
 
+#define GPD_IN_SUSPEND	1
+#define GPD_POWER_OFF	2
+
 struct dev_power_governor {
 	bool (*power_down_ok)(struct dev_pm_domain *domain);
 };
@@ -27,12 +30,21 @@ struct generic_pm_domain {
 	unsigned int in_progress;	/* Number of devices being suspended now */
 	unsigned int sd_count;	/* Number of subdomains with power "on" */
 	bool power_is_off;	/* Whether or not power has been removed */
+	unsigned int device_count;	/* Number of devices */
+	unsigned int suspended_count;	/* System suspend device counter */
+	unsigned int prepared_count;	/* Suspend counter of prepared devices */
+	bool suspend_power_off;	/* Power status before system suspend */
 	int (*power_off)(struct generic_pm_domain *domain);
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
 };
 
+static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)
+{
+	return container_of(pd, struct generic_pm_domain, domain);
+}
+
 struct dev_list_entry {
 	struct list_head node;
 	struct device *dev;

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-25 21:29     ` Rafael J. Wysocki
@ 2011-06-29 23:50       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-29 23:50 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Devices that are set up to wake up the system from sleep states
> should not be stopped and power should not be removed from them
> when the system goes into a sleep state.  

I don't think this belongs in the generic layer since the two
assumptions above are not generally true on embedded systems, and would
result in rather significant power consumption unnecessarily.

First, whether the device should be stopped on device_may_wakeup():

Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
that they can generate wakeups even when they're not clocked (a.k.a
stopped).  So in this case, even after a ->stop_device (which clock
gates the IP), it can still generate wakeups.

Second, whether the device should be powered off if device_may_wakeup():

Embedded SoCs have other ways to wakeup than device-level wakeups.

For example, on OMAP, every pad on the SoC can be configured as a wakeup
source So, for example, you could completely power down the UART IP
blocks (and the enclosing power domain), configure the UART RX pad as a
wakeup source, and still wakeup the system on UART activity.  The OMAP
docs call these IO pad wakeups.

On OMAP in fact, this is the common, default behavior when we enable
"off-mode" in idle and/or suspend, since most of the IPs are powered off
but can still wake up the system.

So in summary, even if device_may_wakeup() is true, many devices (with
additional SoC magic) can still generate wakeups even when stopped and
powered off.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-29 23:50       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-29 23:50 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Devices that are set up to wake up the system from sleep states
> should not be stopped and power should not be removed from them
> when the system goes into a sleep state.  

I don't think this belongs in the generic layer since the two
assumptions above are not generally true on embedded systems, and would
result in rather significant power consumption unnecessarily.

First, whether the device should be stopped on device_may_wakeup():

Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
that they can generate wakeups even when they're not clocked (a.k.a
stopped).  So in this case, even after a ->stop_device (which clock
gates the IP), it can still generate wakeups.

Second, whether the device should be powered off if device_may_wakeup():

Embedded SoCs have other ways to wakeup than device-level wakeups.

For example, on OMAP, every pad on the SoC can be configured as a wakeup
source So, for example, you could completely power down the UART IP
blocks (and the enclosing power domain), configure the UART RX pad as a
wakeup source, and still wakeup the system on UART activity.  The OMAP
docs call these IO pad wakeups.

On OMAP in fact, this is the common, default behavior when we enable
"off-mode" in idle and/or suspend, since most of the IPs are powered off
but can still wake up the system.

So in summary, even if device_may_wakeup() is true, many devices (with
additional SoC magic) can still generate wakeups even when stopped and
powered off.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-25 21:29     ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-29 23:50     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-29 23:50 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Devices that are set up to wake up the system from sleep states
> should not be stopped and power should not be removed from them
> when the system goes into a sleep state.  

I don't think this belongs in the generic layer since the two
assumptions above are not generally true on embedded systems, and would
result in rather significant power consumption unnecessarily.

First, whether the device should be stopped on device_may_wakeup():

Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
that they can generate wakeups even when they're not clocked (a.k.a
stopped).  So in this case, even after a ->stop_device (which clock
gates the IP), it can still generate wakeups.

Second, whether the device should be powered off if device_may_wakeup():

Embedded SoCs have other ways to wakeup than device-level wakeups.

For example, on OMAP, every pad on the SoC can be configured as a wakeup
source So, for example, you could completely power down the UART IP
blocks (and the enclosing power domain), configure the UART RX pad as a
wakeup source, and still wakeup the system on UART activity.  The OMAP
docs call these IO pad wakeups.

On OMAP in fact, this is the common, default behavior when we enable
"off-mode" in idle and/or suspend, since most of the IPs are powered off
but can still wake up the system.

So in summary, even if device_may_wakeup() is true, many devices (with
additional SoC magic) can still generate wakeups even when stopped and
powered off.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-25 21:26     ` Rafael J. Wysocki
@ 2011-06-30  6:14       ` Ming Lei
  -1 siblings, 0 replies; 261+ messages in thread
From: Ming Lei @ 2011-06-30  6:14 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh,
	Paul Mundt

Hi,

On Sun, Jun 26, 2011 at 5:26 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>

> +static void genpd_power_off_work_fn(struct work_struct *work)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       genpd = container_of(work, struct generic_pm_domain, power_off_work);
> +
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

lockdep warning may be triggered if LOCKDEP is enabled, so the
mutex_lock for subdomain should be replaced as mutex_lock_nest.

> +       pm_genpd_poweroff(genpd);
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +}
> +
> +/**
> + * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
> + * @dev: Device to suspend.
> + *
> + * Carry out a runtime suspend of a device under the assumption that its
> + * pm_domain field points to the domain member of an object of type
> + * struct generic_pm_domain representing a PM domain consisting of I/O devices.
> + */
> +static int pm_genpd_runtime_suspend(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(dev->pm_domain))
> +               return -EINVAL;
> +
> +       genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
> +
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

same with above.

> +       if (genpd->stop_device) {
> +               int ret = genpd->stop_device(dev);
> +               if (ret)
> +                       goto out;
> +       }
> +       genpd->in_progress++;
> +       pm_genpd_poweroff(genpd);
> +       genpd->in_progress--;
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +
> +       return 0;
> +}
> +
> +/**
> + * pm_genpd_poweron - Restore power to a given PM domain and its parents.
> + * @genpd: PM domain to power up.
> + *
> + * Restore power to @genpd and all of its parents so that it is possible to
> + * resume a device belonging to it.
> + */
> +static int pm_genpd_poweron(struct generic_pm_domain *genpd)
> +{
> +       int ret = 0;
> +
> + start:
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

same with above

> +       if (!genpd->power_is_off)
> +               goto out;
> +
> +       if (genpd->parent && genpd->parent->power_is_off) {
> +               mutex_unlock(&genpd->lock);
> +               mutex_unlock(&genpd->parent->lock);
> +
> +               ret = pm_genpd_poweron(genpd->parent);
> +               if (ret)
> +                       return ret;
> +
> +               goto start;
> +       }
> +
> +       if (genpd->power_on) {
> +               int ret = genpd->power_on(genpd);
> +               if (ret)
> +                       goto out;
> +       }
> +
> +       genpd->power_is_off = false;
> +       if (genpd->parent)
> +               genpd->parent->sd_count++;
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
> + * @dev: Device to resume.
> + *
> + * Carry out a runtime resume of a device under the assumption that its
> + * pm_domain field points to the domain member of an object of type
> + * struct generic_pm_domain representing a PM domain consisting of I/O devices.
> + */
> +static int pm_genpd_runtime_resume(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +       struct dev_list_entry *dle;
> +       int ret;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(dev->pm_domain))
> +               return -EINVAL;
> +
> +       genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
> +
> +       ret = pm_genpd_poweron(genpd);
> +       if (ret)
> +               return ret;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node) {
> +               if (dle->dev = dev) {
> +                       __pm_genpd_restore_device(dle, genpd);
> +                       break;
> +               }
> +       }
> +
> +       if (genpd->start_device)
> +               genpd->start_device(dev);
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return 0;
> +}
> +
> +#else
> +
> +static inline void genpd_power_off_work_fn(struct work_struct *work) {}
> +
> +#define pm_genpd_runtime_suspend       NULL
> +#define pm_genpd_runtime_resume                NULL
> +
> +#endif /* CONFIG_PM_RUNTIME */
> +
> +/**
> + * pm_genpd_add_device - Add a device to an I/O PM domain.
> + * @genpd: PM domain to add the device to.
> + * @dev: Device to be added.
> + */
> +int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
> +{
> +       struct dev_list_entry *dle;
> +       int ret = 0;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       if (genpd->power_is_off) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node)
> +               if (dle->dev = dev) {
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +
> +       dle = kzalloc(sizeof(*dle), GFP_KERNEL);
> +       if (!dle) {
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       dle->dev = dev;
> +       dle->need_restore = false;
> +       list_add_tail(&dle->node, &genpd->dev_list);
> +
> +       spin_lock_irq(&dev->power.lock);
> +       dev->pm_domain = &genpd->domain;
> +       spin_unlock_irq(&dev->power.lock);
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_remove_device - Remove a device from an I/O PM domain.
> + * @genpd: PM domain to remove the device from.
> + * @dev: Device to be removed.
> + */
> +int pm_genpd_remove_device(struct generic_pm_domain *genpd,
> +                          struct device *dev)
> +{
> +       struct dev_list_entry *dle;
> +       int ret = -EINVAL;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node) {
> +               if (dle->dev != dev)
> +                       continue;
> +
> +               spin_lock_irq(&dev->power.lock);
> +               dev->pm_domain = NULL;
> +               spin_unlock_irq(&dev->power.lock);
> +
> +               list_del(&dle->node);
> +               kfree(dle);
> +
> +               ret = 0;
> +               break;
> +       }
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
> + * @genpd: Master PM domain to add the subdomain to.
> + * @new_subdomain: Subdomain to be added.
> + */
> +int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
> +                          struct generic_pm_domain *new_subdomain)
> +{
> +       struct generic_pm_domain *subdomain;
> +       int ret = 0;
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       if (genpd->power_is_off && !new_subdomain->power_is_off) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
> +               if (subdomain = new_subdomain) {
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +       }
> +
> +       mutex_lock(&new_subdomain->lock);

same with above

> +
> +       list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
> +       new_subdomain->parent = genpd;
> +       if (!subdomain->power_is_off)
> +               genpd->sd_count++;
> +
> +       mutex_unlock(&new_subdomain->lock);
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
> + * @genpd: Master PM domain to remove the subdomain from.
> + * @target: Subdomain to be removed.
> + */
> +int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
> +                             struct generic_pm_domain *target)
> +{
> +       struct generic_pm_domain *subdomain;
> +       int ret = -EINVAL;
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
> +               if (subdomain != target)
> +                       continue;
> +
> +               mutex_lock(&subdomain->lock);

same with above

> +               list_del(&subdomain->sd_node);
> +               subdomain->parent = NULL;
> +               if (!subdomain->power_is_off)
> +                       genpd_sd_counter_dec(genpd);
> +
> +               mutex_unlock(&subdomain->lock);
> +
> +               ret = 0;
> +               break;
> +       }
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_init - Initialize a generic I/O PM domain object.
> + * @genpd: PM domain object to initialize.
> + * @gov: PM domain governor to associate with the domain (may be NULL).
> + * @is_off: Initial value of the domain's power_is_off field.
> + */
> +void pm_genpd_init(struct generic_pm_domain *genpd,
> +                  struct dev_power_governor *gov, bool is_off)
> +{
> +       if (IS_ERR_OR_NULL(genpd))
> +               return;
> +
> +       INIT_LIST_HEAD(&genpd->sd_node);
> +       genpd->parent = NULL;
> +       INIT_LIST_HEAD(&genpd->dev_list);
> +       INIT_LIST_HEAD(&genpd->sd_list);
> +       mutex_init(&genpd->lock);
> +       genpd->gov = gov;
> +       INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
> +       genpd->in_progress = 0;
> +       genpd->sd_count = 0;
> +       genpd->power_is_off = is_off;
> +       genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
> +       genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
> +       genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
> +}
> Index: linux-2.6/kernel/power/Kconfig
> =================================> --- linux-2.6.orig/kernel/power/Kconfig
> +++ linux-2.6/kernel/power/Kconfig
> @@ -227,3 +227,7 @@ config PM_OPP
>  config PM_RUNTIME_CLK
>        def_bool y
>        depends on PM_RUNTIME && HAVE_CLK
> +
> +config PM_GENERIC_DOMAINS
> +       bool
> +       depends on PM
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


thanks,
-- 
Ming Lei

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
@ 2011-06-30  6:14       ` Ming Lei
  0 siblings, 0 replies; 261+ messages in thread
From: Ming Lei @ 2011-06-30  6:14 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh,
	Paul Mundt

Hi,

On Sun, Jun 26, 2011 at 5:26 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>

> +static void genpd_power_off_work_fn(struct work_struct *work)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       genpd = container_of(work, struct generic_pm_domain, power_off_work);
> +
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

lockdep warning may be triggered if LOCKDEP is enabled, so the
mutex_lock for subdomain should be replaced as mutex_lock_nest.

> +       pm_genpd_poweroff(genpd);
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +}
> +
> +/**
> + * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
> + * @dev: Device to suspend.
> + *
> + * Carry out a runtime suspend of a device under the assumption that its
> + * pm_domain field points to the domain member of an object of type
> + * struct generic_pm_domain representing a PM domain consisting of I/O devices.
> + */
> +static int pm_genpd_runtime_suspend(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(dev->pm_domain))
> +               return -EINVAL;
> +
> +       genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
> +
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

same with above.

> +       if (genpd->stop_device) {
> +               int ret = genpd->stop_device(dev);
> +               if (ret)
> +                       goto out;
> +       }
> +       genpd->in_progress++;
> +       pm_genpd_poweroff(genpd);
> +       genpd->in_progress--;
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +
> +       return 0;
> +}
> +
> +/**
> + * pm_genpd_poweron - Restore power to a given PM domain and its parents.
> + * @genpd: PM domain to power up.
> + *
> + * Restore power to @genpd and all of its parents so that it is possible to
> + * resume a device belonging to it.
> + */
> +static int pm_genpd_poweron(struct generic_pm_domain *genpd)
> +{
> +       int ret = 0;
> +
> + start:
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

same with above

> +       if (!genpd->power_is_off)
> +               goto out;
> +
> +       if (genpd->parent && genpd->parent->power_is_off) {
> +               mutex_unlock(&genpd->lock);
> +               mutex_unlock(&genpd->parent->lock);
> +
> +               ret = pm_genpd_poweron(genpd->parent);
> +               if (ret)
> +                       return ret;
> +
> +               goto start;
> +       }
> +
> +       if (genpd->power_on) {
> +               int ret = genpd->power_on(genpd);
> +               if (ret)
> +                       goto out;
> +       }
> +
> +       genpd->power_is_off = false;
> +       if (genpd->parent)
> +               genpd->parent->sd_count++;
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
> + * @dev: Device to resume.
> + *
> + * Carry out a runtime resume of a device under the assumption that its
> + * pm_domain field points to the domain member of an object of type
> + * struct generic_pm_domain representing a PM domain consisting of I/O devices.
> + */
> +static int pm_genpd_runtime_resume(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +       struct dev_list_entry *dle;
> +       int ret;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(dev->pm_domain))
> +               return -EINVAL;
> +
> +       genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
> +
> +       ret = pm_genpd_poweron(genpd);
> +       if (ret)
> +               return ret;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node) {
> +               if (dle->dev == dev) {
> +                       __pm_genpd_restore_device(dle, genpd);
> +                       break;
> +               }
> +       }
> +
> +       if (genpd->start_device)
> +               genpd->start_device(dev);
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return 0;
> +}
> +
> +#else
> +
> +static inline void genpd_power_off_work_fn(struct work_struct *work) {}
> +
> +#define pm_genpd_runtime_suspend       NULL
> +#define pm_genpd_runtime_resume                NULL
> +
> +#endif /* CONFIG_PM_RUNTIME */
> +
> +/**
> + * pm_genpd_add_device - Add a device to an I/O PM domain.
> + * @genpd: PM domain to add the device to.
> + * @dev: Device to be added.
> + */
> +int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
> +{
> +       struct dev_list_entry *dle;
> +       int ret = 0;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       if (genpd->power_is_off) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node)
> +               if (dle->dev == dev) {
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +
> +       dle = kzalloc(sizeof(*dle), GFP_KERNEL);
> +       if (!dle) {
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       dle->dev = dev;
> +       dle->need_restore = false;
> +       list_add_tail(&dle->node, &genpd->dev_list);
> +
> +       spin_lock_irq(&dev->power.lock);
> +       dev->pm_domain = &genpd->domain;
> +       spin_unlock_irq(&dev->power.lock);
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_remove_device - Remove a device from an I/O PM domain.
> + * @genpd: PM domain to remove the device from.
> + * @dev: Device to be removed.
> + */
> +int pm_genpd_remove_device(struct generic_pm_domain *genpd,
> +                          struct device *dev)
> +{
> +       struct dev_list_entry *dle;
> +       int ret = -EINVAL;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node) {
> +               if (dle->dev != dev)
> +                       continue;
> +
> +               spin_lock_irq(&dev->power.lock);
> +               dev->pm_domain = NULL;
> +               spin_unlock_irq(&dev->power.lock);
> +
> +               list_del(&dle->node);
> +               kfree(dle);
> +
> +               ret = 0;
> +               break;
> +       }
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
> + * @genpd: Master PM domain to add the subdomain to.
> + * @new_subdomain: Subdomain to be added.
> + */
> +int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
> +                          struct generic_pm_domain *new_subdomain)
> +{
> +       struct generic_pm_domain *subdomain;
> +       int ret = 0;
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       if (genpd->power_is_off && !new_subdomain->power_is_off) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
> +               if (subdomain == new_subdomain) {
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +       }
> +
> +       mutex_lock(&new_subdomain->lock);

same with above

> +
> +       list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
> +       new_subdomain->parent = genpd;
> +       if (!subdomain->power_is_off)
> +               genpd->sd_count++;
> +
> +       mutex_unlock(&new_subdomain->lock);
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
> + * @genpd: Master PM domain to remove the subdomain from.
> + * @target: Subdomain to be removed.
> + */
> +int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
> +                             struct generic_pm_domain *target)
> +{
> +       struct generic_pm_domain *subdomain;
> +       int ret = -EINVAL;
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
> +               if (subdomain != target)
> +                       continue;
> +
> +               mutex_lock(&subdomain->lock);

same with above

> +               list_del(&subdomain->sd_node);
> +               subdomain->parent = NULL;
> +               if (!subdomain->power_is_off)
> +                       genpd_sd_counter_dec(genpd);
> +
> +               mutex_unlock(&subdomain->lock);
> +
> +               ret = 0;
> +               break;
> +       }
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_init - Initialize a generic I/O PM domain object.
> + * @genpd: PM domain object to initialize.
> + * @gov: PM domain governor to associate with the domain (may be NULL).
> + * @is_off: Initial value of the domain's power_is_off field.
> + */
> +void pm_genpd_init(struct generic_pm_domain *genpd,
> +                  struct dev_power_governor *gov, bool is_off)
> +{
> +       if (IS_ERR_OR_NULL(genpd))
> +               return;
> +
> +       INIT_LIST_HEAD(&genpd->sd_node);
> +       genpd->parent = NULL;
> +       INIT_LIST_HEAD(&genpd->dev_list);
> +       INIT_LIST_HEAD(&genpd->sd_list);
> +       mutex_init(&genpd->lock);
> +       genpd->gov = gov;
> +       INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
> +       genpd->in_progress = 0;
> +       genpd->sd_count = 0;
> +       genpd->power_is_off = is_off;
> +       genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
> +       genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
> +       genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
> +}
> Index: linux-2.6/kernel/power/Kconfig
> ===================================================================
> --- linux-2.6.orig/kernel/power/Kconfig
> +++ linux-2.6/kernel/power/Kconfig
> @@ -227,3 +227,7 @@ config PM_OPP
>  config PM_RUNTIME_CLK
>        def_bool y
>        depends on PM_RUNTIME && HAVE_CLK
> +
> +config PM_GENERIC_DOMAINS
> +       bool
> +       depends on PM
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


thanks,
-- 
Ming Lei

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-25 21:26     ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-06-30  6:14     ` Ming Lei
  -1 siblings, 0 replies; 261+ messages in thread
From: Ming Lei @ 2011-06-30  6:14 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

Hi,

On Sun, Jun 26, 2011 at 5:26 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> From: Rafael J. Wysocki <rjw@sisk.pl>

> +static void genpd_power_off_work_fn(struct work_struct *work)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       genpd = container_of(work, struct generic_pm_domain, power_off_work);
> +
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

lockdep warning may be triggered if LOCKDEP is enabled, so the
mutex_lock for subdomain should be replaced as mutex_lock_nest.

> +       pm_genpd_poweroff(genpd);
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +}
> +
> +/**
> + * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
> + * @dev: Device to suspend.
> + *
> + * Carry out a runtime suspend of a device under the assumption that its
> + * pm_domain field points to the domain member of an object of type
> + * struct generic_pm_domain representing a PM domain consisting of I/O devices.
> + */
> +static int pm_genpd_runtime_suspend(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(dev->pm_domain))
> +               return -EINVAL;
> +
> +       genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
> +
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

same with above.

> +       if (genpd->stop_device) {
> +               int ret = genpd->stop_device(dev);
> +               if (ret)
> +                       goto out;
> +       }
> +       genpd->in_progress++;
> +       pm_genpd_poweroff(genpd);
> +       genpd->in_progress--;
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +
> +       return 0;
> +}
> +
> +/**
> + * pm_genpd_poweron - Restore power to a given PM domain and its parents.
> + * @genpd: PM domain to power up.
> + *
> + * Restore power to @genpd and all of its parents so that it is possible to
> + * resume a device belonging to it.
> + */
> +static int pm_genpd_poweron(struct generic_pm_domain *genpd)
> +{
> +       int ret = 0;
> +
> + start:
> +       if (genpd->parent)
> +               mutex_lock(&genpd->parent->lock);
> +       mutex_lock(&genpd->lock);

same with above

> +       if (!genpd->power_is_off)
> +               goto out;
> +
> +       if (genpd->parent && genpd->parent->power_is_off) {
> +               mutex_unlock(&genpd->lock);
> +               mutex_unlock(&genpd->parent->lock);
> +
> +               ret = pm_genpd_poweron(genpd->parent);
> +               if (ret)
> +                       return ret;
> +
> +               goto start;
> +       }
> +
> +       if (genpd->power_on) {
> +               int ret = genpd->power_on(genpd);
> +               if (ret)
> +                       goto out;
> +       }
> +
> +       genpd->power_is_off = false;
> +       if (genpd->parent)
> +               genpd->parent->sd_count++;
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +       if (genpd->parent)
> +               mutex_unlock(&genpd->parent->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
> + * @dev: Device to resume.
> + *
> + * Carry out a runtime resume of a device under the assumption that its
> + * pm_domain field points to the domain member of an object of type
> + * struct generic_pm_domain representing a PM domain consisting of I/O devices.
> + */
> +static int pm_genpd_runtime_resume(struct device *dev)
> +{
> +       struct generic_pm_domain *genpd;
> +       struct dev_list_entry *dle;
> +       int ret;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(dev->pm_domain))
> +               return -EINVAL;
> +
> +       genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
> +
> +       ret = pm_genpd_poweron(genpd);
> +       if (ret)
> +               return ret;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node) {
> +               if (dle->dev == dev) {
> +                       __pm_genpd_restore_device(dle, genpd);
> +                       break;
> +               }
> +       }
> +
> +       if (genpd->start_device)
> +               genpd->start_device(dev);
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return 0;
> +}
> +
> +#else
> +
> +static inline void genpd_power_off_work_fn(struct work_struct *work) {}
> +
> +#define pm_genpd_runtime_suspend       NULL
> +#define pm_genpd_runtime_resume                NULL
> +
> +#endif /* CONFIG_PM_RUNTIME */
> +
> +/**
> + * pm_genpd_add_device - Add a device to an I/O PM domain.
> + * @genpd: PM domain to add the device to.
> + * @dev: Device to be added.
> + */
> +int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
> +{
> +       struct dev_list_entry *dle;
> +       int ret = 0;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       if (genpd->power_is_off) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node)
> +               if (dle->dev == dev) {
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +
> +       dle = kzalloc(sizeof(*dle), GFP_KERNEL);
> +       if (!dle) {
> +               ret = -ENOMEM;
> +               goto out;
> +       }
> +
> +       dle->dev = dev;
> +       dle->need_restore = false;
> +       list_add_tail(&dle->node, &genpd->dev_list);
> +
> +       spin_lock_irq(&dev->power.lock);
> +       dev->pm_domain = &genpd->domain;
> +       spin_unlock_irq(&dev->power.lock);
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_remove_device - Remove a device from an I/O PM domain.
> + * @genpd: PM domain to remove the device from.
> + * @dev: Device to be removed.
> + */
> +int pm_genpd_remove_device(struct generic_pm_domain *genpd,
> +                          struct device *dev)
> +{
> +       struct dev_list_entry *dle;
> +       int ret = -EINVAL;
> +
> +       dev_dbg(dev, "%s()\n", __func__);
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(dle, &genpd->dev_list, node) {
> +               if (dle->dev != dev)
> +                       continue;
> +
> +               spin_lock_irq(&dev->power.lock);
> +               dev->pm_domain = NULL;
> +               spin_unlock_irq(&dev->power.lock);
> +
> +               list_del(&dle->node);
> +               kfree(dle);
> +
> +               ret = 0;
> +               break;
> +       }
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
> + * @genpd: Master PM domain to add the subdomain to.
> + * @new_subdomain: Subdomain to be added.
> + */
> +int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
> +                          struct generic_pm_domain *new_subdomain)
> +{
> +       struct generic_pm_domain *subdomain;
> +       int ret = 0;
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       if (genpd->power_is_off && !new_subdomain->power_is_off) {
> +               ret = -EINVAL;
> +               goto out;
> +       }
> +
> +       list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
> +               if (subdomain == new_subdomain) {
> +                       ret = -EINVAL;
> +                       goto out;
> +               }
> +       }
> +
> +       mutex_lock(&new_subdomain->lock);

same with above

> +
> +       list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
> +       new_subdomain->parent = genpd;
> +       if (!subdomain->power_is_off)
> +               genpd->sd_count++;
> +
> +       mutex_unlock(&new_subdomain->lock);
> +
> + out:
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
> + * @genpd: Master PM domain to remove the subdomain from.
> + * @target: Subdomain to be removed.
> + */
> +int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
> +                             struct generic_pm_domain *target)
> +{
> +       struct generic_pm_domain *subdomain;
> +       int ret = -EINVAL;
> +
> +       if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
> +               return -EINVAL;
> +
> +       mutex_lock(&genpd->lock);
> +
> +       list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
> +               if (subdomain != target)
> +                       continue;
> +
> +               mutex_lock(&subdomain->lock);

same with above

> +               list_del(&subdomain->sd_node);
> +               subdomain->parent = NULL;
> +               if (!subdomain->power_is_off)
> +                       genpd_sd_counter_dec(genpd);
> +
> +               mutex_unlock(&subdomain->lock);
> +
> +               ret = 0;
> +               break;
> +       }
> +
> +       mutex_unlock(&genpd->lock);
> +
> +       return ret;
> +}
> +
> +/**
> + * pm_genpd_init - Initialize a generic I/O PM domain object.
> + * @genpd: PM domain object to initialize.
> + * @gov: PM domain governor to associate with the domain (may be NULL).
> + * @is_off: Initial value of the domain's power_is_off field.
> + */
> +void pm_genpd_init(struct generic_pm_domain *genpd,
> +                  struct dev_power_governor *gov, bool is_off)
> +{
> +       if (IS_ERR_OR_NULL(genpd))
> +               return;
> +
> +       INIT_LIST_HEAD(&genpd->sd_node);
> +       genpd->parent = NULL;
> +       INIT_LIST_HEAD(&genpd->dev_list);
> +       INIT_LIST_HEAD(&genpd->sd_list);
> +       mutex_init(&genpd->lock);
> +       genpd->gov = gov;
> +       INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
> +       genpd->in_progress = 0;
> +       genpd->sd_count = 0;
> +       genpd->power_is_off = is_off;
> +       genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
> +       genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
> +       genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
> +}
> Index: linux-2.6/kernel/power/Kconfig
> ===================================================================
> --- linux-2.6.orig/kernel/power/Kconfig
> +++ linux-2.6/kernel/power/Kconfig
> @@ -227,3 +227,7 @@ config PM_OPP
>  config PM_RUNTIME_CLK
>        def_bool y
>        depends on PM_RUNTIME && HAVE_CLK
> +
> +config PM_GENERIC_DOMAINS
> +       bool
> +       depends on PM
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
>


thanks,
-- 
Ming Lei

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-30  6:14       ` Ming Lei
@ 2011-06-30 18:58         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 18:58 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh,
	Paul Mundt

On Thursday, June 30, 2011, Ming Lei wrote:
> Hi,
> 
> On Sun, Jun 26, 2011 at 5:26 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> > +static void genpd_power_off_work_fn(struct work_struct *work)
> > +{
> > +       struct generic_pm_domain *genpd;
> > +
> > +       genpd = container_of(work, struct generic_pm_domain, power_off_work);
> > +
> > +       if (genpd->parent)
> > +               mutex_lock(&genpd->parent->lock);
> > +       mutex_lock(&genpd->lock);
> 
> lockdep warning may be triggered if LOCKDEP is enabled, so the
> mutex_lock for subdomain should be replaced as mutex_lock_nest.

[I guess you mean mutex_lock_nested().]

Good point, thanks a lot!  I routinely forget about those things.

Updated patch is appended (Note: [4/10] needs to be updated too,
but in a trivial way).

Thanks,
Rafael

--
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Support for generic I/O PM domains (v8)

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  494 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 ++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 577 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
=================================--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
=================================--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count = 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (drv && drv->pm && drv->pm->runtime_suspend) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (drv && drv->pm && drv->pm->runtime_resume) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		drv->pm->runtime_resume(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count = 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev = dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev = dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain = new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
=================================--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
@ 2011-06-30 18:58         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 18:58 UTC (permalink / raw)
  To: Ming Lei
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh,
	Paul Mundt

On Thursday, June 30, 2011, Ming Lei wrote:
> Hi,
> 
> On Sun, Jun 26, 2011 at 5:26 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> > +static void genpd_power_off_work_fn(struct work_struct *work)
> > +{
> > +       struct generic_pm_domain *genpd;
> > +
> > +       genpd = container_of(work, struct generic_pm_domain, power_off_work);
> > +
> > +       if (genpd->parent)
> > +               mutex_lock(&genpd->parent->lock);
> > +       mutex_lock(&genpd->lock);
> 
> lockdep warning may be triggered if LOCKDEP is enabled, so the
> mutex_lock for subdomain should be replaced as mutex_lock_nest.

[I guess you mean mutex_lock_nested().]

Good point, thanks a lot!  I routinely forget about those things.

Updated patch is appended (Note: [4/10] needs to be updated too,
but in a trivial way).

Thanks,
Rafael

--
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Support for generic I/O PM domains (v8)

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  494 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 ++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 577 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (drv && drv->pm && drv->pm->runtime_suspend) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (drv && drv->pm && drv->pm->runtime_resume) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		drv->pm->runtime_resume(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-30  6:14       ` Ming Lei
  (?)
@ 2011-06-30 18:58       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 18:58 UTC (permalink / raw)
  To: Ming Lei; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thursday, June 30, 2011, Ming Lei wrote:
> Hi,
> 
> On Sun, Jun 26, 2011 at 5:26 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> 
> > +static void genpd_power_off_work_fn(struct work_struct *work)
> > +{
> > +       struct generic_pm_domain *genpd;
> > +
> > +       genpd = container_of(work, struct generic_pm_domain, power_off_work);
> > +
> > +       if (genpd->parent)
> > +               mutex_lock(&genpd->parent->lock);
> > +       mutex_lock(&genpd->lock);
> 
> lockdep warning may be triggered if LOCKDEP is enabled, so the
> mutex_lock for subdomain should be replaced as mutex_lock_nest.

[I guess you mean mutex_lock_nested().]

Good point, thanks a lot!  I routinely forget about those things.

Updated patch is appended (Note: [4/10] needs to be updated too,
but in a trivial way).

Thanks,
Rafael

--
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Support for generic I/O PM domains (v8)

Introduce common headers, helper functions and callbacks allowing
platforms to use simple generic power domains for runtime power
management.

Introduce struct generic_pm_domain to be used for representing
power domains that each contain a number of devices and may be
parent domains or subdomains with respect to other power domains.
Among other things, this structure includes callbacks to be
provided by platforms for performing specific tasks related to
power management (i.e. ->stop_device() may disable a device's
clocks, while ->start_device() may enable them, ->power_off() is
supposed to remove power from the entire power domain
and ->power_on() is supposed to restore it).

Introduce functions that can be used as power domain runtime PM
callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
as well as helper functions for the initialization of a power
domain represented by a struct generic_power_domain object,
adding a device to or removing a device from it and adding or
removing subdomains.

Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
selected by the platforms that want to use the new code.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
---
 drivers/base/power/Makefile |    1 
 drivers/base/power/domain.c |  494 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/pm_domain.h   |   78 ++++++
 kernel/power/Kconfig        |    4 
 4 files changed, 577 insertions(+)

Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- /dev/null
+++ linux-2.6/include/linux/pm_domain.h
@@ -0,0 +1,78 @@
+/*
+ * pm_domain.h - Definitions and headers related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, Renesas Electronics Corp.
+ *
+ * This file is released under the GPLv2.
+ */
+
+#ifndef _LINUX_PM_DOMAIN_H
+#define _LINUX_PM_DOMAIN_H
+
+#include <linux/device.h>
+
+struct dev_power_governor {
+	bool (*power_down_ok)(struct dev_pm_domain *domain);
+};
+
+struct generic_pm_domain {
+	struct dev_pm_domain domain;	/* PM domain operations */
+	struct list_head sd_node;	/* Node in the parent's subdomain list */
+	struct generic_pm_domain *parent;	/* Parent PM domain */
+	struct list_head sd_list;	/* List of dubdomains */
+	struct list_head dev_list;	/* List of devices */
+	struct mutex lock;
+	struct dev_power_governor *gov;
+	struct work_struct power_off_work;
+	unsigned int in_progress;	/* Number of devices being suspended now */
+	unsigned int sd_count;	/* Number of subdomains with power "on" */
+	bool power_is_off;	/* Whether or not power has been removed */
+	int (*power_off)(struct generic_pm_domain *domain);
+	int (*power_on)(struct generic_pm_domain *domain);
+	int (*start_device)(struct device *dev);
+	int (*stop_device)(struct device *dev);
+};
+
+struct dev_list_entry {
+	struct list_head node;
+	struct device *dev;
+	bool need_restore;
+};
+
+#ifdef CONFIG_PM_GENERIC_DOMAINS
+extern int pm_genpd_add_device(struct generic_pm_domain *genpd,
+			       struct device *dev);
+extern int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+				  struct device *dev);
+extern int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+				  struct generic_pm_domain *new_subdomain);
+extern int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+				     struct generic_pm_domain *target);
+extern void pm_genpd_init(struct generic_pm_domain *genpd,
+			  struct dev_power_governor *gov, bool is_off);
+#else
+static inline int pm_genpd_add_device(struct generic_pm_domain *genpd,
+				      struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+					 struct device *dev)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+					 struct generic_pm_domain *new_sd)
+{
+	return -ENOSYS;
+}
+static inline int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+					    struct generic_pm_domain *target)
+{
+	return -ENOSYS;
+}
+static inline void pm_genpd_init(struct generic_pm_domain *genpd,
+				 struct dev_power_governor *gov, bool is_off) {}
+#endif
+
+#endif /* _LINUX_PM_DOMAIN_H */
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_PM_GENERIC_DOMAINS)	+=  domain.o
 obj-$(CONFIG_HAVE_CLK)	+= clock_ops.o
 
 ccflags-$(CONFIG_DEBUG_DRIVER) := -DDEBUG
\ No newline at end of file
Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- /dev/null
+++ linux-2.6/drivers/base/power/domain.c
@@ -0,0 +1,494 @@
+/*
+ * drivers/base/power/domain.c - Common code related to device power domains.
+ *
+ * Copyright (C) 2011 Rafael J. Wysocki <rjw@sisk.pl>, 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_runtime.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#ifdef CONFIG_PM_RUNTIME
+
+static void genpd_sd_counter_dec(struct generic_pm_domain *genpd)
+{
+	if (!WARN_ON(genpd->sd_count == 0))
+			genpd->sd_count--;
+}
+
+/**
+ * __pm_genpd_save_device - Save the pre-suspend state of a device.
+ * @dle: Device list entry of the device to save the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static int __pm_genpd_save_device(struct dev_list_entry *dle,
+				  struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+	int ret = 0;
+
+	if (dle->need_restore)
+		return 0;
+
+	if (drv && drv->pm && drv->pm->runtime_suspend) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		ret = drv->pm->runtime_suspend(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	if (!ret)
+		dle->need_restore = true;
+
+	return ret;
+}
+
+/**
+ * __pm_genpd_restore_device - Restore the pre-suspend state of a device.
+ * @dle: Device list entry of the device to restore the state of.
+ * @genpd: PM domain the device belongs to.
+ */
+static void __pm_genpd_restore_device(struct dev_list_entry *dle,
+				      struct generic_pm_domain *genpd)
+{
+	struct device *dev = dle->dev;
+	struct device_driver *drv = dev->driver;
+
+	if (!dle->need_restore)
+		return;
+
+	if (drv && drv->pm && drv->pm->runtime_resume) {
+		if (genpd->start_device)
+			genpd->start_device(dev);
+
+		drv->pm->runtime_resume(dev);
+
+		if (genpd->stop_device)
+			genpd->stop_device(dev);
+	}
+
+	dle->need_restore = false;
+}
+
+/**
+ * pm_genpd_poweroff - Remove power from a given PM domain.
+ * @genpd: PM domain to power down.
+ *
+ * If all of the @genpd's devices have been suspended and all of its subdomains
+ * have been powered down, run the runtime suspend callbacks provided by all of
+ * the @genpd's devices' drivers and remove power from @genpd.
+ */
+static int pm_genpd_poweroff(struct generic_pm_domain *genpd)
+{
+	struct generic_pm_domain *parent;
+	struct dev_list_entry *dle;
+	unsigned int not_suspended;
+	int ret;
+
+	if (genpd->power_is_off)
+		return 0;
+
+	if (genpd->sd_count > 0)
+		return -EBUSY;
+
+	not_suspended = 0;
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev->driver && !pm_runtime_suspended(dle->dev))
+			not_suspended++;
+
+	if (not_suspended > genpd->in_progress)
+		return -EBUSY;
+
+	if (genpd->gov && genpd->gov->power_down_ok) {
+		if (!genpd->gov->power_down_ok(&genpd->domain))
+			return -EAGAIN;
+	}
+
+	list_for_each_entry_reverse(dle, &genpd->dev_list, node) {
+		ret = __pm_genpd_save_device(dle, genpd);
+		if (ret)
+			goto err_dev;
+	}
+
+	if (genpd->power_off)
+		genpd->power_off(genpd);
+
+	genpd->power_is_off = true;
+
+	parent = genpd->parent;
+	if (parent) {
+		genpd_sd_counter_dec(parent);
+		if (parent->sd_count == 0)
+			queue_work(pm_wq, &parent->power_off_work);
+	}
+
+	return 0;
+
+ err_dev:
+	list_for_each_entry_continue(dle, &genpd->dev_list, node)
+		__pm_genpd_restore_device(dle, genpd);
+
+	return ret;
+}
+
+/**
+ * genpd_power_off_work_fn - Power off PM domain whose subdomain count is 0.
+ * @work: Work structure used for scheduling the execution of this function.
+ */
+static void genpd_power_off_work_fn(struct work_struct *work)
+{
+	struct generic_pm_domain *genpd;
+
+	genpd = container_of(work, struct generic_pm_domain, power_off_work);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+	pm_genpd_poweroff(genpd);
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+}
+
+/**
+ * pm_genpd_runtime_suspend - Suspend a device belonging to I/O PM domain.
+ * @dev: Device to suspend.
+ *
+ * Carry out a runtime suspend of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_suspend(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+
+	if (genpd->stop_device) {
+		int ret = genpd->stop_device(dev);
+		if (ret)
+			goto out;
+	}
+	genpd->in_progress++;
+	pm_genpd_poweroff(genpd);
+	genpd->in_progress--;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return 0;
+}
+
+/**
+ * pm_genpd_poweron - Restore power to a given PM domain and its parents.
+ * @genpd: PM domain to power up.
+ *
+ * Restore power to @genpd and all of its parents so that it is possible to
+ * resume a device belonging to it.
+ */
+static int pm_genpd_poweron(struct generic_pm_domain *genpd)
+{
+	int ret = 0;
+
+ start:
+	if (genpd->parent)
+		mutex_lock(&genpd->parent->lock);
+	mutex_lock_nested(&genpd->lock, SINGLE_DEPTH_NESTING);
+
+	if (!genpd->power_is_off)
+		goto out;
+
+	if (genpd->parent && genpd->parent->power_is_off) {
+		mutex_unlock(&genpd->lock);
+		mutex_unlock(&genpd->parent->lock);
+
+		ret = pm_genpd_poweron(genpd->parent);
+		if (ret)
+			return ret;
+
+		goto start;
+	}
+
+	if (genpd->power_on) {
+		int ret = genpd->power_on(genpd);
+		if (ret)
+			goto out;
+	}
+
+	genpd->power_is_off = false;
+	if (genpd->parent)
+		genpd->parent->sd_count++;
+
+ out:
+	mutex_unlock(&genpd->lock);
+	if (genpd->parent)
+		mutex_unlock(&genpd->parent->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_runtime_resume - Resume a device belonging to I/O PM domain.
+ * @dev: Device to resume.
+ *
+ * Carry out a runtime resume of a device under the assumption that its
+ * pm_domain field points to the domain member of an object of type
+ * struct generic_pm_domain representing a PM domain consisting of I/O devices.
+ */
+static int pm_genpd_runtime_resume(struct device *dev)
+{
+	struct generic_pm_domain *genpd;
+	struct dev_list_entry *dle;
+	int ret;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(dev->pm_domain))
+		return -EINVAL;
+
+	genpd = container_of(dev->pm_domain, struct generic_pm_domain, domain);
+
+	ret = pm_genpd_poweron(genpd);
+	if (ret)
+		return ret;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev == dev) {
+			__pm_genpd_restore_device(dle, genpd);
+			break;
+		}
+	}
+
+	if (genpd->start_device)
+		genpd->start_device(dev);
+
+	mutex_unlock(&genpd->lock);
+
+	return 0;
+}
+
+#else
+
+static inline void genpd_power_off_work_fn(struct work_struct *work) {}
+
+#define pm_genpd_runtime_suspend	NULL
+#define pm_genpd_runtime_resume		NULL
+
+#endif /* CONFIG_PM_RUNTIME */
+
+/**
+ * pm_genpd_add_device - Add a device to an I/O PM domain.
+ * @genpd: PM domain to add the device to.
+ * @dev: Device to be added.
+ */
+int pm_genpd_add_device(struct generic_pm_domain *genpd, struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = 0;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(dle, &genpd->dev_list, node)
+		if (dle->dev == dev) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+	dle = kzalloc(sizeof(*dle), GFP_KERNEL);
+	if (!dle) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	dle->dev = dev;
+	dle->need_restore = false;
+	list_add_tail(&dle->node, &genpd->dev_list);
+
+	spin_lock_irq(&dev->power.lock);
+	dev->pm_domain = &genpd->domain;
+	spin_unlock_irq(&dev->power.lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_device - Remove a device from an I/O PM domain.
+ * @genpd: PM domain to remove the device from.
+ * @dev: Device to be removed.
+ */
+int pm_genpd_remove_device(struct generic_pm_domain *genpd,
+			   struct device *dev)
+{
+	struct dev_list_entry *dle;
+	int ret = -EINVAL;
+
+	dev_dbg(dev, "%s()\n", __func__);
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(dev))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(dle, &genpd->dev_list, node) {
+		if (dle->dev != dev)
+			continue;
+
+		spin_lock_irq(&dev->power.lock);
+		dev->pm_domain = NULL;
+		spin_unlock_irq(&dev->power.lock);
+
+		list_del(&dle->node);
+		kfree(dle);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_add_subdomain - Add a subdomain to an I/O PM domain.
+ * @genpd: Master PM domain to add the subdomain to.
+ * @new_subdomain: Subdomain to be added.
+ */
+int pm_genpd_add_subdomain(struct generic_pm_domain *genpd,
+			   struct generic_pm_domain *new_subdomain)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = 0;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(new_subdomain))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	if (genpd->power_is_off && !new_subdomain->power_is_off) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain == new_subdomain) {
+			ret = -EINVAL;
+			goto out;
+		}
+	}
+
+	mutex_lock_nested(&new_subdomain->lock, SINGLE_DEPTH_NESTING);
+
+	list_add_tail(&new_subdomain->sd_node, &genpd->sd_list);
+	new_subdomain->parent = genpd;
+	if (!subdomain->power_is_off)
+		genpd->sd_count++;
+
+	mutex_unlock(&new_subdomain->lock);
+
+ out:
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_remove_subdomain - Remove a subdomain from an I/O PM domain.
+ * @genpd: Master PM domain to remove the subdomain from.
+ * @target: Subdomain to be removed.
+ */
+int pm_genpd_remove_subdomain(struct generic_pm_domain *genpd,
+			      struct generic_pm_domain *target)
+{
+	struct generic_pm_domain *subdomain;
+	int ret = -EINVAL;
+
+	if (IS_ERR_OR_NULL(genpd) || IS_ERR_OR_NULL(target))
+		return -EINVAL;
+
+	mutex_lock(&genpd->lock);
+
+	list_for_each_entry(subdomain, &genpd->sd_list, sd_node) {
+		if (subdomain != target)
+			continue;
+
+		mutex_lock_nested(&subdomain->lock, SINGLE_DEPTH_NESTING);
+
+		list_del(&subdomain->sd_node);
+		subdomain->parent = NULL;
+		if (!subdomain->power_is_off)
+			genpd_sd_counter_dec(genpd);
+
+		mutex_unlock(&subdomain->lock);
+
+		ret = 0;
+		break;
+	}
+
+	mutex_unlock(&genpd->lock);
+
+	return ret;
+}
+
+/**
+ * pm_genpd_init - Initialize a generic I/O PM domain object.
+ * @genpd: PM domain object to initialize.
+ * @gov: PM domain governor to associate with the domain (may be NULL).
+ * @is_off: Initial value of the domain's power_is_off field.
+ */
+void pm_genpd_init(struct generic_pm_domain *genpd,
+		   struct dev_power_governor *gov, bool is_off)
+{
+	if (IS_ERR_OR_NULL(genpd))
+		return;
+
+	INIT_LIST_HEAD(&genpd->sd_node);
+	genpd->parent = NULL;
+	INIT_LIST_HEAD(&genpd->dev_list);
+	INIT_LIST_HEAD(&genpd->sd_list);
+	mutex_init(&genpd->lock);
+	genpd->gov = gov;
+	INIT_WORK(&genpd->power_off_work, genpd_power_off_work_fn);
+	genpd->in_progress = 0;
+	genpd->sd_count = 0;
+	genpd->power_is_off = is_off;
+	genpd->domain.ops.runtime_suspend = pm_genpd_runtime_suspend;
+	genpd->domain.ops.runtime_resume = pm_genpd_runtime_resume;
+	genpd->domain.ops.runtime_idle = pm_generic_runtime_idle;
+}
Index: linux-2.6/kernel/power/Kconfig
===================================================================
--- linux-2.6.orig/kernel/power/Kconfig
+++ linux-2.6/kernel/power/Kconfig
@@ -227,3 +227,7 @@ config PM_OPP
 config PM_RUNTIME_CLK
 	def_bool y
 	depends on PM_RUNTIME && HAVE_CLK
+
+config PM_GENERIC_DOMAINS
+	bool
+	depends on PM

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-29 23:50       ` Kevin Hilman
@ 2011-06-30 19:37         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 19:37 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Thursday, June 30, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Devices that are set up to wake up the system from sleep states
> > should not be stopped and power should not be removed from them
> > when the system goes into a sleep state.  
> 
> I don't think this belongs in the generic layer since the two
> assumptions above are not generally true on embedded systems, and would
> result in rather significant power consumption unnecessarily.

As to whether or not this belongs to the generic layer, I don't quite agree
(see below), but the changelog seems to be a bit inaccurate.

> First, whether the device should be stopped on device_may_wakeup():
> 
> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> that they can generate wakeups even when they're not clocked (a.k.a
> stopped).  So in this case, even after a ->stop_device (which clock
> gates the IP), it can still generate wakeups.
> 
> Second, whether the device should be powered off if device_may_wakeup():
> 
> Embedded SoCs have other ways to wakeup than device-level wakeups.
> 
> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> source So, for example, you could completely power down the UART IP
> blocks (and the enclosing power domain), configure the UART RX pad as a
> wakeup source, and still wakeup the system on UART activity.  The OMAP
> docs call these IO pad wakeups.
> 
> On OMAP in fact, this is the common, default behavior when we enable
> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> but can still wake up the system.
> 
> So in summary, even if device_may_wakeup() is true, many devices (with
> additional SoC magic) can still generate wakeups even when stopped and
> powered off.

Well, on the other hand, on some SoCs there are devices that can't be
powered off (or "declocked") if they are supposed to generate wakeups.
Also, I'm sure there are cases in which wakeups can be generated for devices
with their clocks off, but only if power is present.  So there are multiple
cases, but not so many overall.  So, IMO, it makes sense to handle that at
the generic level, although not necessarily in such a simplistic way.

Now, at this point, I want to do something very simple, which I think is
done by this patch.  Is this optimal power comsumption-wise for every potential
user of the framework?  No, but certainly for some it's sufficient.  Is it
going to work in general?  I think it is.

Of course, there's the question how to handle that more accurately and I have
some ideas.  If you have any, please let me know.

In the meantime, I'm going to modify the changelog so that it's clear that
it's a "first approximation" thing, like in the patch below.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions

There is the problem how to handle devices set up to wake up the
system from sleep states during system-wide power transitions.
In some cases, those devices can be turned off entirely, because the
wakeup signals will be generated on their behalf anyway.  In some
other cases, they will generate wakeup signals if their clocks are
stopped, but only if power is not removed from them.  Finally, in
some cases, they can only generate wakeup signals if power is not
removed from them and their clocks are enabled.

In the future, it will be necessary to take all of the above
situations into account, but for starters it is possible to use
the observation that if all wakeup devices are treated like the
last group (i.e. their clocks are enabled and power in not removed
from them during system suspend transitions), they all will be able
to generate wakeups, although power consumption in the resulting
system sleep state may not be optimal in some cases.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -450,6 +450,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -670,6 +673,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-30 19:37         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 19:37 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Thursday, June 30, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Devices that are set up to wake up the system from sleep states
> > should not be stopped and power should not be removed from them
> > when the system goes into a sleep state.  
> 
> I don't think this belongs in the generic layer since the two
> assumptions above are not generally true on embedded systems, and would
> result in rather significant power consumption unnecessarily.

As to whether or not this belongs to the generic layer, I don't quite agree
(see below), but the changelog seems to be a bit inaccurate.

> First, whether the device should be stopped on device_may_wakeup():
> 
> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> that they can generate wakeups even when they're not clocked (a.k.a
> stopped).  So in this case, even after a ->stop_device (which clock
> gates the IP), it can still generate wakeups.
> 
> Second, whether the device should be powered off if device_may_wakeup():
> 
> Embedded SoCs have other ways to wakeup than device-level wakeups.
> 
> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> source So, for example, you could completely power down the UART IP
> blocks (and the enclosing power domain), configure the UART RX pad as a
> wakeup source, and still wakeup the system on UART activity.  The OMAP
> docs call these IO pad wakeups.
> 
> On OMAP in fact, this is the common, default behavior when we enable
> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> but can still wake up the system.
> 
> So in summary, even if device_may_wakeup() is true, many devices (with
> additional SoC magic) can still generate wakeups even when stopped and
> powered off.

Well, on the other hand, on some SoCs there are devices that can't be
powered off (or "declocked") if they are supposed to generate wakeups.
Also, I'm sure there are cases in which wakeups can be generated for devices
with their clocks off, but only if power is present.  So there are multiple
cases, but not so many overall.  So, IMO, it makes sense to handle that at
the generic level, although not necessarily in such a simplistic way.

Now, at this point, I want to do something very simple, which I think is
done by this patch.  Is this optimal power comsumption-wise for every potential
user of the framework?  No, but certainly for some it's sufficient.  Is it
going to work in general?  I think it is.

Of course, there's the question how to handle that more accurately and I have
some ideas.  If you have any, please let me know.

In the meantime, I'm going to modify the changelog so that it's clear that
it's a "first approximation" thing, like in the patch below.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions

There is the problem how to handle devices set up to wake up the
system from sleep states during system-wide power transitions.
In some cases, those devices can be turned off entirely, because the
wakeup signals will be generated on their behalf anyway.  In some
other cases, they will generate wakeup signals if their clocks are
stopped, but only if power is not removed from them.  Finally, in
some cases, they can only generate wakeup signals if power is not
removed from them and their clocks are enabled.

In the future, it will be necessary to take all of the above
situations into account, but for starters it is possible to use
the observation that if all wakeup devices are treated like the
last group (i.e. their clocks are enabled and power in not removed
from them during system suspend transitions), they all will be able
to generate wakeups, although power consumption in the resulting
system sleep state may not be optimal in some cases.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -450,6 +450,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -670,6 +673,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-29 23:50       ` Kevin Hilman
  (?)
@ 2011-06-30 19:37       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 19:37 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Thursday, June 30, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Devices that are set up to wake up the system from sleep states
> > should not be stopped and power should not be removed from them
> > when the system goes into a sleep state.  
> 
> I don't think this belongs in the generic layer since the two
> assumptions above are not generally true on embedded systems, and would
> result in rather significant power consumption unnecessarily.

As to whether or not this belongs to the generic layer, I don't quite agree
(see below), but the changelog seems to be a bit inaccurate.

> First, whether the device should be stopped on device_may_wakeup():
> 
> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> that they can generate wakeups even when they're not clocked (a.k.a
> stopped).  So in this case, even after a ->stop_device (which clock
> gates the IP), it can still generate wakeups.
> 
> Second, whether the device should be powered off if device_may_wakeup():
> 
> Embedded SoCs have other ways to wakeup than device-level wakeups.
> 
> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> source So, for example, you could completely power down the UART IP
> blocks (and the enclosing power domain), configure the UART RX pad as a
> wakeup source, and still wakeup the system on UART activity.  The OMAP
> docs call these IO pad wakeups.
> 
> On OMAP in fact, this is the common, default behavior when we enable
> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> but can still wake up the system.
> 
> So in summary, even if device_may_wakeup() is true, many devices (with
> additional SoC magic) can still generate wakeups even when stopped and
> powered off.

Well, on the other hand, on some SoCs there are devices that can't be
powered off (or "declocked") if they are supposed to generate wakeups.
Also, I'm sure there are cases in which wakeups can be generated for devices
with their clocks off, but only if power is present.  So there are multiple
cases, but not so many overall.  So, IMO, it makes sense to handle that at
the generic level, although not necessarily in such a simplistic way.

Now, at this point, I want to do something very simple, which I think is
done by this patch.  Is this optimal power comsumption-wise for every potential
user of the framework?  No, but certainly for some it's sufficient.  Is it
going to work in general?  I think it is.

Of course, there's the question how to handle that more accurately and I have
some ideas.  If you have any, please let me know.

In the meantime, I'm going to modify the changelog so that it's clear that
it's a "first approximation" thing, like in the patch below.

Thanks,
Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions

There is the problem how to handle devices set up to wake up the
system from sleep states during system-wide power transitions.
In some cases, those devices can be turned off entirely, because the
wakeup signals will be generated on their behalf anyway.  In some
other cases, they will generate wakeup signals if their clocks are
stopped, but only if power is not removed from them.  Finally, in
some cases, they can only generate wakeup signals if power is not
removed from them and their clocks are enabled.

In the future, it will be necessary to take all of the above
situations into account, but for starters it is possible to use
the observation that if all wakeup devices are treated like the
last group (i.e. their clocks are enabled and power in not removed
from them during system suspend transitions), they all will be able
to generate wakeups, although power consumption in the resulting
system sleep state may not be optimal in some cases.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    6 ++++++
 1 file changed, 6 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -450,6 +450,9 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -670,6 +673,9 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 19:37         ` Rafael J. Wysocki
@ 2011-06-30 22:42           ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-30 22:42 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Thursday, June 30, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Devices that are set up to wake up the system from sleep states
>> > should not be stopped and power should not be removed from them
>> > when the system goes into a sleep state.  
>> 
>> I don't think this belongs in the generic layer since the two
>> assumptions above are not generally true on embedded systems, and would
>> result in rather significant power consumption unnecessarily.
>
> As to whether or not this belongs to the generic layer, I don't quite agree
> (see below), but the changelog seems to be a bit inaccurate.
>
>> First, whether the device should be stopped on device_may_wakeup():
>> b
>> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> that they can generate wakeups even when they're not clocked (a.k.a
>> stopped).  So in this case, even after a ->stop_device (which clock
>> gates the IP), it can still generate wakeups.
>> 
>> Second, whether the device should be powered off if device_may_wakeup():
>> 
>> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> 
>> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> source So, for example, you could completely power down the UART IP
>> blocks (and the enclosing power domain), configure the UART RX pad as a
>> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> docs call these IO pad wakeups.
>> 
>> On OMAP in fact, this is the common, default behavior when we enable
>> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> but can still wake up the system.
>> 
>> So in summary, even if device_may_wakeup() is true, many devices (with
>> additional SoC magic) can still generate wakeups even when stopped and
>> powered off.
>
> Well, on the other hand, on some SoCs there are devices that can't be
> powered off (or "declocked") if they are supposed to generate wakeups.

Correct.

> Also, I'm sure there are cases in which wakeups can be generated for devices
> with their clocks off, but only if power is present.  

Yes.

> So there are multiple
> cases, but not so many overall.  So, IMO, it makes sense to handle that at
> the generic level, although not necessarily in such a simplistic way.
>
> Now, at this point, I want to do something very simple, which I think is
> done by this patch. 
>
> Is this optimal power comsumption-wise for every potential
> user of the framework?  

Well, sub-optimal would be an understatement.  I would consider this a
major regression since if we were to use this for OMAP, we would never
hit the full-chip low-power states if *any* device had wakeups enabled,
whereas today we can.

> No, but certainly for some it's sufficient.  Is it
> going to work in general?  I think it is.
>
> Of course, there's the question how to handle that more accurately and I have
> some ideas.  If you have any, please let me know.
>
> In the meantime, I'm going to modify the changelog so that it's clear that
> it's a "first approximation" thing, like in the patch below.
>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>
> There is the problem how to handle devices set up to wake up the
> system from sleep states during system-wide power transitions.
> In some cases, those devices can be turned off entirely, because the
> wakeup signals will be generated on their behalf anyway.  In some
> other cases, they will generate wakeup signals if their clocks are
> stopped, but only if power is not removed from them.  Finally, in
> some cases, they can only generate wakeup signals if power is not
> removed from them and their clocks are enabled.

That's a good summary.

> In the future, it will be necessary to take all of the above
> situations into account, but for starters it is possible to use
> the observation that if all wakeup devices are treated like the
> last group (i.e. their clocks are enabled and power in not removed
> from them during system suspend transitions), they all will be able
> to generate wakeups, although power consumption in the resulting
> system sleep state may not be optimal in some cases.

I'm not opposed to this kind of check happening.  I'm only opposed to it
happening in this "generic" layer because..., well, it's not generic.

Not only is it not generic, it would be a major regression in power
consumption for anyone moving to this layer that has the various
different wakeup capabilities already described.

The decision of whether or not to clock gate and/or power gate based on
wakeup capabilies has to be made somewhere (and in fact is already made
by existing code.)  But IMO, that decision should only be made where
wakeup capabilies are known, so that sensible decisions (for power
management) can be made.

Until there is a way in the generic code to distinguish between the
various ways a device can wakeup, this decision should be left up to the
code that knows how.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-30 22:42           ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-30 22:42 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Thursday, June 30, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Devices that are set up to wake up the system from sleep states
>> > should not be stopped and power should not be removed from them
>> > when the system goes into a sleep state.  
>> 
>> I don't think this belongs in the generic layer since the two
>> assumptions above are not generally true on embedded systems, and would
>> result in rather significant power consumption unnecessarily.
>
> As to whether or not this belongs to the generic layer, I don't quite agree
> (see below), but the changelog seems to be a bit inaccurate.
>
>> First, whether the device should be stopped on device_may_wakeup():
>> b
>> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> that they can generate wakeups even when they're not clocked (a.k.a
>> stopped).  So in this case, even after a ->stop_device (which clock
>> gates the IP), it can still generate wakeups.
>> 
>> Second, whether the device should be powered off if device_may_wakeup():
>> 
>> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> 
>> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> source So, for example, you could completely power down the UART IP
>> blocks (and the enclosing power domain), configure the UART RX pad as a
>> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> docs call these IO pad wakeups.
>> 
>> On OMAP in fact, this is the common, default behavior when we enable
>> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> but can still wake up the system.
>> 
>> So in summary, even if device_may_wakeup() is true, many devices (with
>> additional SoC magic) can still generate wakeups even when stopped and
>> powered off.
>
> Well, on the other hand, on some SoCs there are devices that can't be
> powered off (or "declocked") if they are supposed to generate wakeups.

Correct.

> Also, I'm sure there are cases in which wakeups can be generated for devices
> with their clocks off, but only if power is present.  

Yes.

> So there are multiple
> cases, but not so many overall.  So, IMO, it makes sense to handle that at
> the generic level, although not necessarily in such a simplistic way.
>
> Now, at this point, I want to do something very simple, which I think is
> done by this patch. 
>
> Is this optimal power comsumption-wise for every potential
> user of the framework?  

Well, sub-optimal would be an understatement.  I would consider this a
major regression since if we were to use this for OMAP, we would never
hit the full-chip low-power states if *any* device had wakeups enabled,
whereas today we can.

> No, but certainly for some it's sufficient.  Is it
> going to work in general?  I think it is.
>
> Of course, there's the question how to handle that more accurately and I have
> some ideas.  If you have any, please let me know.
>
> In the meantime, I'm going to modify the changelog so that it's clear that
> it's a "first approximation" thing, like in the patch below.
>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>
> There is the problem how to handle devices set up to wake up the
> system from sleep states during system-wide power transitions.
> In some cases, those devices can be turned off entirely, because the
> wakeup signals will be generated on their behalf anyway.  In some
> other cases, they will generate wakeup signals if their clocks are
> stopped, but only if power is not removed from them.  Finally, in
> some cases, they can only generate wakeup signals if power is not
> removed from them and their clocks are enabled.

That's a good summary.

> In the future, it will be necessary to take all of the above
> situations into account, but for starters it is possible to use
> the observation that if all wakeup devices are treated like the
> last group (i.e. their clocks are enabled and power in not removed
> from them during system suspend transitions), they all will be able
> to generate wakeups, although power consumption in the resulting
> system sleep state may not be optimal in some cases.

I'm not opposed to this kind of check happening.  I'm only opposed to it
happening in this "generic" layer because..., well, it's not generic.

Not only is it not generic, it would be a major regression in power
consumption for anyone moving to this layer that has the various
different wakeup capabilities already described.

The decision of whether or not to clock gate and/or power gate based on
wakeup capabilies has to be made somewhere (and in fact is already made
by existing code.)  But IMO, that decision should only be made where
wakeup capabilies are known, so that sensible decisions (for power
management) can be made.

Until there is a way in the generic code to distinguish between the
various ways a device can wakeup, this decision should be left up to the
code that knows how.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 19:37         ` Rafael J. Wysocki
  (?)
@ 2011-06-30 22:42         ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-30 22:42 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Thursday, June 30, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >
>> > Devices that are set up to wake up the system from sleep states
>> > should not be stopped and power should not be removed from them
>> > when the system goes into a sleep state.  
>> 
>> I don't think this belongs in the generic layer since the two
>> assumptions above are not generally true on embedded systems, and would
>> result in rather significant power consumption unnecessarily.
>
> As to whether or not this belongs to the generic layer, I don't quite agree
> (see below), but the changelog seems to be a bit inaccurate.
>
>> First, whether the device should be stopped on device_may_wakeup():
>> b
>> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> that they can generate wakeups even when they're not clocked (a.k.a
>> stopped).  So in this case, even after a ->stop_device (which clock
>> gates the IP), it can still generate wakeups.
>> 
>> Second, whether the device should be powered off if device_may_wakeup():
>> 
>> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> 
>> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> source So, for example, you could completely power down the UART IP
>> blocks (and the enclosing power domain), configure the UART RX pad as a
>> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> docs call these IO pad wakeups.
>> 
>> On OMAP in fact, this is the common, default behavior when we enable
>> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> but can still wake up the system.
>> 
>> So in summary, even if device_may_wakeup() is true, many devices (with
>> additional SoC magic) can still generate wakeups even when stopped and
>> powered off.
>
> Well, on the other hand, on some SoCs there are devices that can't be
> powered off (or "declocked") if they are supposed to generate wakeups.

Correct.

> Also, I'm sure there are cases in which wakeups can be generated for devices
> with their clocks off, but only if power is present.  

Yes.

> So there are multiple
> cases, but not so many overall.  So, IMO, it makes sense to handle that at
> the generic level, although not necessarily in such a simplistic way.
>
> Now, at this point, I want to do something very simple, which I think is
> done by this patch. 
>
> Is this optimal power comsumption-wise for every potential
> user of the framework?  

Well, sub-optimal would be an understatement.  I would consider this a
major regression since if we were to use this for OMAP, we would never
hit the full-chip low-power states if *any* device had wakeups enabled,
whereas today we can.

> No, but certainly for some it's sufficient.  Is it
> going to work in general?  I think it is.
>
> Of course, there's the question how to handle that more accurately and I have
> some ideas.  If you have any, please let me know.
>
> In the meantime, I'm going to modify the changelog so that it's clear that
> it's a "first approximation" thing, like in the patch below.
>
> Thanks,
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>
> There is the problem how to handle devices set up to wake up the
> system from sleep states during system-wide power transitions.
> In some cases, those devices can be turned off entirely, because the
> wakeup signals will be generated on their behalf anyway.  In some
> other cases, they will generate wakeup signals if their clocks are
> stopped, but only if power is not removed from them.  Finally, in
> some cases, they can only generate wakeup signals if power is not
> removed from them and their clocks are enabled.

That's a good summary.

> In the future, it will be necessary to take all of the above
> situations into account, but for starters it is possible to use
> the observation that if all wakeup devices are treated like the
> last group (i.e. their clocks are enabled and power in not removed
> from them during system suspend transitions), they all will be able
> to generate wakeups, although power consumption in the resulting
> system sleep state may not be optimal in some cases.

I'm not opposed to this kind of check happening.  I'm only opposed to it
happening in this "generic" layer because..., well, it's not generic.

Not only is it not generic, it would be a major regression in power
consumption for anyone moving to this layer that has the various
different wakeup capabilities already described.

The decision of whether or not to clock gate and/or power gate based on
wakeup capabilies has to be made somewhere (and in fact is already made
by existing code.)  But IMO, that decision should only be made where
wakeup capabilies are known, so that sensible decisions (for power
management) can be made.

Until there is a way in the generic code to distinguish between the
various ways a device can wakeup, this decision should be left up to the
code that knows how.

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 22:42           ` Kevin Hilman
@ 2011-06-30 22:55             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 22:55 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Thursday, June 30, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Devices that are set up to wake up the system from sleep states
> >> > should not be stopped and power should not be removed from them
> >> > when the system goes into a sleep state.  
> >> 
> >> I don't think this belongs in the generic layer since the two
> >> assumptions above are not generally true on embedded systems, and would
> >> result in rather significant power consumption unnecessarily.
> >
> > As to whether or not this belongs to the generic layer, I don't quite agree
> > (see below), but the changelog seems to be a bit inaccurate.
> >
> >> First, whether the device should be stopped on device_may_wakeup():
> >> b
> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> >> that they can generate wakeups even when they're not clocked (a.k.a
> >> stopped).  So in this case, even after a ->stop_device (which clock
> >> gates the IP), it can still generate wakeups.
> >> 
> >> Second, whether the device should be powered off if device_may_wakeup():
> >> 
> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
> >> 
> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> >> source So, for example, you could completely power down the UART IP
> >> blocks (and the enclosing power domain), configure the UART RX pad as a
> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
> >> docs call these IO pad wakeups.
> >> 
> >> On OMAP in fact, this is the common, default behavior when we enable
> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> >> but can still wake up the system.
> >> 
> >> So in summary, even if device_may_wakeup() is true, many devices (with
> >> additional SoC magic) can still generate wakeups even when stopped and
> >> powered off.
> >
> > Well, on the other hand, on some SoCs there are devices that can't be
> > powered off (or "declocked") if they are supposed to generate wakeups.
> 
> Correct.
> 
> > Also, I'm sure there are cases in which wakeups can be generated for devices
> > with their clocks off, but only if power is present.  
> 
> Yes.
> 
> > So there are multiple
> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
> > the generic level, although not necessarily in such a simplistic way.
> >
> > Now, at this point, I want to do something very simple, which I think is
> > done by this patch. 
> >
> > Is this optimal power comsumption-wise for every potential
> > user of the framework?  
> 
> Well, sub-optimal would be an understatement.  I would consider this a
> major regression since if we were to use this for OMAP, we would never
> hit the full-chip low-power states if *any* device had wakeups enabled,
> whereas today we can.
> 
> > No, but certainly for some it's sufficient.  Is it
> > going to work in general?  I think it is.
> >
> > Of course, there's the question how to handle that more accurately and I have
> > some ideas.  If you have any, please let me know.
> >
> > In the meantime, I'm going to modify the changelog so that it's clear that
> > it's a "first approximation" thing, like in the patch below.
> >
> > Thanks,
> > Rafael
> >
> > ---
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
> >
> > There is the problem how to handle devices set up to wake up the
> > system from sleep states during system-wide power transitions.
> > In some cases, those devices can be turned off entirely, because the
> > wakeup signals will be generated on their behalf anyway.  In some
> > other cases, they will generate wakeup signals if their clocks are
> > stopped, but only if power is not removed from them.  Finally, in
> > some cases, they can only generate wakeup signals if power is not
> > removed from them and their clocks are enabled.
> 
> That's a good summary.
> 
> > In the future, it will be necessary to take all of the above
> > situations into account, but for starters it is possible to use
> > the observation that if all wakeup devices are treated like the
> > last group (i.e. their clocks are enabled and power in not removed
> > from them during system suspend transitions), they all will be able
> > to generate wakeups, although power consumption in the resulting
> > system sleep state may not be optimal in some cases.
> 
> I'm not opposed to this kind of check happening.  I'm only opposed to it
> happening in this "generic" layer because..., well, it's not generic.
> 
> Not only is it not generic, it would be a major regression in power
> consumption for anyone moving to this layer that has the various
> different wakeup capabilities already described.
> 
> The decision of whether or not to clock gate and/or power gate based on
> wakeup capabilies has to be made somewhere (and in fact is already made
> by existing code.)  But IMO, that decision should only be made where
> wakeup capabilies are known, so that sensible decisions (for power
> management) can be made.
> 
> Until there is a way in the generic code to distinguish between the
> various ways a device can wakeup, this decision should be left up to the
> code that knows how.

OK, so I suppose your suggestion is to drop the patch and let the
.stop_device() and .power_off() PM domain callbacks to hand that, is this
correct?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-30 22:55             ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 22:55 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Thursday, June 30, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Devices that are set up to wake up the system from sleep states
> >> > should not be stopped and power should not be removed from them
> >> > when the system goes into a sleep state.  
> >> 
> >> I don't think this belongs in the generic layer since the two
> >> assumptions above are not generally true on embedded systems, and would
> >> result in rather significant power consumption unnecessarily.
> >
> > As to whether or not this belongs to the generic layer, I don't quite agree
> > (see below), but the changelog seems to be a bit inaccurate.
> >
> >> First, whether the device should be stopped on device_may_wakeup():
> >> b
> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> >> that they can generate wakeups even when they're not clocked (a.k.a
> >> stopped).  So in this case, even after a ->stop_device (which clock
> >> gates the IP), it can still generate wakeups.
> >> 
> >> Second, whether the device should be powered off if device_may_wakeup():
> >> 
> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
> >> 
> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> >> source So, for example, you could completely power down the UART IP
> >> blocks (and the enclosing power domain), configure the UART RX pad as a
> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
> >> docs call these IO pad wakeups.
> >> 
> >> On OMAP in fact, this is the common, default behavior when we enable
> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> >> but can still wake up the system.
> >> 
> >> So in summary, even if device_may_wakeup() is true, many devices (with
> >> additional SoC magic) can still generate wakeups even when stopped and
> >> powered off.
> >
> > Well, on the other hand, on some SoCs there are devices that can't be
> > powered off (or "declocked") if they are supposed to generate wakeups.
> 
> Correct.
> 
> > Also, I'm sure there are cases in which wakeups can be generated for devices
> > with their clocks off, but only if power is present.  
> 
> Yes.
> 
> > So there are multiple
> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
> > the generic level, although not necessarily in such a simplistic way.
> >
> > Now, at this point, I want to do something very simple, which I think is
> > done by this patch. 
> >
> > Is this optimal power comsumption-wise for every potential
> > user of the framework?  
> 
> Well, sub-optimal would be an understatement.  I would consider this a
> major regression since if we were to use this for OMAP, we would never
> hit the full-chip low-power states if *any* device had wakeups enabled,
> whereas today we can.
> 
> > No, but certainly for some it's sufficient.  Is it
> > going to work in general?  I think it is.
> >
> > Of course, there's the question how to handle that more accurately and I have
> > some ideas.  If you have any, please let me know.
> >
> > In the meantime, I'm going to modify the changelog so that it's clear that
> > it's a "first approximation" thing, like in the patch below.
> >
> > Thanks,
> > Rafael
> >
> > ---
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
> >
> > There is the problem how to handle devices set up to wake up the
> > system from sleep states during system-wide power transitions.
> > In some cases, those devices can be turned off entirely, because the
> > wakeup signals will be generated on their behalf anyway.  In some
> > other cases, they will generate wakeup signals if their clocks are
> > stopped, but only if power is not removed from them.  Finally, in
> > some cases, they can only generate wakeup signals if power is not
> > removed from them and their clocks are enabled.
> 
> That's a good summary.
> 
> > In the future, it will be necessary to take all of the above
> > situations into account, but for starters it is possible to use
> > the observation that if all wakeup devices are treated like the
> > last group (i.e. their clocks are enabled and power in not removed
> > from them during system suspend transitions), they all will be able
> > to generate wakeups, although power consumption in the resulting
> > system sleep state may not be optimal in some cases.
> 
> I'm not opposed to this kind of check happening.  I'm only opposed to it
> happening in this "generic" layer because..., well, it's not generic.
> 
> Not only is it not generic, it would be a major regression in power
> consumption for anyone moving to this layer that has the various
> different wakeup capabilities already described.
> 
> The decision of whether or not to clock gate and/or power gate based on
> wakeup capabilies has to be made somewhere (and in fact is already made
> by existing code.)  But IMO, that decision should only be made where
> wakeup capabilies are known, so that sensible decisions (for power
> management) can be made.
> 
> Until there is a way in the generic code to distinguish between the
> various ways a device can wakeup, this decision should be left up to the
> code that knows how.

OK, so I suppose your suggestion is to drop the patch and let the
.stop_device() and .power_off() PM domain callbacks to hand that, is this
correct?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 22:42           ` Kevin Hilman
  (?)
@ 2011-06-30 22:55           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 22:55 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Thursday, June 30, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >
> >> > Devices that are set up to wake up the system from sleep states
> >> > should not be stopped and power should not be removed from them
> >> > when the system goes into a sleep state.  
> >> 
> >> I don't think this belongs in the generic layer since the two
> >> assumptions above are not generally true on embedded systems, and would
> >> result in rather significant power consumption unnecessarily.
> >
> > As to whether or not this belongs to the generic layer, I don't quite agree
> > (see below), but the changelog seems to be a bit inaccurate.
> >
> >> First, whether the device should be stopped on device_may_wakeup():
> >> b
> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> >> that they can generate wakeups even when they're not clocked (a.k.a
> >> stopped).  So in this case, even after a ->stop_device (which clock
> >> gates the IP), it can still generate wakeups.
> >> 
> >> Second, whether the device should be powered off if device_may_wakeup():
> >> 
> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
> >> 
> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> >> source So, for example, you could completely power down the UART IP
> >> blocks (and the enclosing power domain), configure the UART RX pad as a
> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
> >> docs call these IO pad wakeups.
> >> 
> >> On OMAP in fact, this is the common, default behavior when we enable
> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> >> but can still wake up the system.
> >> 
> >> So in summary, even if device_may_wakeup() is true, many devices (with
> >> additional SoC magic) can still generate wakeups even when stopped and
> >> powered off.
> >
> > Well, on the other hand, on some SoCs there are devices that can't be
> > powered off (or "declocked") if they are supposed to generate wakeups.
> 
> Correct.
> 
> > Also, I'm sure there are cases in which wakeups can be generated for devices
> > with their clocks off, but only if power is present.  
> 
> Yes.
> 
> > So there are multiple
> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
> > the generic level, although not necessarily in such a simplistic way.
> >
> > Now, at this point, I want to do something very simple, which I think is
> > done by this patch. 
> >
> > Is this optimal power comsumption-wise for every potential
> > user of the framework?  
> 
> Well, sub-optimal would be an understatement.  I would consider this a
> major regression since if we were to use this for OMAP, we would never
> hit the full-chip low-power states if *any* device had wakeups enabled,
> whereas today we can.
> 
> > No, but certainly for some it's sufficient.  Is it
> > going to work in general?  I think it is.
> >
> > Of course, there's the question how to handle that more accurately and I have
> > some ideas.  If you have any, please let me know.
> >
> > In the meantime, I'm going to modify the changelog so that it's clear that
> > it's a "first approximation" thing, like in the patch below.
> >
> > Thanks,
> > Rafael
> >
> > ---
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
> >
> > There is the problem how to handle devices set up to wake up the
> > system from sleep states during system-wide power transitions.
> > In some cases, those devices can be turned off entirely, because the
> > wakeup signals will be generated on their behalf anyway.  In some
> > other cases, they will generate wakeup signals if their clocks are
> > stopped, but only if power is not removed from them.  Finally, in
> > some cases, they can only generate wakeup signals if power is not
> > removed from them and their clocks are enabled.
> 
> That's a good summary.
> 
> > In the future, it will be necessary to take all of the above
> > situations into account, but for starters it is possible to use
> > the observation that if all wakeup devices are treated like the
> > last group (i.e. their clocks are enabled and power in not removed
> > from them during system suspend transitions), they all will be able
> > to generate wakeups, although power consumption in the resulting
> > system sleep state may not be optimal in some cases.
> 
> I'm not opposed to this kind of check happening.  I'm only opposed to it
> happening in this "generic" layer because..., well, it's not generic.
> 
> Not only is it not generic, it would be a major regression in power
> consumption for anyone moving to this layer that has the various
> different wakeup capabilities already described.
> 
> The decision of whether or not to clock gate and/or power gate based on
> wakeup capabilies has to be made somewhere (and in fact is already made
> by existing code.)  But IMO, that decision should only be made where
> wakeup capabilies are known, so that sensible decisions (for power
> management) can be made.
> 
> Until there is a way in the generic code to distinguish between the
> various ways a device can wakeup, this decision should be left up to the
> code that knows how.

OK, so I suppose your suggestion is to drop the patch and let the
.stop_device() and .power_off() PM domain callbacks to hand that, is this
correct?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 22:55             ` Rafael J. Wysocki
@ 2011-06-30 23:14               ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-30 23:14 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > On Thursday, June 30, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> 
>> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> >
>> >> > Devices that are set up to wake up the system from sleep states
>> >> > should not be stopped and power should not be removed from them
>> >> > when the system goes into a sleep state.  
>> >> 
>> >> I don't think this belongs in the generic layer since the two
>> >> assumptions above are not generally true on embedded systems, and would
>> >> result in rather significant power consumption unnecessarily.
>> >
>> > As to whether or not this belongs to the generic layer, I don't quite agree
>> > (see below), but the changelog seems to be a bit inaccurate.
>> >
>> >> First, whether the device should be stopped on device_may_wakeup():
>> >> b
>> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> >> that they can generate wakeups even when they're not clocked (a.k.a
>> >> stopped).  So in this case, even after a ->stop_device (which clock
>> >> gates the IP), it can still generate wakeups.
>> >> 
>> >> Second, whether the device should be powered off if device_may_wakeup():
>> >> 
>> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> >> 
>> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> >> source So, for example, you could completely power down the UART IP
>> >> blocks (and the enclosing power domain), configure the UART RX pad as a
>> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> >> docs call these IO pad wakeups.
>> >> 
>> >> On OMAP in fact, this is the common, default behavior when we enable
>> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> >> but can still wake up the system.
>> >> 
>> >> So in summary, even if device_may_wakeup() is true, many devices (with
>> >> additional SoC magic) can still generate wakeups even when stopped and
>> >> powered off.
>> >
>> > Well, on the other hand, on some SoCs there are devices that can't be
>> > powered off (or "declocked") if they are supposed to generate wakeups.
>> 
>> Correct.
>> 
>> > Also, I'm sure there are cases in which wakeups can be generated for devices
>> > with their clocks off, but only if power is present.  
>> 
>> Yes.
>> 
>> > So there are multiple
>> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
>> > the generic level, although not necessarily in such a simplistic way.
>> >
>> > Now, at this point, I want to do something very simple, which I think is
>> > done by this patch. 
>> >
>> > Is this optimal power comsumption-wise for every potential
>> > user of the framework?  
>> 
>> Well, sub-optimal would be an understatement.  I would consider this a
>> major regression since if we were to use this for OMAP, we would never
>> hit the full-chip low-power states if *any* device had wakeups enabled,
>> whereas today we can.
>> 
>> > No, but certainly for some it's sufficient.  Is it
>> > going to work in general?  I think it is.
>> >
>> > Of course, there's the question how to handle that more accurately and I have
>> > some ideas.  If you have any, please let me know.
>> >
>> > In the meantime, I'm going to modify the changelog so that it's clear that
>> > it's a "first approximation" thing, like in the patch below.
>> >
>> > Thanks,
>> > Rafael
>> >
>> > ---
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>> >
>> > There is the problem how to handle devices set up to wake up the
>> > system from sleep states during system-wide power transitions.
>> > In some cases, those devices can be turned off entirely, because the
>> > wakeup signals will be generated on their behalf anyway.  In some
>> > other cases, they will generate wakeup signals if their clocks are
>> > stopped, but only if power is not removed from them.  Finally, in
>> > some cases, they can only generate wakeup signals if power is not
>> > removed from them and their clocks are enabled.
>> 
>> That's a good summary.
>> 
>> > In the future, it will be necessary to take all of the above
>> > situations into account, but for starters it is possible to use
>> > the observation that if all wakeup devices are treated like the
>> > last group (i.e. their clocks are enabled and power in not removed
>> > from them during system suspend transitions), they all will be able
>> > to generate wakeups, although power consumption in the resulting
>> > system sleep state may not be optimal in some cases.
>> 
>> I'm not opposed to this kind of check happening.  I'm only opposed to it
>> happening in this "generic" layer because..., well, it's not generic.
>> 
>> Not only is it not generic, it would be a major regression in power
>> consumption for anyone moving to this layer that has the various
>> different wakeup capabilities already described.
>> 
>> The decision of whether or not to clock gate and/or power gate based on
>> wakeup capabilies has to be made somewhere (and in fact is already made
>> by existing code.)  But IMO, that decision should only be made where
>> wakeup capabilies are known, so that sensible decisions (for power
>> management) can be made.
>> 
>> Until there is a way in the generic code to distinguish between the
>> various ways a device can wakeup, this decision should be left up to the
>> code that knows how.
>
> OK, so I suppose your suggestion is to drop the patch and let the
> .stop_device() and .power_off() PM domain callbacks to hand that, is this
> correct?

Correct.

Initially I was thinking only about .power_off(), but you'd probably
want this at .stop_device() too.  In order to do that, probably want
.stop_device() to be able to return an error code such that an error
would prevent .power_off().

Kevin



^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-30 23:14               ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-30 23:14 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > On Thursday, June 30, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> 
>> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> >
>> >> > Devices that are set up to wake up the system from sleep states
>> >> > should not be stopped and power should not be removed from them
>> >> > when the system goes into a sleep state.  
>> >> 
>> >> I don't think this belongs in the generic layer since the two
>> >> assumptions above are not generally true on embedded systems, and would
>> >> result in rather significant power consumption unnecessarily.
>> >
>> > As to whether or not this belongs to the generic layer, I don't quite agree
>> > (see below), but the changelog seems to be a bit inaccurate.
>> >
>> >> First, whether the device should be stopped on device_may_wakeup():
>> >> b
>> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> >> that they can generate wakeups even when they're not clocked (a.k.a
>> >> stopped).  So in this case, even after a ->stop_device (which clock
>> >> gates the IP), it can still generate wakeups.
>> >> 
>> >> Second, whether the device should be powered off if device_may_wakeup():
>> >> 
>> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> >> 
>> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> >> source So, for example, you could completely power down the UART IP
>> >> blocks (and the enclosing power domain), configure the UART RX pad as a
>> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> >> docs call these IO pad wakeups.
>> >> 
>> >> On OMAP in fact, this is the common, default behavior when we enable
>> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> >> but can still wake up the system.
>> >> 
>> >> So in summary, even if device_may_wakeup() is true, many devices (with
>> >> additional SoC magic) can still generate wakeups even when stopped and
>> >> powered off.
>> >
>> > Well, on the other hand, on some SoCs there are devices that can't be
>> > powered off (or "declocked") if they are supposed to generate wakeups.
>> 
>> Correct.
>> 
>> > Also, I'm sure there are cases in which wakeups can be generated for devices
>> > with their clocks off, but only if power is present.  
>> 
>> Yes.
>> 
>> > So there are multiple
>> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
>> > the generic level, although not necessarily in such a simplistic way.
>> >
>> > Now, at this point, I want to do something very simple, which I think is
>> > done by this patch. 
>> >
>> > Is this optimal power comsumption-wise for every potential
>> > user of the framework?  
>> 
>> Well, sub-optimal would be an understatement.  I would consider this a
>> major regression since if we were to use this for OMAP, we would never
>> hit the full-chip low-power states if *any* device had wakeups enabled,
>> whereas today we can.
>> 
>> > No, but certainly for some it's sufficient.  Is it
>> > going to work in general?  I think it is.
>> >
>> > Of course, there's the question how to handle that more accurately and I have
>> > some ideas.  If you have any, please let me know.
>> >
>> > In the meantime, I'm going to modify the changelog so that it's clear that
>> > it's a "first approximation" thing, like in the patch below.
>> >
>> > Thanks,
>> > Rafael
>> >
>> > ---
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>> >
>> > There is the problem how to handle devices set up to wake up the
>> > system from sleep states during system-wide power transitions.
>> > In some cases, those devices can be turned off entirely, because the
>> > wakeup signals will be generated on their behalf anyway.  In some
>> > other cases, they will generate wakeup signals if their clocks are
>> > stopped, but only if power is not removed from them.  Finally, in
>> > some cases, they can only generate wakeup signals if power is not
>> > removed from them and their clocks are enabled.
>> 
>> That's a good summary.
>> 
>> > In the future, it will be necessary to take all of the above
>> > situations into account, but for starters it is possible to use
>> > the observation that if all wakeup devices are treated like the
>> > last group (i.e. their clocks are enabled and power in not removed
>> > from them during system suspend transitions), they all will be able
>> > to generate wakeups, although power consumption in the resulting
>> > system sleep state may not be optimal in some cases.
>> 
>> I'm not opposed to this kind of check happening.  I'm only opposed to it
>> happening in this "generic" layer because..., well, it's not generic.
>> 
>> Not only is it not generic, it would be a major regression in power
>> consumption for anyone moving to this layer that has the various
>> different wakeup capabilities already described.
>> 
>> The decision of whether or not to clock gate and/or power gate based on
>> wakeup capabilies has to be made somewhere (and in fact is already made
>> by existing code.)  But IMO, that decision should only be made where
>> wakeup capabilies are known, so that sensible decisions (for power
>> management) can be made.
>> 
>> Until there is a way in the generic code to distinguish between the
>> various ways a device can wakeup, this decision should be left up to the
>> code that knows how.
>
> OK, so I suppose your suggestion is to drop the patch and let the
> .stop_device() and .power_off() PM domain callbacks to hand that, is this
> correct?

Correct.

Initially I was thinking only about .power_off(), but you'd probably
want this at .stop_device() too.  In order to do that, probably want
.stop_device() to be able to return an error code such that an error
would prevent .power_off().

Kevin



^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 22:55             ` Rafael J. Wysocki
  (?)
@ 2011-06-30 23:14             ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-06-30 23:14 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > On Thursday, June 30, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> 
>> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> >
>> >> > Devices that are set up to wake up the system from sleep states
>> >> > should not be stopped and power should not be removed from them
>> >> > when the system goes into a sleep state.  
>> >> 
>> >> I don't think this belongs in the generic layer since the two
>> >> assumptions above are not generally true on embedded systems, and would
>> >> result in rather significant power consumption unnecessarily.
>> >
>> > As to whether or not this belongs to the generic layer, I don't quite agree
>> > (see below), but the changelog seems to be a bit inaccurate.
>> >
>> >> First, whether the device should be stopped on device_may_wakeup():
>> >> b
>> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> >> that they can generate wakeups even when they're not clocked (a.k.a
>> >> stopped).  So in this case, even after a ->stop_device (which clock
>> >> gates the IP), it can still generate wakeups.
>> >> 
>> >> Second, whether the device should be powered off if device_may_wakeup():
>> >> 
>> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> >> 
>> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> >> source So, for example, you could completely power down the UART IP
>> >> blocks (and the enclosing power domain), configure the UART RX pad as a
>> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> >> docs call these IO pad wakeups.
>> >> 
>> >> On OMAP in fact, this is the common, default behavior when we enable
>> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> >> but can still wake up the system.
>> >> 
>> >> So in summary, even if device_may_wakeup() is true, many devices (with
>> >> additional SoC magic) can still generate wakeups even when stopped and
>> >> powered off.
>> >
>> > Well, on the other hand, on some SoCs there are devices that can't be
>> > powered off (or "declocked") if they are supposed to generate wakeups.
>> 
>> Correct.
>> 
>> > Also, I'm sure there are cases in which wakeups can be generated for devices
>> > with their clocks off, but only if power is present.  
>> 
>> Yes.
>> 
>> > So there are multiple
>> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
>> > the generic level, although not necessarily in such a simplistic way.
>> >
>> > Now, at this point, I want to do something very simple, which I think is
>> > done by this patch. 
>> >
>> > Is this optimal power comsumption-wise for every potential
>> > user of the framework?  
>> 
>> Well, sub-optimal would be an understatement.  I would consider this a
>> major regression since if we were to use this for OMAP, we would never
>> hit the full-chip low-power states if *any* device had wakeups enabled,
>> whereas today we can.
>> 
>> > No, but certainly for some it's sufficient.  Is it
>> > going to work in general?  I think it is.
>> >
>> > Of course, there's the question how to handle that more accurately and I have
>> > some ideas.  If you have any, please let me know.
>> >
>> > In the meantime, I'm going to modify the changelog so that it's clear that
>> > it's a "first approximation" thing, like in the patch below.
>> >
>> > Thanks,
>> > Rafael
>> >
>> > ---
>> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>> >
>> > There is the problem how to handle devices set up to wake up the
>> > system from sleep states during system-wide power transitions.
>> > In some cases, those devices can be turned off entirely, because the
>> > wakeup signals will be generated on their behalf anyway.  In some
>> > other cases, they will generate wakeup signals if their clocks are
>> > stopped, but only if power is not removed from them.  Finally, in
>> > some cases, they can only generate wakeup signals if power is not
>> > removed from them and their clocks are enabled.
>> 
>> That's a good summary.
>> 
>> > In the future, it will be necessary to take all of the above
>> > situations into account, but for starters it is possible to use
>> > the observation that if all wakeup devices are treated like the
>> > last group (i.e. their clocks are enabled and power in not removed
>> > from them during system suspend transitions), they all will be able
>> > to generate wakeups, although power consumption in the resulting
>> > system sleep state may not be optimal in some cases.
>> 
>> I'm not opposed to this kind of check happening.  I'm only opposed to it
>> happening in this "generic" layer because..., well, it's not generic.
>> 
>> Not only is it not generic, it would be a major regression in power
>> consumption for anyone moving to this layer that has the various
>> different wakeup capabilities already described.
>> 
>> The decision of whether or not to clock gate and/or power gate based on
>> wakeup capabilies has to be made somewhere (and in fact is already made
>> by existing code.)  But IMO, that decision should only be made where
>> wakeup capabilies are known, so that sensible decisions (for power
>> management) can be made.
>> 
>> Until there is a way in the generic code to distinguish between the
>> various ways a device can wakeup, this decision should be left up to the
>> code that knows how.
>
> OK, so I suppose your suggestion is to drop the patch and let the
> .stop_device() and .power_off() PM domain callbacks to hand that, is this
> correct?

Correct.

Initially I was thinking only about .power_off(), but you'd probably
want this at .stop_device() too.  In order to do that, probably want
.stop_device() to be able to return an error code such that an error
would prevent .power_off().

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 22:55             ` Rafael J. Wysocki
@ 2011-06-30 23:25               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 23:25 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Rafael J. Wysocki wrote:
> On Friday, July 01, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
...
> > The decision of whether or not to clock gate and/or power gate based on
> > wakeup capabilies has to be made somewhere (and in fact is already made
> > by existing code.)  But IMO, that decision should only be made where
> > wakeup capabilies are known, so that sensible decisions (for power
> > management) can be made.
> > 
> > Until there is a way in the generic code to distinguish between the
> > various ways a device can wakeup, this decision should be left up to the
> > code that knows how.
> 
> OK, so I suppose your suggestion is to drop the patch and let the
> .stop_device() and .power_off() PM domain callbacks to hand

That should have been "handle".

> that, is this correct?

Anyway, neither .stop_device(), nor .power_off() can make such decisions,
because they are used for both runtime PM and system suspend, so they shouldn't
do system suspend-specific checks.

So the only way forward I can see is to add a special PM domain callback,
say .active_wakeup(), that will return "true" if the device is to be left
active if wakeup-enabled.  So the check you don't like will become
something like:

if (device_may_wakeup(dev) && genpd->active_wakeup
    && genpd->active_wakeup(dev))
        return 0;

Would that be better?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-30 23:25               ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 23:25 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Rafael J. Wysocki wrote:
> On Friday, July 01, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
...
> > The decision of whether or not to clock gate and/or power gate based on
> > wakeup capabilies has to be made somewhere (and in fact is already made
> > by existing code.)  But IMO, that decision should only be made where
> > wakeup capabilies are known, so that sensible decisions (for power
> > management) can be made.
> > 
> > Until there is a way in the generic code to distinguish between the
> > various ways a device can wakeup, this decision should be left up to the
> > code that knows how.
> 
> OK, so I suppose your suggestion is to drop the patch and let the
> .stop_device() and .power_off() PM domain callbacks to hand

That should have been "handle".

> that, is this correct?

Anyway, neither .stop_device(), nor .power_off() can make such decisions,
because they are used for both runtime PM and system suspend, so they shouldn't
do system suspend-specific checks.

So the only way forward I can see is to add a special PM domain callback,
say .active_wakeup(), that will return "true" if the device is to be left
active if wakeup-enabled.  So the check you don't like will become
something like:

if (device_may_wakeup(dev) && genpd->active_wakeup
    && genpd->active_wakeup(dev))
        return 0;

Would that be better?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 22:55             ` Rafael J. Wysocki
                               ` (2 preceding siblings ...)
  (?)
@ 2011-06-30 23:25             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 23:25 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 01, 2011, Rafael J. Wysocki wrote:
> On Friday, July 01, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
...
> > The decision of whether or not to clock gate and/or power gate based on
> > wakeup capabilies has to be made somewhere (and in fact is already made
> > by existing code.)  But IMO, that decision should only be made where
> > wakeup capabilies are known, so that sensible decisions (for power
> > management) can be made.
> > 
> > Until there is a way in the generic code to distinguish between the
> > various ways a device can wakeup, this decision should be left up to the
> > code that knows how.
> 
> OK, so I suppose your suggestion is to drop the patch and let the
> .stop_device() and .power_off() PM domain callbacks to hand

That should have been "handle".

> that, is this correct?

Anyway, neither .stop_device(), nor .power_off() can make such decisions,
because they are used for both runtime PM and system suspend, so they shouldn't
do system suspend-specific checks.

So the only way forward I can see is to add a special PM domain callback,
say .active_wakeup(), that will return "true" if the device is to be left
active if wakeup-enabled.  So the check you don't like will become
something like:

if (device_may_wakeup(dev) && genpd->active_wakeup
    && genpd->active_wakeup(dev))
        return 0;

Would that be better?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 23:14               ` Kevin Hilman
@ 2011-06-30 23:28                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 23:28 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Friday, July 01, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > On Thursday, June 30, 2011, Kevin Hilman wrote:
> >> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> >> 
> >> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >> >
> >> >> > Devices that are set up to wake up the system from sleep states
> >> >> > should not be stopped and power should not be removed from them
> >> >> > when the system goes into a sleep state.  
> >> >> 
> >> >> I don't think this belongs in the generic layer since the two
> >> >> assumptions above are not generally true on embedded systems, and would
> >> >> result in rather significant power consumption unnecessarily.
> >> >
> >> > As to whether or not this belongs to the generic layer, I don't quite agree
> >> > (see below), but the changelog seems to be a bit inaccurate.
> >> >
> >> >> First, whether the device should be stopped on device_may_wakeup():
> >> >> b
> >> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> >> >> that they can generate wakeups even when they're not clocked (a.k.a
> >> >> stopped).  So in this case, even after a ->stop_device (which clock
> >> >> gates the IP), it can still generate wakeups.
> >> >> 
> >> >> Second, whether the device should be powered off if device_may_wakeup():
> >> >> 
> >> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
> >> >> 
> >> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> >> >> source So, for example, you could completely power down the UART IP
> >> >> blocks (and the enclosing power domain), configure the UART RX pad as a
> >> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
> >> >> docs call these IO pad wakeups.
> >> >> 
> >> >> On OMAP in fact, this is the common, default behavior when we enable
> >> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> >> >> but can still wake up the system.
> >> >> 
> >> >> So in summary, even if device_may_wakeup() is true, many devices (with
> >> >> additional SoC magic) can still generate wakeups even when stopped and
> >> >> powered off.
> >> >
> >> > Well, on the other hand, on some SoCs there are devices that can't be
> >> > powered off (or "declocked") if they are supposed to generate wakeups.
> >> 
> >> Correct.
> >> 
> >> > Also, I'm sure there are cases in which wakeups can be generated for devices
> >> > with their clocks off, but only if power is present.  
> >> 
> >> Yes.
> >> 
> >> > So there are multiple
> >> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
> >> > the generic level, although not necessarily in such a simplistic way.
> >> >
> >> > Now, at this point, I want to do something very simple, which I think is
> >> > done by this patch. 
> >> >
> >> > Is this optimal power comsumption-wise for every potential
> >> > user of the framework?  
> >> 
> >> Well, sub-optimal would be an understatement.  I would consider this a
> >> major regression since if we were to use this for OMAP, we would never
> >> hit the full-chip low-power states if *any* device had wakeups enabled,
> >> whereas today we can.
> >> 
> >> > No, but certainly for some it's sufficient.  Is it
> >> > going to work in general?  I think it is.
> >> >
> >> > Of course, there's the question how to handle that more accurately and I have
> >> > some ideas.  If you have any, please let me know.
> >> >
> >> > In the meantime, I'm going to modify the changelog so that it's clear that
> >> > it's a "first approximation" thing, like in the patch below.
> >> >
> >> > Thanks,
> >> > Rafael
> >> >
> >> > ---
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
> >> >
> >> > There is the problem how to handle devices set up to wake up the
> >> > system from sleep states during system-wide power transitions.
> >> > In some cases, those devices can be turned off entirely, because the
> >> > wakeup signals will be generated on their behalf anyway.  In some
> >> > other cases, they will generate wakeup signals if their clocks are
> >> > stopped, but only if power is not removed from them.  Finally, in
> >> > some cases, they can only generate wakeup signals if power is not
> >> > removed from them and their clocks are enabled.
> >> 
> >> That's a good summary.
> >> 
> >> > In the future, it will be necessary to take all of the above
> >> > situations into account, but for starters it is possible to use
> >> > the observation that if all wakeup devices are treated like the
> >> > last group (i.e. their clocks are enabled and power in not removed
> >> > from them during system suspend transitions), they all will be able
> >> > to generate wakeups, although power consumption in the resulting
> >> > system sleep state may not be optimal in some cases.
> >> 
> >> I'm not opposed to this kind of check happening.  I'm only opposed to it
> >> happening in this "generic" layer because..., well, it's not generic.
> >> 
> >> Not only is it not generic, it would be a major regression in power
> >> consumption for anyone moving to this layer that has the various
> >> different wakeup capabilities already described.
> >> 
> >> The decision of whether or not to clock gate and/or power gate based on
> >> wakeup capabilies has to be made somewhere (and in fact is already made
> >> by existing code.)  But IMO, that decision should only be made where
> >> wakeup capabilies are known, so that sensible decisions (for power
> >> management) can be made.
> >> 
> >> Until there is a way in the generic code to distinguish between the
> >> various ways a device can wakeup, this decision should be left up to the
> >> code that knows how.
> >
> > OK, so I suppose your suggestion is to drop the patch and let the
> > .stop_device() and .power_off() PM domain callbacks to hand that, is this
> > correct?
> 
> Correct.
> 
> Initially I was thinking only about .power_off(), but you'd probably
> want this at .stop_device() too.  In order to do that, probably want
> .stop_device() to be able to return an error code such that an error
> would prevent .power_off().

I've just sent a reply to that. :-) I'll reproduce it below for easier
reference:

Neither .stop_device(), nor .power_off() can make such decisions,
because they are used for both runtime PM and system suspend, so they
shouldn't do system suspend-specific checks.

So the only way forward I can see is to add a special PM domain callback,
say .active_wakeup(), that will return "true" if the device is to be left
active when wakeup-enabled.  So the check you don't like will become
something like:

if (device_may_wakeup(dev) && genpd->active_wakeup
    && genpd->active_wakeup(dev))
        return 0;

Would that be better?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-06-30 23:28                 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 23:28 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Friday, July 01, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > On Thursday, June 30, 2011, Kevin Hilman wrote:
> >> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> >> 
> >> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >> >
> >> >> > Devices that are set up to wake up the system from sleep states
> >> >> > should not be stopped and power should not be removed from them
> >> >> > when the system goes into a sleep state.  
> >> >> 
> >> >> I don't think this belongs in the generic layer since the two
> >> >> assumptions above are not generally true on embedded systems, and would
> >> >> result in rather significant power consumption unnecessarily.
> >> >
> >> > As to whether or not this belongs to the generic layer, I don't quite agree
> >> > (see below), but the changelog seems to be a bit inaccurate.
> >> >
> >> >> First, whether the device should be stopped on device_may_wakeup():
> >> >> b
> >> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> >> >> that they can generate wakeups even when they're not clocked (a.k.a
> >> >> stopped).  So in this case, even after a ->stop_device (which clock
> >> >> gates the IP), it can still generate wakeups.
> >> >> 
> >> >> Second, whether the device should be powered off if device_may_wakeup():
> >> >> 
> >> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
> >> >> 
> >> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> >> >> source So, for example, you could completely power down the UART IP
> >> >> blocks (and the enclosing power domain), configure the UART RX pad as a
> >> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
> >> >> docs call these IO pad wakeups.
> >> >> 
> >> >> On OMAP in fact, this is the common, default behavior when we enable
> >> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> >> >> but can still wake up the system.
> >> >> 
> >> >> So in summary, even if device_may_wakeup() is true, many devices (with
> >> >> additional SoC magic) can still generate wakeups even when stopped and
> >> >> powered off.
> >> >
> >> > Well, on the other hand, on some SoCs there are devices that can't be
> >> > powered off (or "declocked") if they are supposed to generate wakeups.
> >> 
> >> Correct.
> >> 
> >> > Also, I'm sure there are cases in which wakeups can be generated for devices
> >> > with their clocks off, but only if power is present.  
> >> 
> >> Yes.
> >> 
> >> > So there are multiple
> >> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
> >> > the generic level, although not necessarily in such a simplistic way.
> >> >
> >> > Now, at this point, I want to do something very simple, which I think is
> >> > done by this patch. 
> >> >
> >> > Is this optimal power comsumption-wise for every potential
> >> > user of the framework?  
> >> 
> >> Well, sub-optimal would be an understatement.  I would consider this a
> >> major regression since if we were to use this for OMAP, we would never
> >> hit the full-chip low-power states if *any* device had wakeups enabled,
> >> whereas today we can.
> >> 
> >> > No, but certainly for some it's sufficient.  Is it
> >> > going to work in general?  I think it is.
> >> >
> >> > Of course, there's the question how to handle that more accurately and I have
> >> > some ideas.  If you have any, please let me know.
> >> >
> >> > In the meantime, I'm going to modify the changelog so that it's clear that
> >> > it's a "first approximation" thing, like in the patch below.
> >> >
> >> > Thanks,
> >> > Rafael
> >> >
> >> > ---
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
> >> >
> >> > There is the problem how to handle devices set up to wake up the
> >> > system from sleep states during system-wide power transitions.
> >> > In some cases, those devices can be turned off entirely, because the
> >> > wakeup signals will be generated on their behalf anyway.  In some
> >> > other cases, they will generate wakeup signals if their clocks are
> >> > stopped, but only if power is not removed from them.  Finally, in
> >> > some cases, they can only generate wakeup signals if power is not
> >> > removed from them and their clocks are enabled.
> >> 
> >> That's a good summary.
> >> 
> >> > In the future, it will be necessary to take all of the above
> >> > situations into account, but for starters it is possible to use
> >> > the observation that if all wakeup devices are treated like the
> >> > last group (i.e. their clocks are enabled and power in not removed
> >> > from them during system suspend transitions), they all will be able
> >> > to generate wakeups, although power consumption in the resulting
> >> > system sleep state may not be optimal in some cases.
> >> 
> >> I'm not opposed to this kind of check happening.  I'm only opposed to it
> >> happening in this "generic" layer because..., well, it's not generic.
> >> 
> >> Not only is it not generic, it would be a major regression in power
> >> consumption for anyone moving to this layer that has the various
> >> different wakeup capabilities already described.
> >> 
> >> The decision of whether or not to clock gate and/or power gate based on
> >> wakeup capabilies has to be made somewhere (and in fact is already made
> >> by existing code.)  But IMO, that decision should only be made where
> >> wakeup capabilies are known, so that sensible decisions (for power
> >> management) can be made.
> >> 
> >> Until there is a way in the generic code to distinguish between the
> >> various ways a device can wakeup, this decision should be left up to the
> >> code that knows how.
> >
> > OK, so I suppose your suggestion is to drop the patch and let the
> > .stop_device() and .power_off() PM domain callbacks to hand that, is this
> > correct?
> 
> Correct.
> 
> Initially I was thinking only about .power_off(), but you'd probably
> want this at .stop_device() too.  In order to do that, probably want
> .stop_device() to be able to return an error code such that an error
> would prevent .power_off().

I've just sent a reply to that. :-) I'll reproduce it below for easier
reference:

Neither .stop_device(), nor .power_off() can make such decisions,
because they are used for both runtime PM and system suspend, so they
shouldn't do system suspend-specific checks.

So the only way forward I can see is to add a special PM domain callback,
say .active_wakeup(), that will return "true" if the device is to be left
active when wakeup-enabled.  So the check you don't like will become
something like:

if (device_may_wakeup(dev) && genpd->active_wakeup
    && genpd->active_wakeup(dev))
        return 0;

Would that be better?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 23:14               ` Kevin Hilman
  (?)
@ 2011-06-30 23:28               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-06-30 23:28 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > On Friday, July 01, 2011, Kevin Hilman wrote:
> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> 
> >> > On Thursday, June 30, 2011, Kevin Hilman wrote:
> >> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> >> 
> >> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> >> >
> >> >> > Devices that are set up to wake up the system from sleep states
> >> >> > should not be stopped and power should not be removed from them
> >> >> > when the system goes into a sleep state.  
> >> >> 
> >> >> I don't think this belongs in the generic layer since the two
> >> >> assumptions above are not generally true on embedded systems, and would
> >> >> result in rather significant power consumption unnecessarily.
> >> >
> >> > As to whether or not this belongs to the generic layer, I don't quite agree
> >> > (see below), but the changelog seems to be a bit inaccurate.
> >> >
> >> >> First, whether the device should be stopped on device_may_wakeup():
> >> >> b
> >> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
> >> >> that they can generate wakeups even when they're not clocked (a.k.a
> >> >> stopped).  So in this case, even after a ->stop_device (which clock
> >> >> gates the IP), it can still generate wakeups.
> >> >> 
> >> >> Second, whether the device should be powered off if device_may_wakeup():
> >> >> 
> >> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
> >> >> 
> >> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
> >> >> source So, for example, you could completely power down the UART IP
> >> >> blocks (and the enclosing power domain), configure the UART RX pad as a
> >> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
> >> >> docs call these IO pad wakeups.
> >> >> 
> >> >> On OMAP in fact, this is the common, default behavior when we enable
> >> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
> >> >> but can still wake up the system.
> >> >> 
> >> >> So in summary, even if device_may_wakeup() is true, many devices (with
> >> >> additional SoC magic) can still generate wakeups even when stopped and
> >> >> powered off.
> >> >
> >> > Well, on the other hand, on some SoCs there are devices that can't be
> >> > powered off (or "declocked") if they are supposed to generate wakeups.
> >> 
> >> Correct.
> >> 
> >> > Also, I'm sure there are cases in which wakeups can be generated for devices
> >> > with their clocks off, but only if power is present.  
> >> 
> >> Yes.
> >> 
> >> > So there are multiple
> >> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
> >> > the generic level, although not necessarily in such a simplistic way.
> >> >
> >> > Now, at this point, I want to do something very simple, which I think is
> >> > done by this patch. 
> >> >
> >> > Is this optimal power comsumption-wise for every potential
> >> > user of the framework?  
> >> 
> >> Well, sub-optimal would be an understatement.  I would consider this a
> >> major regression since if we were to use this for OMAP, we would never
> >> hit the full-chip low-power states if *any* device had wakeups enabled,
> >> whereas today we can.
> >> 
> >> > No, but certainly for some it's sufficient.  Is it
> >> > going to work in general?  I think it is.
> >> >
> >> > Of course, there's the question how to handle that more accurately and I have
> >> > some ideas.  If you have any, please let me know.
> >> >
> >> > In the meantime, I'm going to modify the changelog so that it's clear that
> >> > it's a "first approximation" thing, like in the patch below.
> >> >
> >> > Thanks,
> >> > Rafael
> >> >
> >> > ---
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
> >> >
> >> > There is the problem how to handle devices set up to wake up the
> >> > system from sleep states during system-wide power transitions.
> >> > In some cases, those devices can be turned off entirely, because the
> >> > wakeup signals will be generated on their behalf anyway.  In some
> >> > other cases, they will generate wakeup signals if their clocks are
> >> > stopped, but only if power is not removed from them.  Finally, in
> >> > some cases, they can only generate wakeup signals if power is not
> >> > removed from them and their clocks are enabled.
> >> 
> >> That's a good summary.
> >> 
> >> > In the future, it will be necessary to take all of the above
> >> > situations into account, but for starters it is possible to use
> >> > the observation that if all wakeup devices are treated like the
> >> > last group (i.e. their clocks are enabled and power in not removed
> >> > from them during system suspend transitions), they all will be able
> >> > to generate wakeups, although power consumption in the resulting
> >> > system sleep state may not be optimal in some cases.
> >> 
> >> I'm not opposed to this kind of check happening.  I'm only opposed to it
> >> happening in this "generic" layer because..., well, it's not generic.
> >> 
> >> Not only is it not generic, it would be a major regression in power
> >> consumption for anyone moving to this layer that has the various
> >> different wakeup capabilities already described.
> >> 
> >> The decision of whether or not to clock gate and/or power gate based on
> >> wakeup capabilies has to be made somewhere (and in fact is already made
> >> by existing code.)  But IMO, that decision should only be made where
> >> wakeup capabilies are known, so that sensible decisions (for power
> >> management) can be made.
> >> 
> >> Until there is a way in the generic code to distinguish between the
> >> various ways a device can wakeup, this decision should be left up to the
> >> code that knows how.
> >
> > OK, so I suppose your suggestion is to drop the patch and let the
> > .stop_device() and .power_off() PM domain callbacks to hand that, is this
> > correct?
> 
> Correct.
> 
> Initially I was thinking only about .power_off(), but you'd probably
> want this at .stop_device() too.  In order to do that, probably want
> .stop_device() to be able to return an error code such that an error
> would prevent .power_off().

I've just sent a reply to that. :-) I'll reproduce it below for easier
reference:

Neither .stop_device(), nor .power_off() can make such decisions,
because they are used for both runtime PM and system suspend, so they
shouldn't do system suspend-specific checks.

So the only way forward I can see is to add a special PM domain callback,
say .active_wakeup(), that will return "true" if the device is to be left
active when wakeup-enabled.  So the check you don't like will become
something like:

if (device_may_wakeup(dev) && genpd->active_wakeup
    && genpd->active_wakeup(dev))
        return 0;

Would that be better?

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 23:28                 ` Rafael J. Wysocki
  (?)
@ 2011-07-01  0:01                   ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01  0:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > On Friday, July 01, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> 
>> >> > On Thursday, June 30, 2011, Kevin Hilman wrote:
>> >> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> >> 
>> >> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> >> >
>> >> >> > Devices that are set up to wake up the system from sleep states
>> >> >> > should not be stopped and power should not be removed from them
>> >> >> > when the system goes into a sleep state.  
>> >> >> 
>> >> >> I don't think this belongs in the generic layer since the two
>> >> >> assumptions above are not generally true on embedded systems, and would
>> >> >> result in rather significant power consumption unnecessarily.
>> >> >
>> >> > As to whether or not this belongs to the generic layer, I don't quite agree
>> >> > (see below), but the changelog seems to be a bit inaccurate.
>> >> >
>> >> >> First, whether the device should be stopped on device_may_wakeup():
>> >> >> b
>> >> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> >> >> that they can generate wakeups even when they're not clocked (a.k.a
>> >> >> stopped).  So in this case, even after a ->stop_device (which clock
>> >> >> gates the IP), it can still generate wakeups.
>> >> >> 
>> >> >> Second, whether the device should be powered off if device_may_wakeup():
>> >> >> 
>> >> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> >> >> 
>> >> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> >> >> source So, for example, you could completely power down the UART IP
>> >> >> blocks (and the enclosing power domain), configure the UART RX pad as a
>> >> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> >> >> docs call these IO pad wakeups.
>> >> >> 
>> >> >> On OMAP in fact, this is the common, default behavior when we enable
>> >> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> >> >> but can still wake up the system.
>> >> >> 
>> >> >> So in summary, even if device_may_wakeup() is true, many devices (with
>> >> >> additional SoC magic) can still generate wakeups even when stopped and
>> >> >> powered off.
>> >> >
>> >> > Well, on the other hand, on some SoCs there are devices that can't be
>> >> > powered off (or "declocked") if they are supposed to generate wakeups.
>> >> 
>> >> Correct.
>> >> 
>> >> > Also, I'm sure there are cases in which wakeups can be generated for devices
>> >> > with their clocks off, but only if power is present.  
>> >> 
>> >> Yes.
>> >> 
>> >> > So there are multiple
>> >> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
>> >> > the generic level, although not necessarily in such a simplistic way.
>> >> >
>> >> > Now, at this point, I want to do something very simple, which I think is
>> >> > done by this patch. 
>> >> >
>> >> > Is this optimal power comsumption-wise for every potential
>> >> > user of the framework?  
>> >> 
>> >> Well, sub-optimal would be an understatement.  I would consider this a
>> >> major regression since if we were to use this for OMAP, we would never
>> >> hit the full-chip low-power states if *any* device had wakeups enabled,
>> >> whereas today we can.
>> >> 
>> >> > No, but certainly for some it's sufficient.  Is it
>> >> > going to work in general?  I think it is.
>> >> >
>> >> > Of course, there's the question how to handle that more accurately and I have
>> >> > some ideas.  If you have any, please let me know.
>> >> >
>> >> > In the meantime, I'm going to modify the changelog so that it's clear that
>> >> > it's a "first approximation" thing, like in the patch below.
>> >> >
>> >> > Thanks,
>> >> > Rafael
>> >> >
>> >> > ---
>> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>> >> >
>> >> > There is the problem how to handle devices set up to wake up the
>> >> > system from sleep states during system-wide power transitions.
>> >> > In some cases, those devices can be turned off entirely, because the
>> >> > wakeup signals will be generated on their behalf anyway.  In some
>> >> > other cases, they will generate wakeup signals if their clocks are
>> >> > stopped, but only if power is not removed from them.  Finally, in
>> >> > some cases, they can only generate wakeup signals if power is not
>> >> > removed from them and their clocks are enabled.
>> >> 
>> >> That's a good summary.
>> >> 
>> >> > In the future, it will be necessary to take all of the above
>> >> > situations into account, but for starters it is possible to use
>> >> > the observation that if all wakeup devices are treated like the
>> >> > last group (i.e. their clocks are enabled and power in not removed
>> >> > from them during system suspend transitions), they all will be able
>> >> > to generate wakeups, although power consumption in the resulting
>> >> > system sleep state may not be optimal in some cases.
>> >> 
>> >> I'm not opposed to this kind of check happening.  I'm only opposed to it
>> >> happening in this "generic" layer because..., well, it's not generic.
>> >> 
>> >> Not only is it not generic, it would be a major regression in power
>> >> consumption for anyone moving to this layer that has the various
>> >> different wakeup capabilities already described.
>> >> 
>> >> The decision of whether or not to clock gate and/or power gate based on
>> >> wakeup capabilies has to be made somewhere (and in fact is already made
>> >> by existing code.)  But IMO, that decision should only be made where
>> >> wakeup capabilies are known, so that sensible decisions (for power
>> >> management) can be made.
>> >> 
>> >> Until there is a way in the generic code to distinguish between the
>> >> various ways a device can wakeup, this decision should be left up to the
>> >> code that knows how.
>> >
>> > OK, so I suppose your suggestion is to drop the patch and let the
>> > .stop_device() and .power_off() PM domain callbacks to hand that, is this
>> > correct?
>> 
>> Correct.
>> 
>> Initially I was thinking only about .power_off(), but you'd probably
>> want this at .stop_device() too.  In order to do that, probably want
>> .stop_device() to be able to return an error code such that an error
>> would prevent .power_off().
>
> I've just sent a reply to that. :-) I'll reproduce it below for easier
> reference:
>
> Neither .stop_device(), nor .power_off() can make such decisions,
> because they are used for both runtime PM and system suspend, so they
> shouldn't do system suspend-specific checks.
>
> So the only way forward I can see is to add a special PM domain callback,
> say .active_wakeup(), that will return "true" if the device is to be left
> active when wakeup-enabled.  So the check you don't like will become
> something like:
>
> if (device_may_wakeup(dev) && genpd->active_wakeup
>     && genpd->active_wakeup(dev))
>         return 0;
>
> Would that be better?

Yes, much better.  And I like the default behavior if no hooks are provided.

Thanks!

Kevin







^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-07-01  0:01                   ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01  0:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > On Friday, July 01, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> 
>> >> > On Thursday, June 30, 2011, Kevin Hilman wrote:
>> >> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> >> 
>> >> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> >> >
>> >> >> > Devices that are set up to wake up the system from sleep states
>> >> >> > should not be stopped and power should not be removed from them
>> >> >> > when the system goes into a sleep state.  
>> >> >> 
>> >> >> I don't think this belongs in the generic layer since the two
>> >> >> assumptions above are not generally true on embedded systems, and would
>> >> >> result in rather significant power consumption unnecessarily.
>> >> >
>> >> > As to whether or not this belongs to the generic layer, I don't quite agree
>> >> > (see below), but the changelog seems to be a bit inaccurate.
>> >> >
>> >> >> First, whether the device should be stopped on device_may_wakeup():
>> >> >> b
>> >> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> >> >> that they can generate wakeups even when they're not clocked (a.k.a
>> >> >> stopped).  So in this case, even after a ->stop_device (which clock
>> >> >> gates the IP), it can still generate wakeups.
>> >> >> 
>> >> >> Second, whether the device should be powered off if device_may_wakeup():
>> >> >> 
>> >> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> >> >> 
>> >> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> >> >> source So, for example, you could completely power down the UART IP
>> >> >> blocks (and the enclosing power domain), configure the UART RX pad as a
>> >> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> >> >> docs call these IO pad wakeups.
>> >> >> 
>> >> >> On OMAP in fact, this is the common, default behavior when we enable
>> >> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> >> >> but can still wake up the system.
>> >> >> 
>> >> >> So in summary, even if device_may_wakeup() is true, many devices (with
>> >> >> additional SoC magic) can still generate wakeups even when stopped and
>> >> >> powered off.
>> >> >
>> >> > Well, on the other hand, on some SoCs there are devices that can't be
>> >> > powered off (or "declocked") if they are supposed to generate wakeups.
>> >> 
>> >> Correct.
>> >> 
>> >> > Also, I'm sure there are cases in which wakeups can be generated for devices
>> >> > with their clocks off, but only if power is present.  
>> >> 
>> >> Yes.
>> >> 
>> >> > So there are multiple
>> >> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
>> >> > the generic level, although not necessarily in such a simplistic way.
>> >> >
>> >> > Now, at this point, I want to do something very simple, which I think is
>> >> > done by this patch. 
>> >> >
>> >> > Is this optimal power comsumption-wise for every potential
>> >> > user of the framework?  
>> >> 
>> >> Well, sub-optimal would be an understatement.  I would consider this a
>> >> major regression since if we were to use this for OMAP, we would never
>> >> hit the full-chip low-power states if *any* device had wakeups enabled,
>> >> whereas today we can.
>> >> 
>> >> > No, but certainly for some it's sufficient.  Is it
>> >> > going to work in general?  I think it is.
>> >> >
>> >> > Of course, there's the question how to handle that more accurately and I have
>> >> > some ideas.  If you have any, please let me know.
>> >> >
>> >> > In the meantime, I'm going to modify the changelog so that it's clear that
>> >> > it's a "first approximation" thing, like in the patch below.
>> >> >
>> >> > Thanks,
>> >> > Rafael
>> >> >
>> >> > ---
>> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>> >> >
>> >> > There is the problem how to handle devices set up to wake up the
>> >> > system from sleep states during system-wide power transitions.
>> >> > In some cases, those devices can be turned off entirely, because the
>> >> > wakeup signals will be generated on their behalf anyway.  In some
>> >> > other cases, they will generate wakeup signals if their clocks are
>> >> > stopped, but only if power is not removed from them.  Finally, in
>> >> > some cases, they can only generate wakeup signals if power is not
>> >> > removed from them and their clocks are enabled.
>> >> 
>> >> That's a good summary.
>> >> 
>> >> > In the future, it will be necessary to take all of the above
>> >> > situations into account, but for starters it is possible to use
>> >> > the observation that if all wakeup devices are treated like the
>> >> > last group (i.e. their clocks are enabled and power in not removed
>> >> > from them during system suspend transitions), they all will be able
>> >> > to generate wakeups, although power consumption in the resulting
>> >> > system sleep state may not be optimal in some cases.
>> >> 
>> >> I'm not opposed to this kind of check happening.  I'm only opposed to it
>> >> happening in this "generic" layer because..., well, it's not generic.
>> >> 
>> >> Not only is it not generic, it would be a major regression in power
>> >> consumption for anyone moving to this layer that has the various
>> >> different wakeup capabilities already described.
>> >> 
>> >> The decision of whether or not to clock gate and/or power gate based on
>> >> wakeup capabilies has to be made somewhere (and in fact is already made
>> >> by existing code.)  But IMO, that decision should only be made where
>> >> wakeup capabilies are known, so that sensible decisions (for power
>> >> management) can be made.
>> >> 
>> >> Until there is a way in the generic code to distinguish between the
>> >> various ways a device can wakeup, this decision should be left up to the
>> >> code that knows how.
>> >
>> > OK, so I suppose your suggestion is to drop the patch and let the
>> > .stop_device() and .power_off() PM domain callbacks to hand that, is this
>> > correct?
>> 
>> Correct.
>> 
>> Initially I was thinking only about .power_off(), but you'd probably
>> want this at .stop_device() too.  In order to do that, probably want
>> .stop_device() to be able to return an error code such that an error
>> would prevent .power_off().
>
> I've just sent a reply to that. :-) I'll reproduce it below for easier
> reference:
>
> Neither .stop_device(), nor .power_off() can make such decisions,
> because they are used for both runtime PM and system suspend, so they
> shouldn't do system suspend-specific checks.
>
> So the only way forward I can see is to add a special PM domain callback,
> say .active_wakeup(), that will return "true" if the device is to be left
> active when wakeup-enabled.  So the check you don't like will become
> something like:
>
> if (device_may_wakeup(dev) && genpd->active_wakeup
>     && genpd->active_wakeup(dev))
>         return 0;
>
> Would that be better?

Yes, much better.  And I like the default behavior if no hooks are provided.

Thanks!

Kevin







^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-07-01  0:01                   ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01  0:01 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> 
>> > On Friday, July 01, 2011, Kevin Hilman wrote:
>> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> 
>> >> > On Thursday, June 30, 2011, Kevin Hilman wrote:
>> >> >> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> >> >> 
>> >> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> >> >
>> >> >> > Devices that are set up to wake up the system from sleep states
>> >> >> > should not be stopped and power should not be removed from them
>> >> >> > when the system goes into a sleep state.  
>> >> >> 
>> >> >> I don't think this belongs in the generic layer since the two
>> >> >> assumptions above are not generally true on embedded systems, and would
>> >> >> result in rather significant power consumption unnecessarily.
>> >> >
>> >> > As to whether or not this belongs to the generic layer, I don't quite agree
>> >> > (see below), but the changelog seems to be a bit inaccurate.
>> >> >
>> >> >> First, whether the device should be stopped on device_may_wakeup():
>> >> >> b
>> >> >> Some IP blocks (at least on OMAP) have "asynchronous" wakeups.  Meaning
>> >> >> that they can generate wakeups even when they're not clocked (a.k.a
>> >> >> stopped).  So in this case, even after a ->stop_device (which clock
>> >> >> gates the IP), it can still generate wakeups.
>> >> >> 
>> >> >> Second, whether the device should be powered off if device_may_wakeup():
>> >> >> 
>> >> >> Embedded SoCs have other ways to wakeup than device-level wakeups.
>> >> >> 
>> >> >> For example, on OMAP, every pad on the SoC can be configured as a wakeup
>> >> >> source So, for example, you could completely power down the UART IP
>> >> >> blocks (and the enclosing power domain), configure the UART RX pad as a
>> >> >> wakeup source, and still wakeup the system on UART activity.  The OMAP
>> >> >> docs call these IO pad wakeups.
>> >> >> 
>> >> >> On OMAP in fact, this is the common, default behavior when we enable
>> >> >> "off-mode" in idle and/or suspend, since most of the IPs are powered off
>> >> >> but can still wake up the system.
>> >> >> 
>> >> >> So in summary, even if device_may_wakeup() is true, many devices (with
>> >> >> additional SoC magic) can still generate wakeups even when stopped and
>> >> >> powered off.
>> >> >
>> >> > Well, on the other hand, on some SoCs there are devices that can't be
>> >> > powered off (or "declocked") if they are supposed to generate wakeups.
>> >> 
>> >> Correct.
>> >> 
>> >> > Also, I'm sure there are cases in which wakeups can be generated for devices
>> >> > with their clocks off, but only if power is present.  
>> >> 
>> >> Yes.
>> >> 
>> >> > So there are multiple
>> >> > cases, but not so many overall.  So, IMO, it makes sense to handle that at
>> >> > the generic level, although not necessarily in such a simplistic way.
>> >> >
>> >> > Now, at this point, I want to do something very simple, which I think is
>> >> > done by this patch. 
>> >> >
>> >> > Is this optimal power comsumption-wise for every potential
>> >> > user of the framework?  
>> >> 
>> >> Well, sub-optimal would be an understatement.  I would consider this a
>> >> major regression since if we were to use this for OMAP, we would never
>> >> hit the full-chip low-power states if *any* device had wakeups enabled,
>> >> whereas today we can.
>> >> 
>> >> > No, but certainly for some it's sufficient.  Is it
>> >> > going to work in general?  I think it is.
>> >> >
>> >> > Of course, there's the question how to handle that more accurately and I have
>> >> > some ideas.  If you have any, please let me know.
>> >> >
>> >> > In the meantime, I'm going to modify the changelog so that it's clear that
>> >> > it's a "first approximation" thing, like in the patch below.
>> >> >
>> >> > Thanks,
>> >> > Rafael
>> >> >
>> >> > ---
>> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
>> >> > Subject: PM / Domains: Don't stop wakeup devices during system sleep transitions
>> >> >
>> >> > There is the problem how to handle devices set up to wake up the
>> >> > system from sleep states during system-wide power transitions.
>> >> > In some cases, those devices can be turned off entirely, because the
>> >> > wakeup signals will be generated on their behalf anyway.  In some
>> >> > other cases, they will generate wakeup signals if their clocks are
>> >> > stopped, but only if power is not removed from them.  Finally, in
>> >> > some cases, they can only generate wakeup signals if power is not
>> >> > removed from them and their clocks are enabled.
>> >> 
>> >> That's a good summary.
>> >> 
>> >> > In the future, it will be necessary to take all of the above
>> >> > situations into account, but for starters it is possible to use
>> >> > the observation that if all wakeup devices are treated like the
>> >> > last group (i.e. their clocks are enabled and power in not removed
>> >> > from them during system suspend transitions), they all will be able
>> >> > to generate wakeups, although power consumption in the resulting
>> >> > system sleep state may not be optimal in some cases.
>> >> 
>> >> I'm not opposed to this kind of check happening.  I'm only opposed to it
>> >> happening in this "generic" layer because..., well, it's not generic.
>> >> 
>> >> Not only is it not generic, it would be a major regression in power
>> >> consumption for anyone moving to this layer that has the various
>> >> different wakeup capabilities already described.
>> >> 
>> >> The decision of whether or not to clock gate and/or power gate based on
>> >> wakeup capabilies has to be made somewhere (and in fact is already made
>> >> by existing code.)  But IMO, that decision should only be made where
>> >> wakeup capabilies are known, so that sensible decisions (for power
>> >> management) can be made.
>> >> 
>> >> Until there is a way in the generic code to distinguish between the
>> >> various ways a device can wakeup, this decision should be left up to the
>> >> code that knows how.
>> >
>> > OK, so I suppose your suggestion is to drop the patch and let the
>> > .stop_device() and .power_off() PM domain callbacks to hand that, is this
>> > correct?
>> 
>> Correct.
>> 
>> Initially I was thinking only about .power_off(), but you'd probably
>> want this at .stop_device() too.  In order to do that, probably want
>> .stop_device() to be able to return an error code such that an error
>> would prevent .power_off().
>
> I've just sent a reply to that. :-) I'll reproduce it below for easier
> reference:
>
> Neither .stop_device(), nor .power_off() can make such decisions,
> because they are used for both runtime PM and system suspend, so they
> shouldn't do system suspend-specific checks.
>
> So the only way forward I can see is to add a special PM domain callback,
> say .active_wakeup(), that will return "true" if the device is to be left
> active when wakeup-enabled.  So the check you don't like will become
> something like:
>
> if (device_may_wakeup(dev) && genpd->active_wakeup
>     && genpd->active_wakeup(dev))
>         return 0;
>
> Would that be better?

Yes, much better.  And I like the default behavior if no hooks are provided.

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-07-01  0:01                   ` Kevin Hilman
@ 2011-07-01  0:24                     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01  0:24 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
...
> > So the only way forward I can see is to add a special PM domain callback,
> > say .active_wakeup(), that will return "true" if the device is to be left
> > active when wakeup-enabled.  So the check you don't like will become
> > something like:
> >
> > if (device_may_wakeup(dev) && genpd->active_wakeup
> >     && genpd->active_wakeup(dev))
> >         return 0;
> >
> > Would that be better?
> 
> Yes, much better.  And I like the default behavior if no hooks are provided.

So, what about the appended patch instead of the $subject one?

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Wakeup devices support for system sleep transitions

There is the problem how to handle devices set up to wake up the
system from sleep states during system-wide power transitions.
In some cases, those devices can be turned off entirely, because the
wakeup signals will be generated on their behalf anyway.  In some
other cases, they will generate wakeup signals if their clocks are
stopped, but only if power is not removed from them.  Finally, in
some cases, they can only generate wakeup signals if power is not
removed from them and their clocks are enabled.

To allow platform-specific code to decide whether or not to put
wakeup devices (and their PM domains) into low-power state during
system-wide transitions, such as system suspend, introduce a new
generic PM domain callback, .active_wakeup(), that will be used
during the "noirq" phase of system suspend and hibernation (after
image creation) to decide what to do with wakeup devices.
Specifically, if this callback is present and returns "true", the
generic PM domain code will not execute .stop_device() for the
given wakeup device and its PM domain won't be powered off.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    8 ++++++++
 include/linux/pm_domain.h   |    1 +
 2 files changed, 9 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -450,6 +450,10 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev)
+	    && genpd->active_wakeup && genpd->active_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -670,6 +674,10 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev)
+	    && genpd->active_wakeup && genpd->active_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
Index: linux-2.6/include/linux/pm_domain.h
=================================--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -38,6 +38,7 @@ struct generic_pm_domain {
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
+	bool (*active_wakeup)(struct device *dev);
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-07-01  0:24                     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01  0:24 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
...
> > So the only way forward I can see is to add a special PM domain callback,
> > say .active_wakeup(), that will return "true" if the device is to be left
> > active when wakeup-enabled.  So the check you don't like will become
> > something like:
> >
> > if (device_may_wakeup(dev) && genpd->active_wakeup
> >     && genpd->active_wakeup(dev))
> >         return 0;
> >
> > Would that be better?
> 
> Yes, much better.  And I like the default behavior if no hooks are provided.

So, what about the appended patch instead of the $subject one?

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Wakeup devices support for system sleep transitions

There is the problem how to handle devices set up to wake up the
system from sleep states during system-wide power transitions.
In some cases, those devices can be turned off entirely, because the
wakeup signals will be generated on their behalf anyway.  In some
other cases, they will generate wakeup signals if their clocks are
stopped, but only if power is not removed from them.  Finally, in
some cases, they can only generate wakeup signals if power is not
removed from them and their clocks are enabled.

To allow platform-specific code to decide whether or not to put
wakeup devices (and their PM domains) into low-power state during
system-wide transitions, such as system suspend, introduce a new
generic PM domain callback, .active_wakeup(), that will be used
during the "noirq" phase of system suspend and hibernation (after
image creation) to decide what to do with wakeup devices.
Specifically, if this callback is present and returns "true", the
generic PM domain code will not execute .stop_device() for the
given wakeup device and its PM domain won't be powered off.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    8 ++++++++
 include/linux/pm_domain.h   |    1 +
 2 files changed, 9 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -450,6 +450,10 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev)
+	    && genpd->active_wakeup && genpd->active_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -670,6 +674,10 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev)
+	    && genpd->active_wakeup && genpd->active_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -38,6 +38,7 @@ struct generic_pm_domain {
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
+	bool (*active_wakeup)(struct device *dev);
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-07-01  0:01                   ` Kevin Hilman
  (?)
  (?)
@ 2011-07-01  0:24                   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01  0:24 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
...
> > So the only way forward I can see is to add a special PM domain callback,
> > say .active_wakeup(), that will return "true" if the device is to be left
> > active when wakeup-enabled.  So the check you don't like will become
> > something like:
> >
> > if (device_may_wakeup(dev) && genpd->active_wakeup
> >     && genpd->active_wakeup(dev))
> >         return 0;
> >
> > Would that be better?
> 
> Yes, much better.  And I like the default behavior if no hooks are provided.

So, what about the appended patch instead of the $subject one?

Rafael

---
From: Rafael J. Wysocki <rjw@sisk.pl>
Subject: PM / Domains: Wakeup devices support for system sleep transitions

There is the problem how to handle devices set up to wake up the
system from sleep states during system-wide power transitions.
In some cases, those devices can be turned off entirely, because the
wakeup signals will be generated on their behalf anyway.  In some
other cases, they will generate wakeup signals if their clocks are
stopped, but only if power is not removed from them.  Finally, in
some cases, they can only generate wakeup signals if power is not
removed from them and their clocks are enabled.

To allow platform-specific code to decide whether or not to put
wakeup devices (and their PM domains) into low-power state during
system-wide transitions, such as system suspend, introduce a new
generic PM domain callback, .active_wakeup(), that will be used
during the "noirq" phase of system suspend and hibernation (after
image creation) to decide what to do with wakeup devices.
Specifically, if this callback is present and returns "true", the
generic PM domain code will not execute .stop_device() for the
given wakeup device and its PM domain won't be powered off.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
---
 drivers/base/power/domain.c |    8 ++++++++
 include/linux/pm_domain.h   |    1 +
 2 files changed, 9 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -450,6 +450,10 @@ static int pm_genpd_suspend_noirq(struct
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev)
+	    && genpd->active_wakeup && genpd->active_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
@@ -670,6 +674,10 @@ static int pm_genpd_dev_poweroff_noirq(s
 	if (ret)
 		return ret;
 
+	if (device_may_wakeup(dev)
+	    && genpd->active_wakeup && genpd->active_wakeup(dev))
+		return 0;
+
 	if (genpd->stop_device)
 		genpd->stop_device(dev);
 
Index: linux-2.6/include/linux/pm_domain.h
===================================================================
--- linux-2.6.orig/include/linux/pm_domain.h
+++ linux-2.6/include/linux/pm_domain.h
@@ -38,6 +38,7 @@ struct generic_pm_domain {
 	int (*power_on)(struct generic_pm_domain *domain);
 	int (*start_device)(struct device *dev);
 	int (*stop_device)(struct device *dev);
+	bool (*active_wakeup)(struct device *dev);
 };
 
 static inline struct generic_pm_domain *pd_to_genpd(struct dev_pm_domain *pd)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-07-01  0:24                     ` Rafael J. Wysocki
@ 2011-07-01 14:34                       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 14:34 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> ...
>> > So the only way forward I can see is to add a special PM domain callback,
>> > say .active_wakeup(), that will return "true" if the device is to be left
>> > active when wakeup-enabled.  So the check you don't like will become
>> > something like:
>> >
>> > if (device_may_wakeup(dev) && genpd->active_wakeup
>> >     && genpd->active_wakeup(dev))
>> >         return 0;
>> >
>> > Would that be better?
>> 
>> Yes, much better.  And I like the default behavior if no hooks are provided.
>
> So, what about the appended patch instead of the $subject one?
>
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Domains: Wakeup devices support for system sleep transitions
>
> There is the problem how to handle devices set up to wake up the
> system from sleep states during system-wide power transitions.
> In some cases, those devices can be turned off entirely, because the
> wakeup signals will be generated on their behalf anyway.  In some
> other cases, they will generate wakeup signals if their clocks are
> stopped, but only if power is not removed from them.  Finally, in
> some cases, they can only generate wakeup signals if power is not
> removed from them and their clocks are enabled.
>
> To allow platform-specific code to decide whether or not to put
> wakeup devices (and their PM domains) into low-power state during
> system-wide transitions, such as system suspend, introduce a new
> generic PM domain callback, .active_wakeup(), that will be used
> during the "noirq" phase of system suspend and hibernation (after
> image creation) to decide what to do with wakeup devices.
> Specifically, if this callback is present and returns "true", the
> generic PM domain code will not execute .stop_device() for the
> given wakeup device and its PM domain won't be powered off.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Acked-by: Kevin Hilman <khilman@ti.com>

Thanks,

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-07-01 14:34                       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 14:34 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> ...
>> > So the only way forward I can see is to add a special PM domain callback,
>> > say .active_wakeup(), that will return "true" if the device is to be left
>> > active when wakeup-enabled.  So the check you don't like will become
>> > something like:
>> >
>> > if (device_may_wakeup(dev) && genpd->active_wakeup
>> >     && genpd->active_wakeup(dev))
>> >         return 0;
>> >
>> > Would that be better?
>> 
>> Yes, much better.  And I like the default behavior if no hooks are provided.
>
> So, what about the appended patch instead of the $subject one?
>
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Domains: Wakeup devices support for system sleep transitions
>
> There is the problem how to handle devices set up to wake up the
> system from sleep states during system-wide power transitions.
> In some cases, those devices can be turned off entirely, because the
> wakeup signals will be generated on their behalf anyway.  In some
> other cases, they will generate wakeup signals if their clocks are
> stopped, but only if power is not removed from them.  Finally, in
> some cases, they can only generate wakeup signals if power is not
> removed from them and their clocks are enabled.
>
> To allow platform-specific code to decide whether or not to put
> wakeup devices (and their PM domains) into low-power state during
> system-wide transitions, such as system suspend, introduce a new
> generic PM domain callback, .active_wakeup(), that will be used
> during the "noirq" phase of system suspend and hibernation (after
> image creation) to decide what to do with wakeup devices.
> Specifically, if this callback is present and returns "true", the
> generic PM domain code will not execute .stop_device() for the
> given wakeup device and its PM domain won't be powered off.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Acked-by: Kevin Hilman <khilman@ti.com>

Thanks,

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-07-01  0:24                     ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-07-01 14:34                     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 14:34 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> On Friday, July 01, 2011, Kevin Hilman wrote:
>> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> ...
>> > So the only way forward I can see is to add a special PM domain callback,
>> > say .active_wakeup(), that will return "true" if the device is to be left
>> > active when wakeup-enabled.  So the check you don't like will become
>> > something like:
>> >
>> > if (device_may_wakeup(dev) && genpd->active_wakeup
>> >     && genpd->active_wakeup(dev))
>> >         return 0;
>> >
>> > Would that be better?
>> 
>> Yes, much better.  And I like the default behavior if no hooks are provided.
>
> So, what about the appended patch instead of the $subject one?
>
> Rafael
>
> ---
> From: Rafael J. Wysocki <rjw@sisk.pl>
> Subject: PM / Domains: Wakeup devices support for system sleep transitions
>
> There is the problem how to handle devices set up to wake up the
> system from sleep states during system-wide power transitions.
> In some cases, those devices can be turned off entirely, because the
> wakeup signals will be generated on their behalf anyway.  In some
> other cases, they will generate wakeup signals if their clocks are
> stopped, but only if power is not removed from them.  Finally, in
> some cases, they can only generate wakeup signals if power is not
> removed from them and their clocks are enabled.
>
> To allow platform-specific code to decide whether or not to put
> wakeup devices (and their PM domains) into low-power state during
> system-wide transitions, such as system suspend, introduce a new
> generic PM domain callback, .active_wakeup(), that will be used
> during the "noirq" phase of system suspend and hibernation (after
> image creation) to decide what to do with wakeup devices.
> Specifically, if this callback is present and returns "true", the
> generic PM domain code will not execute .stop_device() for the
> given wakeup device and its PM domain won't be powered off.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>

Acked-by: Kevin Hilman <khilman@ti.com>

Thanks,

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during
  2011-06-30 23:25               ` Rafael J. Wysocki
@ 2011-07-01 14:45                 ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-07-01 14:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Fri, 1 Jul 2011, Rafael J. Wysocki wrote:

> So the only way forward I can see is to add a special PM domain callback,
> say .active_wakeup(), that will return "true" if the device is to be left
> active if wakeup-enabled.  So the check you don't like will become
> something like:
> 
> if (device_may_wakeup(dev) && genpd->active_wakeup
>     && genpd->active_wakeup(dev))
>         return 0;
> 
> Would that be better?

Another option, less flexible but perhaps easier to use, would be to 
set a couple of bitflags indicating whether the device needs power or 
clocks to handle wakeup signals.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-07-01 14:45                 ` Alan Stern
  0 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-07-01 14:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Fri, 1 Jul 2011, Rafael J. Wysocki wrote:

> So the only way forward I can see is to add a special PM domain callback,
> say .active_wakeup(), that will return "true" if the device is to be left
> active if wakeup-enabled.  So the check you don't like will become
> something like:
> 
> if (device_may_wakeup(dev) && genpd->active_wakeup
>     && genpd->active_wakeup(dev))
>         return 0;
> 
> Would that be better?

Another option, less flexible but perhaps easier to use, would be to 
set a couple of bitflags indicating whether the device needs power or 
clocks to handle wakeup signals.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-06-30 23:25               ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-07-01 14:45               ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-07-01 14:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Fri, 1 Jul 2011, Rafael J. Wysocki wrote:

> So the only way forward I can see is to add a special PM domain callback,
> say .active_wakeup(), that will return "true" if the device is to be left
> active if wakeup-enabled.  So the check you don't like will become
> something like:
> 
> if (device_may_wakeup(dev) && genpd->active_wakeup
>     && genpd->active_wakeup(dev))
>         return 0;
> 
> Would that be better?

Another option, less flexible but perhaps easier to use, would be to 
set a couple of bitflags indicating whether the device needs power or 
clocks to handle wakeup signals.

Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-25 21:26     ` Rafael J. Wysocki
@ 2011-07-01 18:11       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 18:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Introduce common headers, helper functions and callbacks allowing
> platforms to use simple generic power domains for runtime power
> management.
>
> Introduce struct generic_pm_domain to be used for representing
> power domains that each contain a number of devices and may be
> parent domains or subdomains with respect to other power domains.
> Among other things, this structure includes callbacks to be
> provided by platforms for performing specific tasks related to
> power management (i.e. ->stop_device() may disable a device's
> clocks, while ->start_device() may enable them, ->power_off() is
> supposed to remove power from the entire power domain
> and ->power_on() is supposed to restore it).
>
> Introduce functions that can be used as power domain runtime PM
> callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> as well as helper functions for the initialization of a power
> domain represented by a struct generic_power_domain object,
> adding a device to or removing a device from it and adding or
> removing subdomains.
>
> Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> selected by the platforms that want to use the new code.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Greg Kroah-Hartman <gregkh@suse.de>

Reviewed-by: Kevin Hilman <khilman@ti.com>

While I still don't think this is generic enough for some more
complicated SoCs, it most certainly a huge step in the right direction,
and I think we can address the other complexities with additional
patches as needed.

Thanks Rafael for all your hard work on this and putting up with my
nagging. ;)

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
@ 2011-07-01 18:11       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 18:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Introduce common headers, helper functions and callbacks allowing
> platforms to use simple generic power domains for runtime power
> management.
>
> Introduce struct generic_pm_domain to be used for representing
> power domains that each contain a number of devices and may be
> parent domains or subdomains with respect to other power domains.
> Among other things, this structure includes callbacks to be
> provided by platforms for performing specific tasks related to
> power management (i.e. ->stop_device() may disable a device's
> clocks, while ->start_device() may enable them, ->power_off() is
> supposed to remove power from the entire power domain
> and ->power_on() is supposed to restore it).
>
> Introduce functions that can be used as power domain runtime PM
> callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> as well as helper functions for the initialization of a power
> domain represented by a struct generic_power_domain object,
> adding a device to or removing a device from it and adding or
> removing subdomains.
>
> Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> selected by the platforms that want to use the new code.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Greg Kroah-Hartman <gregkh@suse.de>

Reviewed-by: Kevin Hilman <khilman@ti.com>

While I still don't think this is generic enough for some more
complicated SoCs, it most certainly a huge step in the right direction,
and I think we can address the other complexities with additional
patches as needed.

Thanks Rafael for all your hard work on this and putting up with my
nagging. ;)

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-06-25 21:26     ` Rafael J. Wysocki
                       ` (3 preceding siblings ...)
  (?)
@ 2011-07-01 18:11     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 18:11 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Introduce common headers, helper functions and callbacks allowing
> platforms to use simple generic power domains for runtime power
> management.
>
> Introduce struct generic_pm_domain to be used for representing
> power domains that each contain a number of devices and may be
> parent domains or subdomains with respect to other power domains.
> Among other things, this structure includes callbacks to be
> provided by platforms for performing specific tasks related to
> power management (i.e. ->stop_device() may disable a device's
> clocks, while ->start_device() may enable them, ->power_off() is
> supposed to remove power from the entire power domain
> and ->power_on() is supposed to restore it).
>
> Introduce functions that can be used as power domain runtime PM
> callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> as well as helper functions for the initialization of a power
> domain represented by a struct generic_power_domain object,
> adding a device to or removing a device from it and adding or
> removing subdomains.
>
> Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> selected by the platforms that want to use the new code.
>
> Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> Acked-by: Greg Kroah-Hartman <gregkh@suse.de>

Reviewed-by: Kevin Hilman <khilman@ti.com>

While I still don't think this is generic enough for some more
complicated SoCs, it most certainly a huge step in the right direction,
and I think we can address the other complexities with additional
patches as needed.

Thanks Rafael for all your hard work on this and putting up with my
nagging. ;)

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains
  2011-06-25 21:24   ` Rafael J. Wysocki
@ 2011-07-01 18:27     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 18:27 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> Hi,
>
> Well, one more update. :-)
>
> This is the 6th version of the patchset adding support for generic I/O PM
> domains.  The majority of patches have had some small fixes here and there
> to make things work when CONFIG_PM_RUNTIME is unset.  In addition to that,
> to make the shmobile code introduced by the last patch work for
> CONFIG_PM_RUNTIME unset I had to add patches [8-9/10].  Patch [7/10]
> adds wakeup support that was missing from the previous version and I
> decided not to push patch [1/10] for 3.0 after all.

For everything but the shmobile specifics, which I didn't look at too
closely, and the changes for [7/10] that we agreed on:

Reviewed-by: Kevin Hilman <khilman@ti.com>

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains
@ 2011-07-01 18:27     ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 18:27 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> Hi,
>
> Well, one more update. :-)
>
> This is the 6th version of the patchset adding support for generic I/O PM
> domains.  The majority of patches have had some small fixes here and there
> to make things work when CONFIG_PM_RUNTIME is unset.  In addition to that,
> to make the shmobile code introduced by the last patch work for
> CONFIG_PM_RUNTIME unset I had to add patches [8-9/10].  Patch [7/10]
> adds wakeup support that was missing from the previous version and I
> decided not to push patch [1/10] for 3.0 after all.

For everything but the shmobile specifics, which I didn't look at too
closely, and the changes for [7/10] that we agreed on:

Reviewed-by: Kevin Hilman <khilman@ti.com>

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains
  2011-06-25 21:24   ` Rafael J. Wysocki
                     ` (19 preceding siblings ...)
  (?)
@ 2011-07-01 18:27   ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-01 18:27 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> Hi,
>
> Well, one more update. :-)
>
> This is the 6th version of the patchset adding support for generic I/O PM
> domains.  The majority of patches have had some small fixes here and there
> to make things work when CONFIG_PM_RUNTIME is unset.  In addition to that,
> to make the shmobile code introduced by the last patch work for
> CONFIG_PM_RUNTIME unset I had to add patches [8-9/10].  Patch [7/10]
> adds wakeup support that was missing from the previous version and I
> decided not to push patch [1/10] for 3.0 after all.

For everything but the shmobile specifics, which I didn't look at too
closely, and the changes for [7/10] that we agreed on:

Reviewed-by: Kevin Hilman <khilman@ti.com>

Thanks!

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-07-01 18:11       ` Kevin Hilman
@ 2011-07-01 20:03         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01 20:03 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Introduce common headers, helper functions and callbacks allowing
> > platforms to use simple generic power domains for runtime power
> > management.
> >
> > Introduce struct generic_pm_domain to be used for representing
> > power domains that each contain a number of devices and may be
> > parent domains or subdomains with respect to other power domains.
> > Among other things, this structure includes callbacks to be
> > provided by platforms for performing specific tasks related to
> > power management (i.e. ->stop_device() may disable a device's
> > clocks, while ->start_device() may enable them, ->power_off() is
> > supposed to remove power from the entire power domain
> > and ->power_on() is supposed to restore it).
> >
> > Introduce functions that can be used as power domain runtime PM
> > callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> > as well as helper functions for the initialization of a power
> > domain represented by a struct generic_power_domain object,
> > adding a device to or removing a device from it and adding or
> > removing subdomains.
> >
> > Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> > selected by the platforms that want to use the new code.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> 
> Reviewed-by: Kevin Hilman <khilman@ti.com>

Thanks!

> While I still don't think this is generic enough for some more
> complicated SoCs, it most certainly a huge step in the right direction,
> and I think we can address the other complexities with additional
> patches as needed.

That's my intention.

> Thanks Rafael for all your hard work on this and putting up with my
> nagging. ;)

I don't mind that at all, it kind of helps to focus on important stuff. ;-)

Rafael


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
@ 2011-07-01 20:03         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01 20:03 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Introduce common headers, helper functions and callbacks allowing
> > platforms to use simple generic power domains for runtime power
> > management.
> >
> > Introduce struct generic_pm_domain to be used for representing
> > power domains that each contain a number of devices and may be
> > parent domains or subdomains with respect to other power domains.
> > Among other things, this structure includes callbacks to be
> > provided by platforms for performing specific tasks related to
> > power management (i.e. ->stop_device() may disable a device's
> > clocks, while ->start_device() may enable them, ->power_off() is
> > supposed to remove power from the entire power domain
> > and ->power_on() is supposed to restore it).
> >
> > Introduce functions that can be used as power domain runtime PM
> > callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> > as well as helper functions for the initialization of a power
> > domain represented by a struct generic_power_domain object,
> > adding a device to or removing a device from it and adding or
> > removing subdomains.
> >
> > Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> > selected by the platforms that want to use the new code.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> 
> Reviewed-by: Kevin Hilman <khilman@ti.com>

Thanks!

> While I still don't think this is generic enough for some more
> complicated SoCs, it most certainly a huge step in the right direction,
> and I think we can address the other complexities with additional
> patches as needed.

That's my intention.

> Thanks Rafael for all your hard work on this and putting up with my
> nagging. ;)

I don't mind that at all, it kind of helps to focus on important stuff. ;-)

Rafael


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7)
  2011-07-01 18:11       ` Kevin Hilman
  (?)
  (?)
@ 2011-07-01 20:03       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01 20:03 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 01, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Introduce common headers, helper functions and callbacks allowing
> > platforms to use simple generic power domains for runtime power
> > management.
> >
> > Introduce struct generic_pm_domain to be used for representing
> > power domains that each contain a number of devices and may be
> > parent domains or subdomains with respect to other power domains.
> > Among other things, this structure includes callbacks to be
> > provided by platforms for performing specific tasks related to
> > power management (i.e. ->stop_device() may disable a device's
> > clocks, while ->start_device() may enable them, ->power_off() is
> > supposed to remove power from the entire power domain
> > and ->power_on() is supposed to restore it).
> >
> > Introduce functions that can be used as power domain runtime PM
> > callbacks, pm_genpd_runtime_suspend() and pm_genpd_runtime_resume(),
> > as well as helper functions for the initialization of a power
> > domain represented by a struct generic_power_domain object,
> > adding a device to or removing a device from it and adding or
> > removing subdomains.
> >
> > Introduce configuration option CONFIG_PM_GENERIC_DOMAINS to be
> > selected by the platforms that want to use the new code.
> >
> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> > Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
> 
> Reviewed-by: Kevin Hilman <khilman@ti.com>

Thanks!

> While I still don't think this is generic enough for some more
> complicated SoCs, it most certainly a huge step in the right direction,
> and I think we can address the other complexities with additional
> patches as needed.

That's my intention.

> Thanks Rafael for all your hard work on this and putting up with my
> nagging. ;)

I don't mind that at all, it kind of helps to focus on important stuff. ;-)

Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-07-01 14:45                 ` [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions Alan Stern
@ 2011-07-01 20:06                   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01 20:06 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Alan Stern wrote:
> On Fri, 1 Jul 2011, Rafael J. Wysocki wrote:
> 
> > So the only way forward I can see is to add a special PM domain callback,
> > say .active_wakeup(), that will return "true" if the device is to be left
> > active if wakeup-enabled.  So the check you don't like will become
> > something like:
> > 
> > if (device_may_wakeup(dev) && genpd->active_wakeup
> >     && genpd->active_wakeup(dev))
> >         return 0;
> > 
> > Would that be better?
> 
> Another option, less flexible but perhaps easier to use, would be to 
> set a couple of bitflags indicating whether the device needs power or 
> clocks to handle wakeup signals.

Well, I agree, but I've decided to add the new callback after all.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
@ 2011-07-01 20:06                   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01 20:06 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 01, 2011, Alan Stern wrote:
> On Fri, 1 Jul 2011, Rafael J. Wysocki wrote:
> 
> > So the only way forward I can see is to add a special PM domain callback,
> > say .active_wakeup(), that will return "true" if the device is to be left
> > active if wakeup-enabled.  So the check you don't like will become
> > something like:
> > 
> > if (device_may_wakeup(dev) && genpd->active_wakeup
> >     && genpd->active_wakeup(dev))
> >         return 0;
> > 
> > Would that be better?
> 
> Another option, less flexible but perhaps easier to use, would be to 
> set a couple of bitflags indicating whether the device needs power or 
> clocks to handle wakeup signals.

Well, I agree, but I've decided to add the new callback after all.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions
  2011-07-01 14:45                 ` [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions Alan Stern
  (?)
  (?)
@ 2011-07-01 20:06                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-01 20:06 UTC (permalink / raw)
  To: Alan Stern; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 01, 2011, Alan Stern wrote:
> On Fri, 1 Jul 2011, Rafael J. Wysocki wrote:
> 
> > So the only way forward I can see is to add a special PM domain callback,
> > say .active_wakeup(), that will return "true" if the device is to be left
> > active if wakeup-enabled.  So the check you don't like will become
> > something like:
> > 
> > if (device_may_wakeup(dev) && genpd->active_wakeup
> >     && genpd->active_wakeup(dev))
> >         return 0;
> > 
> > Would that be better?
> 
> Another option, less flexible but perhaps easier to use, would be to 
> set a couple of bitflags indicating whether the device needs power or 
> clocks to handle wakeup signals.

Well, I agree, but I've decided to add the new callback after all.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-06-28 23:44       ` Rafael J. Wysocki
@ 2011-07-08  0:29         ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-08  0:29 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
>
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  

Thinking about this some more, how is a driver supposed to reconfigure
wakeups during suspend if it has already been runtime suspended?

For example, assume a device where device_may_wakeup() = false.  This
means wakeups during *suspend* are disabled, but wakeups wakeups are
assumed to enabled when it is runtime suspended.

So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
and then system suspend comes along.  

With this current patch, the driver will never receive any callbacks, so
it can never disable its wakeups.  

Am I missing something?

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08  0:29         ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-08  0:29 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
>
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  

Thinking about this some more, how is a driver supposed to reconfigure
wakeups during suspend if it has already been runtime suspended?

For example, assume a device where device_may_wakeup() == false.  This
means wakeups during *suspend* are disabled, but wakeups wakeups are
assumed to enabled when it is runtime suspended.

So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
and then system suspend comes along.  

With this current patch, the driver will never receive any callbacks, so
it can never disable its wakeups.  

Am I missing something?

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-06-28 23:44       ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-07-08  0:29       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-08  0:29 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

> From: Rafael J. Wysocki <rjw@sisk.pl>
>
> Make generic PM domains support system-wide power transitions
> (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> poweroff and restore callbacks to be associated with struct
> generic_pm_domain objects and make pm_genpd_init() use them as
> appropriate.
>
> The new callbacks do nothing for devices belonging to power domains
> that were powered down at run time (before the transition).  

Thinking about this some more, how is a driver supposed to reconfigure
wakeups during suspend if it has already been runtime suspended?

For example, assume a device where device_may_wakeup() == false.  This
means wakeups during *suspend* are disabled, but wakeups wakeups are
assumed to enabled when it is runtime suspended.

So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
and then system suspend comes along.  

With this current patch, the driver will never receive any callbacks, so
it can never disable its wakeups.  

Am I missing something?

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08  0:29         ` Kevin Hilman
@ 2011-07-08  9:24           ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08  9:24 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Make generic PM domains support system-wide power transitions
> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > poweroff and restore callbacks to be associated with struct
> > generic_pm_domain objects and make pm_genpd_init() use them as
> > appropriate.
> >
> > The new callbacks do nothing for devices belonging to power domains
> > that were powered down at run time (before the transition).  
> 
> Thinking about this some more, how is a driver supposed to reconfigure
> wakeups during suspend if it has already been runtime suspended?

If the device belongs to a PM domain that has been powered off, it
won't be notified.

> For example, assume a device where device_may_wakeup() = false.  This
> means wakeups during *suspend* are disabled, but wakeups wakeups are
> assumed to enabled when it is runtime suspended.
> 
> So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> and then system suspend comes along.  
> 
> With this current patch, the driver will never receive any callbacks, so
> it can never disable its wakeups.  
>
> Am I missing something?

As I said above, this only happens with devices that belog to PM domains
that were powered off before system suspend has started, so the problem
is limited to devices that wakeup is signaled on behalf of even when they
have no power.

So this is a limitation, but not affecting all platforms.

There are a few ways to avoid this limitation I can think of:
(1) Add a "make me operational during system suspend" flag to struct dev_pm_info
    and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
    core, or pm_genpd_prepare()).
(2) Add a "my .prepare() is safe to run if device is not accessible" flag to
    struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
    devices regardless of whether or not their PM domains are off.
(3) Call .prepare() from all drivers unconditionally during system suspend
    (and probably .complete() too) in the hope they won't access inaccessible
    devices.
Probably, there's more.

In any case I think it's material for future work.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08  9:24           ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08  9:24 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Linux PM mailing list, Greg Kroah-Hartman, Magnus Damm,
	Paul Walmsley, Alan Stern, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Make generic PM domains support system-wide power transitions
> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > poweroff and restore callbacks to be associated with struct
> > generic_pm_domain objects and make pm_genpd_init() use them as
> > appropriate.
> >
> > The new callbacks do nothing for devices belonging to power domains
> > that were powered down at run time (before the transition).  
> 
> Thinking about this some more, how is a driver supposed to reconfigure
> wakeups during suspend if it has already been runtime suspended?

If the device belongs to a PM domain that has been powered off, it
won't be notified.

> For example, assume a device where device_may_wakeup() == false.  This
> means wakeups during *suspend* are disabled, but wakeups wakeups are
> assumed to enabled when it is runtime suspended.
> 
> So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> and then system suspend comes along.  
> 
> With this current patch, the driver will never receive any callbacks, so
> it can never disable its wakeups.  
>
> Am I missing something?

As I said above, this only happens with devices that belog to PM domains
that were powered off before system suspend has started, so the problem
is limited to devices that wakeup is signaled on behalf of even when they
have no power.

So this is a limitation, but not affecting all platforms.

There are a few ways to avoid this limitation I can think of:
(1) Add a "make me operational during system suspend" flag to struct dev_pm_info
    and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
    core, or pm_genpd_prepare()).
(2) Add a "my .prepare() is safe to run if device is not accessible" flag to
    struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
    devices regardless of whether or not their PM domains are off.
(3) Call .prepare() from all drivers unconditionally during system suspend
    (and probably .complete() too) in the hope they won't access inaccessible
    devices.
Probably, there's more.

In any case I think it's material for future work.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08  0:29         ` Kevin Hilman
  (?)
@ 2011-07-08  9:24         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08  9:24 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 08, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >
> > Make generic PM domains support system-wide power transitions
> > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > poweroff and restore callbacks to be associated with struct
> > generic_pm_domain objects and make pm_genpd_init() use them as
> > appropriate.
> >
> > The new callbacks do nothing for devices belonging to power domains
> > that were powered down at run time (before the transition).  
> 
> Thinking about this some more, how is a driver supposed to reconfigure
> wakeups during suspend if it has already been runtime suspended?

If the device belongs to a PM domain that has been powered off, it
won't be notified.

> For example, assume a device where device_may_wakeup() == false.  This
> means wakeups during *suspend* are disabled, but wakeups wakeups are
> assumed to enabled when it is runtime suspended.
> 
> So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> and then system suspend comes along.  
> 
> With this current patch, the driver will never receive any callbacks, so
> it can never disable its wakeups.  
>
> Am I missing something?

As I said above, this only happens with devices that belog to PM domains
that were powered off before system suspend has started, so the problem
is limited to devices that wakeup is signaled on behalf of even when they
have no power.

So this is a limitation, but not affecting all platforms.

There are a few ways to avoid this limitation I can think of:
(1) Add a "make me operational during system suspend" flag to struct dev_pm_info
    and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
    core, or pm_genpd_prepare()).
(2) Add a "my .prepare() is safe to run if device is not accessible" flag to
    struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
    devices regardless of whether or not their PM domains are off.
(3) Call .prepare() from all drivers unconditionally during system suspend
    (and probably .complete() too) in the hope they won't access inaccessible
    devices.
Probably, there's more.

In any case I think it's material for future work.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions
  2011-07-08  9:24           ` Rafael J. Wysocki
@ 2011-07-08 14:37             ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-07-08 14:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:

> On Friday, July 08, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >
> > > Make generic PM domains support system-wide power transitions
> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > > poweroff and restore callbacks to be associated with struct
> > > generic_pm_domain objects and make pm_genpd_init() use them as
> > > appropriate.
> > >
> > > The new callbacks do nothing for devices belonging to power domains
> > > that were powered down at run time (before the transition).  
> > 
> > Thinking about this some more, how is a driver supposed to reconfigure
> > wakeups during suspend if it has already been runtime suspended?
> 
> If the device belongs to a PM domain that has been powered off, it
> won't be notified.
> 
> > For example, assume a device where device_may_wakeup() = false.  This
> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > assumed to enabled when it is runtime suspended.
> > 
> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > and then system suspend comes along.  
> > 
> > With this current patch, the driver will never receive any callbacks, so
> > it can never disable its wakeups.  
> >
> > Am I missing something?
> 
> As I said above, this only happens with devices that belog to PM domains
> that were powered off before system suspend has started, so the problem
> is limited to devices that wakeup is signaled on behalf of even when they
> have no power.
> 
> So this is a limitation, but not affecting all platforms.
> 
> There are a few ways to avoid this limitation I can think of:
> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
>     core, or pm_genpd_prepare()).

What's to prevent the device from being runtime-suspended again before 
the wakeup setting can be changed?

> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
>     devices regardless of whether or not their PM domains are off.
> (3) Call .prepare() from all drivers unconditionally during system suspend
>     (and probably .complete() too) in the hope they won't access inaccessible
>     devices.
> Probably, there's more.

In the PM domain's suspend code, do a runtime resume if the wakeup
setting needs to be changed, rather than simply skipping over the
device.

> In any case I think it's material for future work.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08 14:37             ` Alan Stern
  0 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-07-08 14:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:

> On Friday, July 08, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >
> > > Make generic PM domains support system-wide power transitions
> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > > poweroff and restore callbacks to be associated with struct
> > > generic_pm_domain objects and make pm_genpd_init() use them as
> > > appropriate.
> > >
> > > The new callbacks do nothing for devices belonging to power domains
> > > that were powered down at run time (before the transition).  
> > 
> > Thinking about this some more, how is a driver supposed to reconfigure
> > wakeups during suspend if it has already been runtime suspended?
> 
> If the device belongs to a PM domain that has been powered off, it
> won't be notified.
> 
> > For example, assume a device where device_may_wakeup() == false.  This
> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > assumed to enabled when it is runtime suspended.
> > 
> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > and then system suspend comes along.  
> > 
> > With this current patch, the driver will never receive any callbacks, so
> > it can never disable its wakeups.  
> >
> > Am I missing something?
> 
> As I said above, this only happens with devices that belog to PM domains
> that were powered off before system suspend has started, so the problem
> is limited to devices that wakeup is signaled on behalf of even when they
> have no power.
> 
> So this is a limitation, but not affecting all platforms.
> 
> There are a few ways to avoid this limitation I can think of:
> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
>     core, or pm_genpd_prepare()).

What's to prevent the device from being runtime-suspended again before 
the wakeup setting can be changed?

> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
>     devices regardless of whether or not their PM domains are off.
> (3) Call .prepare() from all drivers unconditionally during system suspend
>     (and probably .complete() too) in the hope they won't access inaccessible
>     devices.
> Probably, there's more.

In the PM domain's suspend code, do a runtime resume if the wakeup
setting needs to be changed, rather than simply skipping over the
device.

> In any case I think it's material for future work.

Alan Stern


^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08  9:24           ` Rafael J. Wysocki
  (?)
  (?)
@ 2011-07-08 14:37           ` Alan Stern
  -1 siblings, 0 replies; 261+ messages in thread
From: Alan Stern @ 2011-07-08 14:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:

> On Friday, July 08, 2011, Kevin Hilman wrote:
> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > 
> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >
> > > Make generic PM domains support system-wide power transitions
> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > > poweroff and restore callbacks to be associated with struct
> > > generic_pm_domain objects and make pm_genpd_init() use them as
> > > appropriate.
> > >
> > > The new callbacks do nothing for devices belonging to power domains
> > > that were powered down at run time (before the transition).  
> > 
> > Thinking about this some more, how is a driver supposed to reconfigure
> > wakeups during suspend if it has already been runtime suspended?
> 
> If the device belongs to a PM domain that has been powered off, it
> won't be notified.
> 
> > For example, assume a device where device_may_wakeup() == false.  This
> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > assumed to enabled when it is runtime suspended.
> > 
> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > and then system suspend comes along.  
> > 
> > With this current patch, the driver will never receive any callbacks, so
> > it can never disable its wakeups.  
> >
> > Am I missing something?
> 
> As I said above, this only happens with devices that belog to PM domains
> that were powered off before system suspend has started, so the problem
> is limited to devices that wakeup is signaled on behalf of even when they
> have no power.
> 
> So this is a limitation, but not affecting all platforms.
> 
> There are a few ways to avoid this limitation I can think of:
> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
>     core, or pm_genpd_prepare()).

What's to prevent the device from being runtime-suspended again before 
the wakeup setting can be changed?

> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
>     devices regardless of whether or not their PM domains are off.
> (3) Call .prepare() from all drivers unconditionally during system suspend
>     (and probably .complete() too) in the hope they won't access inaccessible
>     devices.
> Probably, there's more.

In the PM domain's suspend code, do a runtime resume if the wakeup
setting needs to be changed, rather than simply skipping over the
device.

> In any case I think it's material for future work.

Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 14:37             ` [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5) Alan Stern
@ 2011-07-08 17:20               ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-08 17:20 UTC (permalink / raw)
  To: Alan Stern
  Cc: Rafael J. Wysocki, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

Alan Stern <stern@rowland.harvard.edu> writes:

> On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
>
>> On Friday, July 08, 2011, Kevin Hilman wrote:
>> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> > 
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > Make generic PM domains support system-wide power transitions
>> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
>> > > poweroff and restore callbacks to be associated with struct
>> > > generic_pm_domain objects and make pm_genpd_init() use them as
>> > > appropriate.
>> > >
>> > > The new callbacks do nothing for devices belonging to power domains
>> > > that were powered down at run time (before the transition).  
>> > 
>> > Thinking about this some more, how is a driver supposed to reconfigure
>> > wakeups during suspend if it has already been runtime suspended?
>> 
>> If the device belongs to a PM domain that has been powered off, it
>> won't be notified.
>> 
>> > For example, assume a device where device_may_wakeup() = false.  This
>> > means wakeups during *suspend* are disabled, but wakeups wakeups are
>> > assumed to enabled when it is runtime suspended.
>> > 
>> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
>> > and then system suspend comes along.  
>> > 
>> > With this current patch, the driver will never receive any callbacks, so
>> > it can never disable its wakeups.  
>> >
>> > Am I missing something?
>> 
>> As I said above, this only happens with devices that belog to PM domains
>> that were powered off before system suspend has started, so the problem
>> is limited to devices that wakeup is signaled on behalf of even when they
>> have no power.

Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)

>> So this is a limitation, but not affecting all platforms.
>> 
>> There are a few ways to avoid this limitation I can think of:
>> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
>>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
>>     core, or pm_genpd_prepare()).
>
> What's to prevent the device from being runtime-suspended again before 
> the wakeup setting can be changed?
>
>> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
>>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
>>     devices regardless of whether or not their PM domains are off.
>> (3) Call .prepare() from all drivers unconditionally during system suspend
>>     (and probably .complete() too) in the hope they won't access inaccessible
>>     devices.

Like Alan's comment above for (1), I think the same applies for (3)
since runtime PM transitions can still happen between .prepare() and
.suspend()

Kevin

>> Probably, there's more.
>
> In the PM domain's suspend code, do a runtime resume if the wakeup
> setting needs to be changed, rather than simply skipping over the
> device.
>
>> In any case I think it's material for future work.
>
> Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08 17:20               ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-08 17:20 UTC (permalink / raw)
  To: Alan Stern
  Cc: Rafael J. Wysocki, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

Alan Stern <stern@rowland.harvard.edu> writes:

> On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
>
>> On Friday, July 08, 2011, Kevin Hilman wrote:
>> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> > 
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > Make generic PM domains support system-wide power transitions
>> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
>> > > poweroff and restore callbacks to be associated with struct
>> > > generic_pm_domain objects and make pm_genpd_init() use them as
>> > > appropriate.
>> > >
>> > > The new callbacks do nothing for devices belonging to power domains
>> > > that were powered down at run time (before the transition).  
>> > 
>> > Thinking about this some more, how is a driver supposed to reconfigure
>> > wakeups during suspend if it has already been runtime suspended?
>> 
>> If the device belongs to a PM domain that has been powered off, it
>> won't be notified.
>> 
>> > For example, assume a device where device_may_wakeup() == false.  This
>> > means wakeups during *suspend* are disabled, but wakeups wakeups are
>> > assumed to enabled when it is runtime suspended.
>> > 
>> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
>> > and then system suspend comes along.  
>> > 
>> > With this current patch, the driver will never receive any callbacks, so
>> > it can never disable its wakeups.  
>> >
>> > Am I missing something?
>> 
>> As I said above, this only happens with devices that belog to PM domains
>> that were powered off before system suspend has started, so the problem
>> is limited to devices that wakeup is signaled on behalf of even when they
>> have no power.

Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)

>> So this is a limitation, but not affecting all platforms.
>> 
>> There are a few ways to avoid this limitation I can think of:
>> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
>>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
>>     core, or pm_genpd_prepare()).
>
> What's to prevent the device from being runtime-suspended again before 
> the wakeup setting can be changed?
>
>> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
>>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
>>     devices regardless of whether or not their PM domains are off.
>> (3) Call .prepare() from all drivers unconditionally during system suspend
>>     (and probably .complete() too) in the hope they won't access inaccessible
>>     devices.

Like Alan's comment above for (1), I think the same applies for (3)
since runtime PM transitions can still happen between .prepare() and
.suspend()

Kevin

>> Probably, there's more.
>
> In the PM domain's suspend code, do a runtime resume if the wakeup
> setting needs to be changed, rather than simply skipping over the
> device.
>
>> In any case I think it's material for future work.
>
> Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 14:37             ` [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5) Alan Stern
  (?)
@ 2011-07-08 17:20             ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-08 17:20 UTC (permalink / raw)
  To: Alan Stern; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

Alan Stern <stern@rowland.harvard.edu> writes:

> On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
>
>> On Friday, July 08, 2011, Kevin Hilman wrote:
>> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
>> > 
>> > > From: Rafael J. Wysocki <rjw@sisk.pl>
>> > >
>> > > Make generic PM domains support system-wide power transitions
>> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
>> > > poweroff and restore callbacks to be associated with struct
>> > > generic_pm_domain objects and make pm_genpd_init() use them as
>> > > appropriate.
>> > >
>> > > The new callbacks do nothing for devices belonging to power domains
>> > > that were powered down at run time (before the transition).  
>> > 
>> > Thinking about this some more, how is a driver supposed to reconfigure
>> > wakeups during suspend if it has already been runtime suspended?
>> 
>> If the device belongs to a PM domain that has been powered off, it
>> won't be notified.
>> 
>> > For example, assume a device where device_may_wakeup() == false.  This
>> > means wakeups during *suspend* are disabled, but wakeups wakeups are
>> > assumed to enabled when it is runtime suspended.
>> > 
>> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
>> > and then system suspend comes along.  
>> > 
>> > With this current patch, the driver will never receive any callbacks, so
>> > it can never disable its wakeups.  
>> >
>> > Am I missing something?
>> 
>> As I said above, this only happens with devices that belog to PM domains
>> that were powered off before system suspend has started, so the problem
>> is limited to devices that wakeup is signaled on behalf of even when they
>> have no power.

Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)

>> So this is a limitation, but not affecting all platforms.
>> 
>> There are a few ways to avoid this limitation I can think of:
>> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
>>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
>>     core, or pm_genpd_prepare()).
>
> What's to prevent the device from being runtime-suspended again before 
> the wakeup setting can be changed?
>
>> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
>>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
>>     devices regardless of whether or not their PM domains are off.
>> (3) Call .prepare() from all drivers unconditionally during system suspend
>>     (and probably .complete() too) in the hope they won't access inaccessible
>>     devices.

Like Alan's comment above for (1), I think the same applies for (3)
since runtime PM transitions can still happen between .prepare() and
.suspend()

Kevin

>> Probably, there's more.
>
> In the PM domain's suspend code, do a runtime resume if the wakeup
> setting needs to be changed, rather than simply skipping over the
> device.
>
>> In any case I think it's material for future work.
>
> Alan Stern

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 14:37             ` [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5) Alan Stern
@ 2011-07-08 17:56               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 17:56 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Alan Stern wrote:
> On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> 
> > On Friday, July 08, 2011, Kevin Hilman wrote:
> > > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > > 
> > > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > > >
> > > > Make generic PM domains support system-wide power transitions
> > > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > > > poweroff and restore callbacks to be associated with struct
> > > > generic_pm_domain objects and make pm_genpd_init() use them as
> > > > appropriate.
> > > >
> > > > The new callbacks do nothing for devices belonging to power domains
> > > > that were powered down at run time (before the transition).  
> > > 
> > > Thinking about this some more, how is a driver supposed to reconfigure
> > > wakeups during suspend if it has already been runtime suspended?
> > 
> > If the device belongs to a PM domain that has been powered off, it
> > won't be notified.
> > 
> > > For example, assume a device where device_may_wakeup() = false.  This
> > > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > > assumed to enabled when it is runtime suspended.
> > > 
> > > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > > and then system suspend comes along.  
> > > 
> > > With this current patch, the driver will never receive any callbacks, so
> > > it can never disable its wakeups.  
> > >
> > > Am I missing something?
> > 
> > As I said above, this only happens with devices that belog to PM domains
> > that were powered off before system suspend has started, so the problem
> > is limited to devices that wakeup is signaled on behalf of even when they
> > have no power.
> > 
> > So this is a limitation, but not affecting all platforms.
> > 
> > There are a few ways to avoid this limitation I can think of:
> > (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> >     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> >     core, or pm_genpd_prepare()).
> 
> What's to prevent the device from being runtime-suspended again before 
> the wakeup setting can be changed?

If we do it from pm_genpd_prepare() after this patch:
https://lkml.org/lkml/2011/7/8/223
the pm_runtime_get_noresume() (and the subsequent disabling of runtime PM)
will prevent this from happening.

> > (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> >     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> >     devices regardless of whether or not their PM domains are off.
> > (3) Call .prepare() from all drivers unconditionally during system suspend
> >     (and probably .complete() too) in the hope they won't access inaccessible
> >     devices.
> > Probably, there's more.
> 
> In the PM domain's suspend code, do a runtime resume if the wakeup
> setting needs to be changed, rather than simply skipping over the
> device.

.suspend() is too late, we'd need to do that in .prepare().  If we did it
in .suspend(), we'd need to run .prepare() for all drivers, because at
this point we wouldn't know if the driver's .suspend() was going to be
called.  However, the driver's .prepare() may need to access the device,
which is going to fail if the whole domain is off at this point.

Also, we need a new flag that will tell the core code whether or not
the wakeup setting needs to be changed.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08 17:56               ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 17:56 UTC (permalink / raw)
  To: Alan Stern
  Cc: Kevin Hilman, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Alan Stern wrote:
> On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> 
> > On Friday, July 08, 2011, Kevin Hilman wrote:
> > > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > > 
> > > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > > >
> > > > Make generic PM domains support system-wide power transitions
> > > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > > > poweroff and restore callbacks to be associated with struct
> > > > generic_pm_domain objects and make pm_genpd_init() use them as
> > > > appropriate.
> > > >
> > > > The new callbacks do nothing for devices belonging to power domains
> > > > that were powered down at run time (before the transition).  
> > > 
> > > Thinking about this some more, how is a driver supposed to reconfigure
> > > wakeups during suspend if it has already been runtime suspended?
> > 
> > If the device belongs to a PM domain that has been powered off, it
> > won't be notified.
> > 
> > > For example, assume a device where device_may_wakeup() == false.  This
> > > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > > assumed to enabled when it is runtime suspended.
> > > 
> > > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > > and then system suspend comes along.  
> > > 
> > > With this current patch, the driver will never receive any callbacks, so
> > > it can never disable its wakeups.  
> > >
> > > Am I missing something?
> > 
> > As I said above, this only happens with devices that belog to PM domains
> > that were powered off before system suspend has started, so the problem
> > is limited to devices that wakeup is signaled on behalf of even when they
> > have no power.
> > 
> > So this is a limitation, but not affecting all platforms.
> > 
> > There are a few ways to avoid this limitation I can think of:
> > (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> >     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> >     core, or pm_genpd_prepare()).
> 
> What's to prevent the device from being runtime-suspended again before 
> the wakeup setting can be changed?

If we do it from pm_genpd_prepare() after this patch:
https://lkml.org/lkml/2011/7/8/223
the pm_runtime_get_noresume() (and the subsequent disabling of runtime PM)
will prevent this from happening.

> > (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> >     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> >     devices regardless of whether or not their PM domains are off.
> > (3) Call .prepare() from all drivers unconditionally during system suspend
> >     (and probably .complete() too) in the hope they won't access inaccessible
> >     devices.
> > Probably, there's more.
> 
> In the PM domain's suspend code, do a runtime resume if the wakeup
> setting needs to be changed, rather than simply skipping over the
> device.

.suspend() is too late, we'd need to do that in .prepare().  If we did it
in .suspend(), we'd need to run .prepare() for all drivers, because at
this point we wouldn't know if the driver's .suspend() was going to be
called.  However, the driver's .prepare() may need to access the device,
which is going to fail if the whole domain is off at this point.

Also, we need a new flag that will tell the core code whether or not
the wakeup setting needs to be changed.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 14:37             ` [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5) Alan Stern
                               ` (2 preceding siblings ...)
  (?)
@ 2011-07-08 17:56             ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 17:56 UTC (permalink / raw)
  To: Alan Stern; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 08, 2011, Alan Stern wrote:
> On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> 
> > On Friday, July 08, 2011, Kevin Hilman wrote:
> > > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > > 
> > > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > > >
> > > > Make generic PM domains support system-wide power transitions
> > > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > > > poweroff and restore callbacks to be associated with struct
> > > > generic_pm_domain objects and make pm_genpd_init() use them as
> > > > appropriate.
> > > >
> > > > The new callbacks do nothing for devices belonging to power domains
> > > > that were powered down at run time (before the transition).  
> > > 
> > > Thinking about this some more, how is a driver supposed to reconfigure
> > > wakeups during suspend if it has already been runtime suspended?
> > 
> > If the device belongs to a PM domain that has been powered off, it
> > won't be notified.
> > 
> > > For example, assume a device where device_may_wakeup() == false.  This
> > > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > > assumed to enabled when it is runtime suspended.
> > > 
> > > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > > and then system suspend comes along.  
> > > 
> > > With this current patch, the driver will never receive any callbacks, so
> > > it can never disable its wakeups.  
> > >
> > > Am I missing something?
> > 
> > As I said above, this only happens with devices that belog to PM domains
> > that were powered off before system suspend has started, so the problem
> > is limited to devices that wakeup is signaled on behalf of even when they
> > have no power.
> > 
> > So this is a limitation, but not affecting all platforms.
> > 
> > There are a few ways to avoid this limitation I can think of:
> > (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> >     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> >     core, or pm_genpd_prepare()).
> 
> What's to prevent the device from being runtime-suspended again before 
> the wakeup setting can be changed?

If we do it from pm_genpd_prepare() after this patch:
https://lkml.org/lkml/2011/7/8/223
the pm_runtime_get_noresume() (and the subsequent disabling of runtime PM)
will prevent this from happening.

> > (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> >     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> >     devices regardless of whether or not their PM domains are off.
> > (3) Call .prepare() from all drivers unconditionally during system suspend
> >     (and probably .complete() too) in the hope they won't access inaccessible
> >     devices.
> > Probably, there's more.
> 
> In the PM domain's suspend code, do a runtime resume if the wakeup
> setting needs to be changed, rather than simply skipping over the
> device.

.suspend() is too late, we'd need to do that in .prepare().  If we did it
in .suspend(), we'd need to run .prepare() for all drivers, because at
this point we wouldn't know if the driver's .suspend() was going to be
called.  However, the driver's .prepare() may need to access the device,
which is going to fail if the whole domain is off at this point.

Also, we need a new flag that will tell the core code whether or not
the wakeup setting needs to be changed.

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 17:20               ` Kevin Hilman
@ 2011-07-08 18:06                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 18:06 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Kevin Hilman wrote:
> Alan Stern <stern@rowland.harvard.edu> writes:
> 
> > On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> >
> >> On Friday, July 08, 2011, Kevin Hilman wrote:
> >> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> > 
> >> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > >
> >> > > Make generic PM domains support system-wide power transitions
> >> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> >> > > poweroff and restore callbacks to be associated with struct
> >> > > generic_pm_domain objects and make pm_genpd_init() use them as
> >> > > appropriate.
> >> > >
> >> > > The new callbacks do nothing for devices belonging to power domains
> >> > > that were powered down at run time (before the transition).  
> >> > 
> >> > Thinking about this some more, how is a driver supposed to reconfigure
> >> > wakeups during suspend if it has already been runtime suspended?
> >> 
> >> If the device belongs to a PM domain that has been powered off, it
> >> won't be notified.
> >> 
> >> > For example, assume a device where device_may_wakeup() = false.  This
> >> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> >> > assumed to enabled when it is runtime suspended.
> >> > 
> >> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> >> > and then system suspend comes along.  
> >> > 
> >> > With this current patch, the driver will never receive any callbacks, so
> >> > it can never disable its wakeups.  
> >> >
> >> > Am I missing something?
> >> 
> >> As I said above, this only happens with devices that belog to PM domains
> >> that were powered off before system suspend has started, so the problem
> >> is limited to devices that wakeup is signaled on behalf of even when they
> >> have no power.
> 
> Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)

So we'll need to avoid it _for_ _OMAP_.  Any of the platforms that will use
this code in near future doesn't have this limitation, AFAICS, so I don't
really see a point coding for it right now.

Your point seems to be that the whole thing has to be ready for OMAP
in advance and my point is that we can make it ready for OMAP later.
Which I believe is more pragmatic, because if every platform had adopted
the other viewpoint, we wouldn't have made any progress at all.

> >> So this is a limitation, but not affecting all platforms.
> >> 
> >> There are a few ways to avoid this limitation I can think of:
> >> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> >>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> >>     core, or pm_genpd_prepare()).
> >
> > What's to prevent the device from being runtime-suspended again before 
> > the wakeup setting can be changed?
> >
> >> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> >>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> >>     devices regardless of whether or not their PM domains are off.
> >> (3) Call .prepare() from all drivers unconditionally during system suspend
> >>     (and probably .complete() too) in the hope they won't access inaccessible
> >>     devices.
> 
> Like Alan's comment above for (1), I think the same applies for (3)
> since runtime PM transitions can still happen between .prepare() and
> .suspend()

Not with the current PM domains code (and please see my reply to Alan).

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08 18:06                 ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 18:06 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Kevin Hilman wrote:
> Alan Stern <stern@rowland.harvard.edu> writes:
> 
> > On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> >
> >> On Friday, July 08, 2011, Kevin Hilman wrote:
> >> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> > 
> >> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > >
> >> > > Make generic PM domains support system-wide power transitions
> >> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> >> > > poweroff and restore callbacks to be associated with struct
> >> > > generic_pm_domain objects and make pm_genpd_init() use them as
> >> > > appropriate.
> >> > >
> >> > > The new callbacks do nothing for devices belonging to power domains
> >> > > that were powered down at run time (before the transition).  
> >> > 
> >> > Thinking about this some more, how is a driver supposed to reconfigure
> >> > wakeups during suspend if it has already been runtime suspended?
> >> 
> >> If the device belongs to a PM domain that has been powered off, it
> >> won't be notified.
> >> 
> >> > For example, assume a device where device_may_wakeup() == false.  This
> >> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> >> > assumed to enabled when it is runtime suspended.
> >> > 
> >> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> >> > and then system suspend comes along.  
> >> > 
> >> > With this current patch, the driver will never receive any callbacks, so
> >> > it can never disable its wakeups.  
> >> >
> >> > Am I missing something?
> >> 
> >> As I said above, this only happens with devices that belog to PM domains
> >> that were powered off before system suspend has started, so the problem
> >> is limited to devices that wakeup is signaled on behalf of even when they
> >> have no power.
> 
> Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)

So we'll need to avoid it _for_ _OMAP_.  Any of the platforms that will use
this code in near future doesn't have this limitation, AFAICS, so I don't
really see a point coding for it right now.

Your point seems to be that the whole thing has to be ready for OMAP
in advance and my point is that we can make it ready for OMAP later.
Which I believe is more pragmatic, because if every platform had adopted
the other viewpoint, we wouldn't have made any progress at all.

> >> So this is a limitation, but not affecting all platforms.
> >> 
> >> There are a few ways to avoid this limitation I can think of:
> >> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> >>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> >>     core, or pm_genpd_prepare()).
> >
> > What's to prevent the device from being runtime-suspended again before 
> > the wakeup setting can be changed?
> >
> >> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> >>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> >>     devices regardless of whether or not their PM domains are off.
> >> (3) Call .prepare() from all drivers unconditionally during system suspend
> >>     (and probably .complete() too) in the hope they won't access inaccessible
> >>     devices.
> 
> Like Alan's comment above for (1), I think the same applies for (3)
> since runtime PM transitions can still happen between .prepare() and
> .suspend()

Not with the current PM domains code (and please see my reply to Alan).

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 17:20               ` Kevin Hilman
  (?)
  (?)
@ 2011-07-08 18:06               ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 18:06 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 08, 2011, Kevin Hilman wrote:
> Alan Stern <stern@rowland.harvard.edu> writes:
> 
> > On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> >
> >> On Friday, July 08, 2011, Kevin Hilman wrote:
> >> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> >> > 
> >> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > >
> >> > > Make generic PM domains support system-wide power transitions
> >> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> >> > > poweroff and restore callbacks to be associated with struct
> >> > > generic_pm_domain objects and make pm_genpd_init() use them as
> >> > > appropriate.
> >> > >
> >> > > The new callbacks do nothing for devices belonging to power domains
> >> > > that were powered down at run time (before the transition).  
> >> > 
> >> > Thinking about this some more, how is a driver supposed to reconfigure
> >> > wakeups during suspend if it has already been runtime suspended?
> >> 
> >> If the device belongs to a PM domain that has been powered off, it
> >> won't be notified.
> >> 
> >> > For example, assume a device where device_may_wakeup() == false.  This
> >> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> >> > assumed to enabled when it is runtime suspended.
> >> > 
> >> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> >> > and then system suspend comes along.  
> >> > 
> >> > With this current patch, the driver will never receive any callbacks, so
> >> > it can never disable its wakeups.  
> >> >
> >> > Am I missing something?
> >> 
> >> As I said above, this only happens with devices that belog to PM domains
> >> that were powered off before system suspend has started, so the problem
> >> is limited to devices that wakeup is signaled on behalf of even when they
> >> have no power.
> 
> Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)

So we'll need to avoid it _for_ _OMAP_.  Any of the platforms that will use
this code in near future doesn't have this limitation, AFAICS, so I don't
really see a point coding for it right now.

Your point seems to be that the whole thing has to be ready for OMAP
in advance and my point is that we can make it ready for OMAP later.
Which I believe is more pragmatic, because if every platform had adopted
the other viewpoint, we wouldn't have made any progress at all.

> >> So this is a limitation, but not affecting all platforms.
> >> 
> >> There are a few ways to avoid this limitation I can think of:
> >> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> >>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> >>     core, or pm_genpd_prepare()).
> >
> > What's to prevent the device from being runtime-suspended again before 
> > the wakeup setting can be changed?
> >
> >> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> >>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> >>     devices regardless of whether or not their PM domains are off.
> >> (3) Call .prepare() from all drivers unconditionally during system suspend
> >>     (and probably .complete() too) in the hope they won't access inaccessible
> >>     devices.
> 
> Like Alan's comment above for (1), I think the same applies for (3)
> since runtime PM transitions can still happen between .prepare() and
> .suspend()

Not with the current PM domains code (and please see my reply to Alan).

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 18:06                 ` Rafael J. Wysocki
@ 2011-07-08 19:24                   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 19:24 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Rafael J. Wysocki wrote:
> On Friday, July 08, 2011, Kevin Hilman wrote:
> > Alan Stern <stern@rowland.harvard.edu> writes:
> > 
> > > On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> > >
> > >> On Friday, July 08, 2011, Kevin Hilman wrote:
> > >> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > >> > 
> > >> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >> > >
> > >> > > Make generic PM domains support system-wide power transitions
> > >> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > >> > > poweroff and restore callbacks to be associated with struct
> > >> > > generic_pm_domain objects and make pm_genpd_init() use them as
> > >> > > appropriate.
> > >> > >
> > >> > > The new callbacks do nothing for devices belonging to power domains
> > >> > > that were powered down at run time (before the transition).  
> > >> > 
> > >> > Thinking about this some more, how is a driver supposed to reconfigure
> > >> > wakeups during suspend if it has already been runtime suspended?
> > >> 
> > >> If the device belongs to a PM domain that has been powered off, it
> > >> won't be notified.
> > >> 
> > >> > For example, assume a device where device_may_wakeup() = false.  This
> > >> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > >> > assumed to enabled when it is runtime suspended.
> > >> > 
> > >> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > >> > and then system suspend comes along.  
> > >> > 
> > >> > With this current patch, the driver will never receive any callbacks, so
> > >> > it can never disable its wakeups.  
> > >> >
> > >> > Am I missing something?
> > >> 
> > >> As I said above, this only happens with devices that belog to PM domains
> > >> that were powered off before system suspend has started, so the problem
> > >> is limited to devices that wakeup is signaled on behalf of even when they
> > >> have no power.
> > 
> > Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)
> 
> So we'll need to avoid it _for_ _OMAP_.  Any of the platforms that will use
> this code in near future doesn't have this limitation, AFAICS, so I don't
> really see a point coding for it right now.
> 
> Your point seems to be that the whole thing has to be ready for OMAP
> in advance and my point is that we can make it ready for OMAP later.
> Which I believe is more pragmatic, because if every platform had adopted
> the other viewpoint, we wouldn't have made any progress at all.
> 
> > >> So this is a limitation, but not affecting all platforms.
> > >> 
> > >> There are a few ways to avoid this limitation I can think of:
> > >> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> > >>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> > >>     core, or pm_genpd_prepare()).
> > >
> > > What's to prevent the device from being runtime-suspended again before 
> > > the wakeup setting can be changed?
> > >
> > >> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> > >>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> > >>     devices regardless of whether or not their PM domains are off.
> > >> (3) Call .prepare() from all drivers unconditionally during system suspend
> > >>     (and probably .complete() too) in the hope they won't access inaccessible
> > >>     devices.
> > 
> > Like Alan's comment above for (1), I think the same applies for (3)
> > since runtime PM transitions can still happen between .prepare() and
> > .suspend()
> 
> Not with the current PM domains code (and please see my reply to Alan).

Well, in fact, since we already have .active_wakeup(), what about the
following patch (on top of https://lkml.org/lkml/2011/7/8/223):

---
 drivers/base/power/domain.c |    4 ++++
 1 file changed, 4 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -519,6 +519,10 @@ static int pm_genpd_prepare(struct devic
 		return -EBUSY;
 	}
 
+	if (device_may_wakeup(dev)
+	    && !(genpd->active_wakeup && genpd->active_wakeup(dev)))
+		pm_runtime_resume(dev);
+
 	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count++ = 0)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-08 19:24                   ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 19:24 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Rafael J. Wysocki wrote:
> On Friday, July 08, 2011, Kevin Hilman wrote:
> > Alan Stern <stern@rowland.harvard.edu> writes:
> > 
> > > On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> > >
> > >> On Friday, July 08, 2011, Kevin Hilman wrote:
> > >> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > >> > 
> > >> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >> > >
> > >> > > Make generic PM domains support system-wide power transitions
> > >> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > >> > > poweroff and restore callbacks to be associated with struct
> > >> > > generic_pm_domain objects and make pm_genpd_init() use them as
> > >> > > appropriate.
> > >> > >
> > >> > > The new callbacks do nothing for devices belonging to power domains
> > >> > > that were powered down at run time (before the transition).  
> > >> > 
> > >> > Thinking about this some more, how is a driver supposed to reconfigure
> > >> > wakeups during suspend if it has already been runtime suspended?
> > >> 
> > >> If the device belongs to a PM domain that has been powered off, it
> > >> won't be notified.
> > >> 
> > >> > For example, assume a device where device_may_wakeup() == false.  This
> > >> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > >> > assumed to enabled when it is runtime suspended.
> > >> > 
> > >> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > >> > and then system suspend comes along.  
> > >> > 
> > >> > With this current patch, the driver will never receive any callbacks, so
> > >> > it can never disable its wakeups.  
> > >> >
> > >> > Am I missing something?
> > >> 
> > >> As I said above, this only happens with devices that belog to PM domains
> > >> that were powered off before system suspend has started, so the problem
> > >> is limited to devices that wakeup is signaled on behalf of even when they
> > >> have no power.
> > 
> > Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)
> 
> So we'll need to avoid it _for_ _OMAP_.  Any of the platforms that will use
> this code in near future doesn't have this limitation, AFAICS, so I don't
> really see a point coding for it right now.
> 
> Your point seems to be that the whole thing has to be ready for OMAP
> in advance and my point is that we can make it ready for OMAP later.
> Which I believe is more pragmatic, because if every platform had adopted
> the other viewpoint, we wouldn't have made any progress at all.
> 
> > >> So this is a limitation, but not affecting all platforms.
> > >> 
> > >> There are a few ways to avoid this limitation I can think of:
> > >> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> > >>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> > >>     core, or pm_genpd_prepare()).
> > >
> > > What's to prevent the device from being runtime-suspended again before 
> > > the wakeup setting can be changed?
> > >
> > >> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> > >>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> > >>     devices regardless of whether or not their PM domains are off.
> > >> (3) Call .prepare() from all drivers unconditionally during system suspend
> > >>     (and probably .complete() too) in the hope they won't access inaccessible
> > >>     devices.
> > 
> > Like Alan's comment above for (1), I think the same applies for (3)
> > since runtime PM transitions can still happen between .prepare() and
> > .suspend()
> 
> Not with the current PM domains code (and please see my reply to Alan).

Well, in fact, since we already have .active_wakeup(), what about the
following patch (on top of https://lkml.org/lkml/2011/7/8/223):

---
 drivers/base/power/domain.c |    4 ++++
 1 file changed, 4 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -519,6 +519,10 @@ static int pm_genpd_prepare(struct devic
 		return -EBUSY;
 	}
 
+	if (device_may_wakeup(dev)
+	    && !(genpd->active_wakeup && genpd->active_wakeup(dev)))
+		pm_runtime_resume(dev);
+
 	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count++ == 0)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 18:06                 ` Rafael J. Wysocki
  (?)
@ 2011-07-08 19:24                 ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-08 19:24 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 08, 2011, Rafael J. Wysocki wrote:
> On Friday, July 08, 2011, Kevin Hilman wrote:
> > Alan Stern <stern@rowland.harvard.edu> writes:
> > 
> > > On Fri, 8 Jul 2011, Rafael J. Wysocki wrote:
> > >
> > >> On Friday, July 08, 2011, Kevin Hilman wrote:
> > >> > "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> > >> > 
> > >> > > From: Rafael J. Wysocki <rjw@sisk.pl>
> > >> > >
> > >> > > Make generic PM domains support system-wide power transitions
> > >> > > (system suspend and hibernation).  Add suspend, resume, freeze, thaw,
> > >> > > poweroff and restore callbacks to be associated with struct
> > >> > > generic_pm_domain objects and make pm_genpd_init() use them as
> > >> > > appropriate.
> > >> > >
> > >> > > The new callbacks do nothing for devices belonging to power domains
> > >> > > that were powered down at run time (before the transition).  
> > >> > 
> > >> > Thinking about this some more, how is a driver supposed to reconfigure
> > >> > wakeups during suspend if it has already been runtime suspended?
> > >> 
> > >> If the device belongs to a PM domain that has been powered off, it
> > >> won't be notified.
> > >> 
> > >> > For example, assume a device where device_may_wakeup() == false.  This
> > >> > means wakeups during *suspend* are disabled, but wakeups wakeups are
> > >> > assumed to enabled when it is runtime suspended.
> > >> > 
> > >> > So now, assume this device is RPM_SUSPENDED, it has wakeups *enabled*,
> > >> > and then system suspend comes along.  
> > >> > 
> > >> > With this current patch, the driver will never receive any callbacks, so
> > >> > it can never disable its wakeups.  
> > >> >
> > >> > Am I missing something?
> > >> 
> > >> As I said above, this only happens with devices that belog to PM domains
> > >> that were powered off before system suspend has started, so the problem
> > >> is limited to devices that wakeup is signaled on behalf of even when they
> > >> have no power.
> > 
> > Which on OMAP, is *all* devices, so that's a pretty major limitation.  :)
> 
> So we'll need to avoid it _for_ _OMAP_.  Any of the platforms that will use
> this code in near future doesn't have this limitation, AFAICS, so I don't
> really see a point coding for it right now.
> 
> Your point seems to be that the whole thing has to be ready for OMAP
> in advance and my point is that we can make it ready for OMAP later.
> Which I believe is more pragmatic, because if every platform had adopted
> the other viewpoint, we wouldn't have made any progress at all.
> 
> > >> So this is a limitation, but not affecting all platforms.
> > >> 
> > >> There are a few ways to avoid this limitation I can think of:
> > >> (1) Add a "make me operational during system suspend" flag to struct dev_pm_info
> > >>     and run pm_runtime_resume() on such devices from the core (either dpm_prepare()
> > >>     core, or pm_genpd_prepare()).
> > >
> > > What's to prevent the device from being runtime-suspended again before 
> > > the wakeup setting can be changed?
> > >
> > >> (2) Add a "my .prepare() is safe to run if device is not accessible" flag to
> > >>     struct dev_pm_info and make pm_genpd_prepare() execute .prepare() for such
> > >>     devices regardless of whether or not their PM domains are off.
> > >> (3) Call .prepare() from all drivers unconditionally during system suspend
> > >>     (and probably .complete() too) in the hope they won't access inaccessible
> > >>     devices.
> > 
> > Like Alan's comment above for (1), I think the same applies for (3)
> > since runtime PM transitions can still happen between .prepare() and
> > .suspend()
> 
> Not with the current PM domains code (and please see my reply to Alan).

Well, in fact, since we already have .active_wakeup(), what about the
following patch (on top of https://lkml.org/lkml/2011/7/8/223):

---
 drivers/base/power/domain.c |    4 ++++
 1 file changed, 4 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -519,6 +519,10 @@ static int pm_genpd_prepare(struct devic
 		return -EBUSY;
 	}
 
+	if (device_may_wakeup(dev)
+	    && !(genpd->active_wakeup && genpd->active_wakeup(dev)))
+		pm_runtime_resume(dev);
+
 	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count++ == 0)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 19:24                   ` Rafael J. Wysocki
@ 2011-07-09 14:15                     ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-09 14:15 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Rafael J. Wysocki wrote:
...
> 
> Well, in fact, since we already have .active_wakeup(), what about the
> following patch (on top of https://lkml.org/lkml/2011/7/8/223):

Well, it is wrong, because we've been discussing devices that are _not_
enabled to wake up the system.

> ---
>  drivers/base/power/domain.c |    4 ++++
>  1 file changed, 4 insertions(+)
> 
> Index: linux-2.6/drivers/base/power/domain.c
> =================================> --- linux-2.6.orig/drivers/base/power/domain.c
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -519,6 +519,10 @@ static int pm_genpd_prepare(struct devic
>  		return -EBUSY;
>  	}
>  
> +	if (device_may_wakeup(dev)

So, this should have been !device_may_wakeup(dev), _but_ ...

> +	    && !(genpd->active_wakeup && genpd->active_wakeup(dev)))
> +		pm_runtime_resume(dev);
> +
>  	genpd_acquire_lock(genpd);
>  
>  	if (genpd->prepared_count++ = 0)

There's one more case to consider, namely devices that are runtime
suspended, set up to wake up the system from sleep states (ie. device_may_wakeup(dev)
returns "true") and such that genpd->active_wakeup(dev) returns "true" for them,
because they need to be resumed at this point too (arguably, it makes a little
sense to runtime suspend such devices, but that's possible in principle).

So, IMO, the patch should look like this:

---
 drivers/base/power/domain.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
=================================--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
 }
 
 /**
+ * resume_needed - Check whether to resume a device before system suspend.
+ * @dev: Device to handle.
+ * @genpd: PM domain the device belongs to.
+ */
+static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
+{
+	bool active_wakeup;
+
+	if (!device_can_wakeup(dev))
+		return false;
+
+	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
+	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
+}
+
+/**
  * pm_genpd_prepare - Start power transition of a device in a PM domain.
  * @dev: Device to start the transition of.
  *
@@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
 		return -EBUSY;
 	}
 
+	if (resume_needed(dev, genpd))
+		pm_runtime_resume(dev);
+
 	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count++ = 0)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-09 14:15                     ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-09 14:15 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Friday, July 08, 2011, Rafael J. Wysocki wrote:
...
> 
> Well, in fact, since we already have .active_wakeup(), what about the
> following patch (on top of https://lkml.org/lkml/2011/7/8/223):

Well, it is wrong, because we've been discussing devices that are _not_
enabled to wake up the system.

> ---
>  drivers/base/power/domain.c |    4 ++++
>  1 file changed, 4 insertions(+)
> 
> Index: linux-2.6/drivers/base/power/domain.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/power/domain.c
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -519,6 +519,10 @@ static int pm_genpd_prepare(struct devic
>  		return -EBUSY;
>  	}
>  
> +	if (device_may_wakeup(dev)

So, this should have been !device_may_wakeup(dev), _but_ ...

> +	    && !(genpd->active_wakeup && genpd->active_wakeup(dev)))
> +		pm_runtime_resume(dev);
> +
>  	genpd_acquire_lock(genpd);
>  
>  	if (genpd->prepared_count++ == 0)

There's one more case to consider, namely devices that are runtime
suspended, set up to wake up the system from sleep states (ie. device_may_wakeup(dev)
returns "true") and such that genpd->active_wakeup(dev) returns "true" for them,
because they need to be resumed at this point too (arguably, it makes a little
sense to runtime suspend such devices, but that's possible in principle).

So, IMO, the patch should look like this:

---
 drivers/base/power/domain.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
 }
 
 /**
+ * resume_needed - Check whether to resume a device before system suspend.
+ * @dev: Device to handle.
+ * @genpd: PM domain the device belongs to.
+ */
+static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
+{
+	bool active_wakeup;
+
+	if (!device_can_wakeup(dev))
+		return false;
+
+	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
+	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
+}
+
+/**
  * pm_genpd_prepare - Start power transition of a device in a PM domain.
  * @dev: Device to start the transition of.
  *
@@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
 		return -EBUSY;
 	}
 
+	if (resume_needed(dev, genpd))
+		pm_runtime_resume(dev);
+
 	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count++ == 0)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-08 19:24                   ` Rafael J. Wysocki
  (?)
@ 2011-07-09 14:15                   ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-09 14:15 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Friday, July 08, 2011, Rafael J. Wysocki wrote:
...
> 
> Well, in fact, since we already have .active_wakeup(), what about the
> following patch (on top of https://lkml.org/lkml/2011/7/8/223):

Well, it is wrong, because we've been discussing devices that are _not_
enabled to wake up the system.

> ---
>  drivers/base/power/domain.c |    4 ++++
>  1 file changed, 4 insertions(+)
> 
> Index: linux-2.6/drivers/base/power/domain.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/power/domain.c
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -519,6 +519,10 @@ static int pm_genpd_prepare(struct devic
>  		return -EBUSY;
>  	}
>  
> +	if (device_may_wakeup(dev)

So, this should have been !device_may_wakeup(dev), _but_ ...

> +	    && !(genpd->active_wakeup && genpd->active_wakeup(dev)))
> +		pm_runtime_resume(dev);
> +
>  	genpd_acquire_lock(genpd);
>  
>  	if (genpd->prepared_count++ == 0)

There's one more case to consider, namely devices that are runtime
suspended, set up to wake up the system from sleep states (ie. device_may_wakeup(dev)
returns "true") and such that genpd->active_wakeup(dev) returns "true" for them,
because they need to be resumed at this point too (arguably, it makes a little
sense to runtime suspend such devices, but that's possible in principle).

So, IMO, the patch should look like this:

---
 drivers/base/power/domain.c |   19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

Index: linux-2.6/drivers/base/power/domain.c
===================================================================
--- linux-2.6.orig/drivers/base/power/domain.c
+++ linux-2.6/drivers/base/power/domain.c
@@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
 }
 
 /**
+ * resume_needed - Check whether to resume a device before system suspend.
+ * @dev: Device to handle.
+ * @genpd: PM domain the device belongs to.
+ */
+static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
+{
+	bool active_wakeup;
+
+	if (!device_can_wakeup(dev))
+		return false;
+
+	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
+	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
+}
+
+/**
  * pm_genpd_prepare - Start power transition of a device in a PM domain.
  * @dev: Device to start the transition of.
  *
@@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
 		return -EBUSY;
 	}
 
+	if (resume_needed(dev, genpd))
+		pm_runtime_resume(dev);
+
 	genpd_acquire_lock(genpd);
 
 	if (genpd->prepared_count++ == 0)

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-15 14:17         ` Magnus Damm
@ 2011-07-10 11:45           ` Laurent Pinchart
  -1 siblings, 0 replies; 261+ messages in thread
From: Laurent Pinchart @ 2011-07-10 11:45 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Rafael J. Wysocki, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

Hi Magnus,

On Wednesday 15 June 2011 16:17:15 Magnus Damm wrote:
> On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Tuesday, June 14, 2011, Magnus Damm wrote:
> >> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > 
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> > 
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >> 
> >> Thanks for your work on this. I just tried this on my Mackerel board,
> >> but I can't seem to get the pd_power_up() and pd_power_down()
> >> callbacks to be executed. It is probably a misconfiguration from my
> >> side.
> > 
> > They trigger for me e.g. after doing
> > 
> > # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> > 
> > Attached is the .config I've been using.
> 
> Thanks, I can trigger using sysfs and your kernel configuration.
> 
> However, I assumed it also would work when the sceen saver kicked in.
> I recall it being fbcon that controls the screen save, perhaps
> something else. So just wait a bit and see if you also can reproduce
> it. The console gets black but the power is still on...

That's because, by default, the VT layer will use FB_BLANK_NORMAL mode when 
blanking the console. Switching that to FB_BLANK_VSYNC_SUSPEND, 
FB_BLANK_HSYNC_SUSPEND or FB_BLANK_POWERDOWN with TIOCLINUX(TIOC_SETVESABLANK) 
results in the device being runtime-suspended when the console is blanked.

> Also forcing to go back to powered-on state (see below) doesn't work that
> well: # echo 0 >
> /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> 
> It looks like we loose the panning information somehow. Most likely a LCDC
> driver bug. Unless the driver callbacks are not being invoked as expected.
>
> Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
> enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
> as a LCD cache. It sits in the same hardware power domain as the
> LCDCs. I don't think the MERAM software supports power down
> unfortunately. Disabling MERAM support removes the garbage on the
> screen.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
@ 2011-07-10 11:45           ` Laurent Pinchart
  0 siblings, 0 replies; 261+ messages in thread
From: Laurent Pinchart @ 2011-07-10 11:45 UTC (permalink / raw)
  To: Magnus Damm
  Cc: Rafael J. Wysocki, Linux PM mailing list, Greg Kroah-Hartman,
	Paul Walmsley, Kevin Hilman, Alan Stern, LKML, linux-sh

Hi Magnus,

On Wednesday 15 June 2011 16:17:15 Magnus Damm wrote:
> On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Tuesday, June 14, 2011, Magnus Damm wrote:
> >> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > 
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> > 
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >> 
> >> Thanks for your work on this. I just tried this on my Mackerel board,
> >> but I can't seem to get the pd_power_up() and pd_power_down()
> >> callbacks to be executed. It is probably a misconfiguration from my
> >> side.
> > 
> > They trigger for me e.g. after doing
> > 
> > # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> > 
> > Attached is the .config I've been using.
> 
> Thanks, I can trigger using sysfs and your kernel configuration.
> 
> However, I assumed it also would work when the sceen saver kicked in.
> I recall it being fbcon that controls the screen save, perhaps
> something else. So just wait a bit and see if you also can reproduce
> it. The console gets black but the power is still on...

That's because, by default, the VT layer will use FB_BLANK_NORMAL mode when 
blanking the console. Switching that to FB_BLANK_VSYNC_SUSPEND, 
FB_BLANK_HSYNC_SUSPEND or FB_BLANK_POWERDOWN with TIOCLINUX(TIOC_SETVESABLANK) 
results in the device being runtime-suspended when the console is blanked.

> Also forcing to go back to powered-on state (see below) doesn't work that
> well: # echo 0 >
> /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> 
> It looks like we loose the panning information somehow. Most likely a LCDC
> driver bug. Unless the driver callbacks are not being invoked as expected.
>
> Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
> enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
> as a LCD cache. It sits in the same hardware power domain as the
> LCDCs. I don't think the MERAM software supports power down
> unfortunately. Disabling MERAM support removes the garbage on the
> screen.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5)
  2011-06-15 14:17         ` Magnus Damm
                           ` (2 preceding siblings ...)
  (?)
@ 2011-07-10 11:45         ` Laurent Pinchart
  -1 siblings, 0 replies; 261+ messages in thread
From: Laurent Pinchart @ 2011-07-10 11:45 UTC (permalink / raw)
  To: Magnus Damm; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

Hi Magnus,

On Wednesday 15 June 2011 16:17:15 Magnus Damm wrote:
> On Wed, Jun 15, 2011 at 6:16 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> > On Tuesday, June 14, 2011, Magnus Damm wrote:
> >> On Sun, Jun 12, 2011 at 5:40 AM, Rafael J. Wysocki <rjw@sisk.pl> wrote:
> >> > From: Rafael J. Wysocki <rjw@sisk.pl>
> >> > 
> >> > Use the generic power domains support introduced by the previous
> >> > patch to implement support for power domains on SH7372.
> >> > 
> >> > Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
> >> > ---
> >> 
> >> Thanks for your work on this. I just tried this on my Mackerel board,
> >> but I can't seem to get the pd_power_up() and pd_power_down()
> >> callbacks to be executed. It is probably a misconfiguration from my
> >> side.
> > 
> > They trigger for me e.g. after doing
> > 
> > # echo 3 > /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> > 
> > Attached is the .config I've been using.
> 
> Thanks, I can trigger using sysfs and your kernel configuration.
> 
> However, I assumed it also would work when the sceen saver kicked in.
> I recall it being fbcon that controls the screen save, perhaps
> something else. So just wait a bit and see if you also can reproduce
> it. The console gets black but the power is still on...

That's because, by default, the VT layer will use FB_BLANK_NORMAL mode when 
blanking the console. Switching that to FB_BLANK_VSYNC_SUSPEND, 
FB_BLANK_HSYNC_SUSPEND or FB_BLANK_POWERDOWN with TIOCLINUX(TIOC_SETVESABLANK) 
results in the device being runtime-suspended when the console is blanked.

> Also forcing to go back to powered-on state (see below) doesn't work that
> well: # echo 0 >
> /sys/devices/platform/sh_mobile_lcdc_fb.0/graphics/fb0/blank
> 
> It looks like we loose the panning information somehow. Most likely a LCDC
> driver bug. Unless the driver callbacks are not being invoked as expected.
>
> Also, there is garbage in on the screen if FB_SH_MOBILE_MERAM is
> enabled. The MERAM hardware is a 1.5 MiB memory block that can be used
> as a LCD cache. It sits in the same hardware power domain as the
> LCDCs. I don't think the MERAM software supports power down
> unfortunately. Disabling MERAM support removes the garbage on the
> screen.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-09 14:15                     ` Rafael J. Wysocki
@ 2011-07-11 15:37                       ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-11 15:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

[...]

>
> There's one more case to consider, namely devices that are runtime
> suspended, set up to wake up the system from sleep states
> (ie. device_may_wakeup(dev) returns "true") and such that
> genpd->active_wakeup(dev) returns "true" for them, because they need
> to be resumed at this point too (arguably, it makes a little sense to
> runtime suspend such devices, but that's possible in principle).
>
> So, IMO, the patch should look like this:
>
> ---
>  drivers/base/power/domain.c |   19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>
>> Index: linux-2.6/drivers/base/power/domain.c
> =================================> --- linux-2.6.orig/drivers/base/power/domain.c
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
>  }
>  
>  /**
> + * resume_needed - Check whether to resume a device before system suspend.
> + * @dev: Device to handle.
> + * @genpd: PM domain the device belongs to.
> + */
> +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> +{
> +	bool active_wakeup;
> +
> +	if (!device_can_wakeup(dev))
> +		return false;
> +
> +	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> +	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;

This also returns true and causes a resume if active_wakeup = false and
device_may_wakeup() = false.  That doesn't seem right.

> +}
> +
> +/**
>   * pm_genpd_prepare - Start power transition of a device in a PM domain.
>   * @dev: Device to start the transition of.
>   *
> @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
>  		return -EBUSY;
>  	}
>  
> +	if (resume_needed(dev, genpd))
> +		pm_runtime_resume(dev);
> +
>  	genpd_acquire_lock(genpd);
>  
>  	if (genpd->prepared_count++ = 0)

IIUC, if a device is runtime suspended when a system suspend happens,
the device will be runtime resumed, but never re-suspended.

Should resumes by the PM core be done with a get (and a corresponding
put in .complete())?

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-11 15:37                       ` Kevin Hilman
  0 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-11 15:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

[...]

>
> There's one more case to consider, namely devices that are runtime
> suspended, set up to wake up the system from sleep states
> (ie. device_may_wakeup(dev) returns "true") and such that
> genpd->active_wakeup(dev) returns "true" for them, because they need
> to be resumed at this point too (arguably, it makes a little sense to
> runtime suspend such devices, but that's possible in principle).
>
> So, IMO, the patch should look like this:
>
> ---
>  drivers/base/power/domain.c |   19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>
>> Index: linux-2.6/drivers/base/power/domain.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/power/domain.c
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
>  }
>  
>  /**
> + * resume_needed - Check whether to resume a device before system suspend.
> + * @dev: Device to handle.
> + * @genpd: PM domain the device belongs to.
> + */
> +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> +{
> +	bool active_wakeup;
> +
> +	if (!device_can_wakeup(dev))
> +		return false;
> +
> +	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> +	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;

This also returns true and causes a resume if active_wakeup = false and
device_may_wakeup() = false.  That doesn't seem right.

> +}
> +
> +/**
>   * pm_genpd_prepare - Start power transition of a device in a PM domain.
>   * @dev: Device to start the transition of.
>   *
> @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
>  		return -EBUSY;
>  	}
>  
> +	if (resume_needed(dev, genpd))
> +		pm_runtime_resume(dev);
> +
>  	genpd_acquire_lock(genpd);
>  
>  	if (genpd->prepared_count++ == 0)

IIUC, if a device is runtime suspended when a system suspend happens,
the device will be runtime resumed, but never re-suspended.

Should resumes by the PM core be done with a get (and a corresponding
put in .complete())?

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-09 14:15                     ` Rafael J. Wysocki
  (?)
@ 2011-07-11 15:37                     ` Kevin Hilman
  -1 siblings, 0 replies; 261+ messages in thread
From: Kevin Hilman @ 2011-07-11 15:37 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

"Rafael J. Wysocki" <rjw@sisk.pl> writes:

[...]

>
> There's one more case to consider, namely devices that are runtime
> suspended, set up to wake up the system from sleep states
> (ie. device_may_wakeup(dev) returns "true") and such that
> genpd->active_wakeup(dev) returns "true" for them, because they need
> to be resumed at this point too (arguably, it makes a little sense to
> runtime suspend such devices, but that's possible in principle).
>
> So, IMO, the patch should look like this:
>
> ---
>  drivers/base/power/domain.c |   19 +++++++++++++++++++
>  1 file changed, 19 insertions(+)
>
>> Index: linux-2.6/drivers/base/power/domain.c
> ===================================================================
> --- linux-2.6.orig/drivers/base/power/domain.c
> +++ linux-2.6/drivers/base/power/domain.c
> @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
>  }
>  
>  /**
> + * resume_needed - Check whether to resume a device before system suspend.
> + * @dev: Device to handle.
> + * @genpd: PM domain the device belongs to.
> + */
> +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> +{
> +	bool active_wakeup;
> +
> +	if (!device_can_wakeup(dev))
> +		return false;
> +
> +	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> +	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;

This also returns true and causes a resume if active_wakeup = false and
device_may_wakeup() = false.  That doesn't seem right.

> +}
> +
> +/**
>   * pm_genpd_prepare - Start power transition of a device in a PM domain.
>   * @dev: Device to start the transition of.
>   *
> @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
>  		return -EBUSY;
>  	}
>  
> +	if (resume_needed(dev, genpd))
> +		pm_runtime_resume(dev);
> +
>  	genpd_acquire_lock(genpd);
>  
>  	if (genpd->prepared_count++ == 0)

IIUC, if a device is runtime suspended when a system suspend happens,
the device will be runtime resumed, but never re-suspended.

Should resumes by the PM core be done with a get (and a corresponding
put in .complete())?

Kevin

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-11 15:37                       ` Kevin Hilman
@ 2011-07-11 19:39                         ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-11 19:39 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Monday, July 11, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> [...]
> 
> >
> > There's one more case to consider, namely devices that are runtime
> > suspended, set up to wake up the system from sleep states
> > (ie. device_may_wakeup(dev) returns "true") and such that
> > genpd->active_wakeup(dev) returns "true" for them, because they need
> > to be resumed at this point too (arguably, it makes a little sense to
> > runtime suspend such devices, but that's possible in principle).
> >
> > So, IMO, the patch should look like this:
> >
> > ---
> >  drivers/base/power/domain.c |   19 +++++++++++++++++++
> >  1 file changed, 19 insertions(+)
> >
> >> Index: linux-2.6/drivers/base/power/domain.c
> > =================================> > --- linux-2.6.orig/drivers/base/power/domain.c
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
> >  }
> >  
> >  /**
> > + * resume_needed - Check whether to resume a device before system suspend.
> > + * @dev: Device to handle.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> > +{
> > +	bool active_wakeup;
> > +
> > +	if (!device_can_wakeup(dev))
> > +		return false;
> > +
> > +	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> > +	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
> 
> This also returns true and causes a resume if active_wakeup = false and
> device_may_wakeup() = false.  That doesn't seem right.

This is on purpose. :-)  If active_wakeup is false, the device may signal
remote wakeup while suspended.  So, if active_wakeup is false and the device
is suspended, we have to assume that the device has been set up to signal
remote wakeup for runtime PM (if it is not suspended, attempting to resume it
will not have any effect).  Now, if device_may_wakeup() returns false in
addition to that, we may need to change the device's wakeup settings, so the
driver's callbacks should be invoked during suspend, so we're resuming the
device (we can't just leave it suspended and then invoke the driver's callbacks
in the hope they'll do the right thing).

I don't really think we can do anything else without using new device flags.

> > +}
> > +
> > +/**
> >   * pm_genpd_prepare - Start power transition of a device in a PM domain.
> >   * @dev: Device to start the transition of.
> >   *
> > @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
> >  		return -EBUSY;
> >  	}
> >  
> > +	if (resume_needed(dev, genpd))
> > +		pm_runtime_resume(dev);
> > +
> >  	genpd_acquire_lock(genpd);
> >  
> >  	if (genpd->prepared_count++ = 0)
> 
> IIUC, if a device is runtime suspended when a system suspend happens,
> the device will be runtime resumed, but never re-suspended.

It will be resuspended by the pm_runtime_idle() in pm_genpd_complete()
(added by one of the new patches I've been posting for the last few days).

> Should resumes by the PM core be done with a get (and a corresponding
> put in .complete())?

Not necessarily. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
@ 2011-07-11 19:39                         ` Rafael J. Wysocki
  0 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-11 19:39 UTC (permalink / raw)
  To: Kevin Hilman
  Cc: Alan Stern, Linux PM mailing list, Greg Kroah-Hartman,
	Magnus Damm, Paul Walmsley, LKML, linux-sh, Paul Mundt

On Monday, July 11, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> [...]
> 
> >
> > There's one more case to consider, namely devices that are runtime
> > suspended, set up to wake up the system from sleep states
> > (ie. device_may_wakeup(dev) returns "true") and such that
> > genpd->active_wakeup(dev) returns "true" for them, because they need
> > to be resumed at this point too (arguably, it makes a little sense to
> > runtime suspend such devices, but that's possible in principle).
> >
> > So, IMO, the patch should look like this:
> >
> > ---
> >  drivers/base/power/domain.c |   19 +++++++++++++++++++
> >  1 file changed, 19 insertions(+)
> >
> >> Index: linux-2.6/drivers/base/power/domain.c
> > ===================================================================
> > --- linux-2.6.orig/drivers/base/power/domain.c
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
> >  }
> >  
> >  /**
> > + * resume_needed - Check whether to resume a device before system suspend.
> > + * @dev: Device to handle.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> > +{
> > +	bool active_wakeup;
> > +
> > +	if (!device_can_wakeup(dev))
> > +		return false;
> > +
> > +	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> > +	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
> 
> This also returns true and causes a resume if active_wakeup = false and
> device_may_wakeup() = false.  That doesn't seem right.

This is on purpose. :-)  If active_wakeup is false, the device may signal
remote wakeup while suspended.  So, if active_wakeup is false and the device
is suspended, we have to assume that the device has been set up to signal
remote wakeup for runtime PM (if it is not suspended, attempting to resume it
will not have any effect).  Now, if device_may_wakeup() returns false in
addition to that, we may need to change the device's wakeup settings, so the
driver's callbacks should be invoked during suspend, so we're resuming the
device (we can't just leave it suspended and then invoke the driver's callbacks
in the hope they'll do the right thing).

I don't really think we can do anything else without using new device flags.

> > +}
> > +
> > +/**
> >   * pm_genpd_prepare - Start power transition of a device in a PM domain.
> >   * @dev: Device to start the transition of.
> >   *
> > @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
> >  		return -EBUSY;
> >  	}
> >  
> > +	if (resume_needed(dev, genpd))
> > +		pm_runtime_resume(dev);
> > +
> >  	genpd_acquire_lock(genpd);
> >  
> >  	if (genpd->prepared_count++ == 0)
> 
> IIUC, if a device is runtime suspended when a system suspend happens,
> the device will be runtime resumed, but never re-suspended.

It will be resuspended by the pm_runtime_idle() in pm_genpd_complete()
(added by one of the new patches I've been posting for the last few days).

> Should resumes by the PM core be done with a get (and a corresponding
> put in .complete())?

Not necessarily. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

* Re: [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5)
  2011-07-11 15:37                       ` Kevin Hilman
  (?)
  (?)
@ 2011-07-11 19:39                       ` Rafael J. Wysocki
  -1 siblings, 0 replies; 261+ messages in thread
From: Rafael J. Wysocki @ 2011-07-11 19:39 UTC (permalink / raw)
  To: Kevin Hilman; +Cc: linux-sh, Greg Kroah-Hartman, LKML, Linux PM mailing list

On Monday, July 11, 2011, Kevin Hilman wrote:
> "Rafael J. Wysocki" <rjw@sisk.pl> writes:
> 
> [...]
> 
> >
> > There's one more case to consider, namely devices that are runtime
> > suspended, set up to wake up the system from sleep states
> > (ie. device_may_wakeup(dev) returns "true") and such that
> > genpd->active_wakeup(dev) returns "true" for them, because they need
> > to be resumed at this point too (arguably, it makes a little sense to
> > runtime suspend such devices, but that's possible in principle).
> >
> > So, IMO, the patch should look like this:
> >
> > ---
> >  drivers/base/power/domain.c |   19 +++++++++++++++++++
> >  1 file changed, 19 insertions(+)
> >
> >> Index: linux-2.6/drivers/base/power/domain.c
> > ===================================================================
> > --- linux-2.6.orig/drivers/base/power/domain.c
> > +++ linux-2.6/drivers/base/power/domain.c
> > @@ -486,6 +486,22 @@ static void pm_genpd_sync_poweroff(struc
> >  }
> >  
> >  /**
> > + * resume_needed - Check whether to resume a device before system suspend.
> > + * @dev: Device to handle.
> > + * @genpd: PM domain the device belongs to.
> > + */
> > +static bool resume_needed(struct device *dev, struct generic_pm_domain *genpd)
> > +{
> > +	bool active_wakeup;
> > +
> > +	if (!device_can_wakeup(dev))
> > +		return false;
> > +
> > +	active_wakeup = genpd->active_wakeup && genpd->active_wakeup(dev);
> > +	return device_may_wakeup(dev) ? active_wakeup : !active_wakeup;
> 
> This also returns true and causes a resume if active_wakeup = false and
> device_may_wakeup() = false.  That doesn't seem right.

This is on purpose. :-)  If active_wakeup is false, the device may signal
remote wakeup while suspended.  So, if active_wakeup is false and the device
is suspended, we have to assume that the device has been set up to signal
remote wakeup for runtime PM (if it is not suspended, attempting to resume it
will not have any effect).  Now, if device_may_wakeup() returns false in
addition to that, we may need to change the device's wakeup settings, so the
driver's callbacks should be invoked during suspend, so we're resuming the
device (we can't just leave it suspended and then invoke the driver's callbacks
in the hope they'll do the right thing).

I don't really think we can do anything else without using new device flags.

> > +}
> > +
> > +/**
> >   * pm_genpd_prepare - Start power transition of a device in a PM domain.
> >   * @dev: Device to start the transition of.
> >   *
> > @@ -519,6 +535,9 @@ static int pm_genpd_prepare(struct devic
> >  		return -EBUSY;
> >  	}
> >  
> > +	if (resume_needed(dev, genpd))
> > +		pm_runtime_resume(dev);
> > +
> >  	genpd_acquire_lock(genpd);
> >  
> >  	if (genpd->prepared_count++ == 0)
> 
> IIUC, if a device is runtime suspended when a system suspend happens,
> the device will be runtime resumed, but never re-suspended.

It will be resuspended by the pm_runtime_idle() in pm_genpd_complete()
(added by one of the new patches I've been posting for the last few days).

> Should resumes by the PM core be done with a get (and a corresponding
> put in .complete())?

Not necessarily. :-)

Thanks,
Rafael

^ permalink raw reply	[flat|nested] 261+ messages in thread

end of thread, other threads:[~2011-07-11 19:39 UTC | newest]

Thread overview: 261+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-06-11 20:23 [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5) Rafael J. Wysocki
2011-06-11 20:23 ` Rafael J. Wysocki
2011-06-11 20:23 ` Rafael J. Wysocki
2011-06-11 20:25 ` [PATCH 1/8] PM / Domains: Update documentation Rafael J. Wysocki
2011-06-11 20:25   ` Rafael J. Wysocki
2011-06-11 20:25 ` Rafael J. Wysocki
2011-06-11 20:26 ` [PATCH 2/8] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain Rafael J. Wysocki
2011-06-11 20:26   ` Rafael J. Wysocki
2011-06-20 23:37   ` Kevin Hilman
2011-06-20 23:37   ` Kevin Hilman
2011-06-20 23:37     ` Kevin Hilman
2011-06-11 20:26 ` Rafael J. Wysocki
2011-06-11 20:27 ` [PATCH 3/8] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME Rafael J. Wysocki
2011-06-11 20:27   ` Rafael J. Wysocki
2011-06-11 20:27 ` Rafael J. Wysocki
2011-06-11 20:31 ` [PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v5) Rafael J. Wysocki
2011-06-11 20:31 ` Rafael J. Wysocki
2011-06-11 20:31   ` Rafael J. Wysocki
2011-06-19 22:02   ` [Update][PATCH 4/8] PM / Domains: Support for generic I/O PM domains (v6) Rafael J. Wysocki
2011-06-19 22:02   ` Rafael J. Wysocki
2011-06-19 22:02     ` Rafael J. Wysocki
2011-06-21 17:42     ` Kevin Hilman
2011-06-21 17:42     ` Kevin Hilman
2011-06-21 17:42       ` Kevin Hilman
2011-06-22  0:07       ` Rafael J. Wysocki
2011-06-22  0:07         ` Rafael J. Wysocki
2011-06-22 19:51         ` Kevin Hilman
2011-06-22 19:51         ` Kevin Hilman
2011-06-22 19:51           ` Kevin Hilman
2011-06-22 21:30           ` Rafael J. Wysocki
2011-06-22 21:30           ` Rafael J. Wysocki
2011-06-22 21:30             ` Rafael J. Wysocki
2011-06-22  0:07       ` Rafael J. Wysocki
2011-06-11 20:36 ` [PATCH 5/8] PM: Introduce generic "noirq" callback routines for subsystems Rafael J. Wysocki
2011-06-11 20:36   ` Rafael J. Wysocki
2011-06-11 20:36 ` Rafael J. Wysocki
2011-06-11 20:37 ` [PATCH 6/8] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME Rafael J. Wysocki
2011-06-11 20:37 ` Rafael J. Wysocki
2011-06-11 20:37   ` Rafael J. Wysocki
2011-06-11 20:39 ` [PATCH 7/8] PM / Domains: System-wide transitions support for generic PM domains Rafael J. Wysocki
2011-06-11 20:39   ` Rafael J. Wysocki
2011-06-11 23:28   ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v2) Rafael J. Wysocki
2011-06-11 23:28   ` Rafael J. Wysocki
2011-06-11 23:28     ` Rafael J. Wysocki
2011-06-19 22:06   ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Rafael J. Wysocki
2011-06-19 22:06     ` Rafael J. Wysocki
2011-06-20 23:05     ` Rafael J. Wysocki
2011-06-20 23:05       ` Rafael J. Wysocki
2011-06-20 23:05     ` Rafael J. Wysocki
2011-06-22 21:50     ` Kevin Hilman
2011-06-22 21:50     ` Kevin Hilman
2011-06-22 21:50       ` Kevin Hilman
2011-06-22 22:16       ` Rafael J. Wysocki
2011-06-22 22:16       ` Rafael J. Wysocki
2011-06-22 22:16         ` Rafael J. Wysocki
2011-06-22 22:18         ` Kevin Hilman
2011-06-22 22:18           ` Kevin Hilman
2011-06-22 22:22           ` Rafael J. Wysocki
2011-06-22 22:22             ` Rafael J. Wysocki
2011-06-23 13:57             ` [PATCH] PM / Runtime: Update documentation of interactions with system sleep Rafael J. Wysocki
2011-06-23 13:57               ` Rafael J. Wysocki
2011-06-24 18:25               ` Kevin Hilman
2011-06-24 18:25               ` Kevin Hilman
2011-06-24 18:25                 ` Kevin Hilman
2011-06-23 13:57             ` Rafael J. Wysocki
2011-06-22 22:22           ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Rafael J. Wysocki
2011-06-22 22:18         ` Kevin Hilman
2011-06-23 14:19         ` Alan Stern
2011-06-23 14:19         ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support Alan Stern
2011-06-23 14:19           ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
2011-06-23 14:44           ` Rafael J. Wysocki
2011-06-23 14:44           ` Rafael J. Wysocki
2011-06-23 14:44             ` Rafael J. Wysocki
2011-06-23 15:11             ` Alan Stern
2011-06-23 15:11             ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support Alan Stern
2011-06-23 15:11               ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
2011-06-23 17:41               ` Rafael J. Wysocki
2011-06-23 17:41               ` Rafael J. Wysocki
2011-06-23 17:41                 ` Rafael J. Wysocki
2011-06-23 18:22                 ` Alan Stern
2011-06-23 18:22                 ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support Alan Stern
2011-06-23 18:22                   ` [Update][PATCH 7/8] PM / Domains: System-wide transitions support for generic domains (v3) Alan Stern
2011-06-23 21:03                   ` Rafael J. Wysocki
2011-06-23 21:03                   ` Rafael J. Wysocki
2011-06-23 21:03                     ` Rafael J. Wysocki
2011-06-19 22:06   ` Rafael J. Wysocki
2011-06-11 20:39 ` [PATCH 7/8] PM / Domains: System-wide transitions support for generic PM domains Rafael J. Wysocki
2011-06-11 20:40 ` [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5) Rafael J. Wysocki
2011-06-11 20:40 ` Rafael J. Wysocki
2011-06-11 20:40   ` Rafael J. Wysocki
2011-06-14 13:12   ` Magnus Damm
2011-06-14 13:12     ` Magnus Damm
2011-06-14 21:16     ` Rafael J. Wysocki
2011-06-14 21:16     ` Rafael J. Wysocki
2011-06-14 21:16       ` Rafael J. Wysocki
2011-06-15 14:17       ` Magnus Damm
2011-06-15 14:17         ` Magnus Damm
2011-06-15 23:06         ` Rafael J. Wysocki
2011-06-15 23:06           ` Rafael J. Wysocki
2011-06-19 22:07           ` [Update][PATCH 8/8] ARM / shmobile: Support for I/O power domains for SH7372 (v6) Rafael J. Wysocki
2011-06-19 22:07             ` Rafael J. Wysocki
2011-06-20  2:01             ` Paul Mundt
2011-06-20  2:01               ` Paul Mundt
2011-06-20 22:30               ` Rafael J. Wysocki
2011-06-20 22:30                 ` Rafael J. Wysocki
2011-06-21 11:57                 ` Rafael J. Wysocki
2011-06-21 11:57                   ` Rafael J. Wysocki
2011-06-21 12:47                   ` Paul Mundt
2011-06-21 12:47                   ` Paul Mundt
2011-06-21 12:47                     ` Paul Mundt
2011-06-21 11:57                 ` Rafael J. Wysocki
2011-06-20 22:30               ` Rafael J. Wysocki
2011-06-20  2:01             ` Paul Mundt
2011-06-19 22:07           ` Rafael J. Wysocki
2011-06-15 23:06         ` [PATCH 8/8] ARM / shmobile: Support for I/O PM domains for SH7372 (v5) Rafael J. Wysocki
2011-07-10 11:45         ` Laurent Pinchart
2011-07-10 11:45         ` Laurent Pinchart
2011-07-10 11:45           ` Laurent Pinchart
2011-06-15 14:17       ` Magnus Damm
2011-06-14 13:12   ` Magnus Damm
2011-06-11 20:57 ` [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5) Greg KH
2011-06-11 20:57 ` Greg KH
2011-06-11 20:57   ` Greg KH
2011-06-21  0:02 ` Kevin Hilman
2011-06-21  0:02 ` Kevin Hilman
2011-06-21  0:02   ` Kevin Hilman
2011-06-21 11:06   ` Rafael J. Wysocki
2011-06-21 11:06   ` Rafael J. Wysocki
2011-06-21 11:06     ` Rafael J. Wysocki
2011-06-21 14:47     ` Kevin Hilman
2011-06-21 14:47     ` [PATCH 0/8] PM / Domains: Support for generic I/O PM domains Kevin Hilman
2011-06-21 14:47       ` [PATCH 0/8] PM / Domains: Support for generic I/O PM domains (v5) Kevin Hilman
2011-06-25 21:24 ` [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains Rafael J. Wysocki
2011-06-25 21:24   ` Rafael J. Wysocki
2011-06-25 21:24   ` [PATCH 1/10 v6] PM / Domains: Rename struct dev_power_domain to struct dev_pm_domain Rafael J. Wysocki
2011-06-25 21:24   ` Rafael J. Wysocki
2011-06-25 21:24     ` Rafael J. Wysocki
2011-06-25 21:25   ` [PATCH 2/10 v6] PM: subsys_data in struct dev_pm_info need not depend on RM_RUNTIME Rafael J. Wysocki
2011-06-25 21:25   ` Rafael J. Wysocki
2011-06-25 21:25     ` Rafael J. Wysocki
2011-06-25 21:26   ` [PATCH 3/10 v6] PM / Domains: Support for generic I/O PM domains (v7) Rafael J. Wysocki
2011-06-25 21:26   ` Rafael J. Wysocki
2011-06-25 21:26     ` Rafael J. Wysocki
2011-06-30  6:14     ` Ming Lei
2011-06-30  6:14       ` Ming Lei
2011-06-30 18:58       ` Rafael J. Wysocki
2011-06-30 18:58       ` Rafael J. Wysocki
2011-06-30 18:58         ` Rafael J. Wysocki
2011-06-30  6:14     ` Ming Lei
2011-07-01 18:11     ` Kevin Hilman
2011-07-01 18:11       ` Kevin Hilman
2011-07-01 20:03       ` Rafael J. Wysocki
2011-07-01 20:03         ` Rafael J. Wysocki
2011-07-01 20:03       ` Rafael J. Wysocki
2011-07-01 18:11     ` Kevin Hilman
2011-06-25 21:27   ` [PATCH 4/10 v6] PM: Introduce generic "noirq" callback routines for subsystems (v2) Rafael J. Wysocki
2011-06-25 21:27   ` Rafael J. Wysocki
2011-06-25 21:27     ` Rafael J. Wysocki
2011-06-25 21:27   ` [PATCH 5/10 v6] PM / Domains: Move code from under #ifdef CONFIG_PM_RUNTIME (v2) Rafael J. Wysocki
2011-06-25 21:27     ` Rafael J. Wysocki
2011-06-25 21:27     ` Rafael J. Wysocki
2011-06-25 21:28   ` [PATCH 6/10 v6] PM / Domains: System-wide transitions support for generic domains (v4) Rafael J. Wysocki
2011-06-25 21:28     ` Rafael J. Wysocki
2011-06-28 23:44     ` [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5) Rafael J. Wysocki
2011-06-28 23:44     ` Rafael J. Wysocki
2011-06-28 23:44       ` Rafael J. Wysocki
2011-07-08  0:29       ` Kevin Hilman
2011-07-08  0:29         ` Kevin Hilman
2011-07-08  9:24         ` Rafael J. Wysocki
2011-07-08  9:24         ` Rafael J. Wysocki
2011-07-08  9:24           ` Rafael J. Wysocki
2011-07-08 14:37           ` [Update][PATCH 6/10] PM / Domains: System-wide transitions Alan Stern
2011-07-08 14:37             ` [Update][PATCH 6/10] PM / Domains: System-wide transitions support for generic domains (v5) Alan Stern
2011-07-08 17:20             ` Kevin Hilman
2011-07-08 17:20             ` Kevin Hilman
2011-07-08 17:20               ` Kevin Hilman
2011-07-08 18:06               ` Rafael J. Wysocki
2011-07-08 18:06                 ` Rafael J. Wysocki
2011-07-08 19:24                 ` Rafael J. Wysocki
2011-07-08 19:24                 ` Rafael J. Wysocki
2011-07-08 19:24                   ` Rafael J. Wysocki
2011-07-09 14:15                   ` Rafael J. Wysocki
2011-07-09 14:15                   ` Rafael J. Wysocki
2011-07-09 14:15                     ` Rafael J. Wysocki
2011-07-11 15:37                     ` Kevin Hilman
2011-07-11 15:37                     ` Kevin Hilman
2011-07-11 15:37                       ` Kevin Hilman
2011-07-11 19:39                       ` Rafael J. Wysocki
2011-07-11 19:39                         ` Rafael J. Wysocki
2011-07-11 19:39                       ` Rafael J. Wysocki
2011-07-08 18:06               ` Rafael J. Wysocki
2011-07-08 17:56             ` Rafael J. Wysocki
2011-07-08 17:56             ` Rafael J. Wysocki
2011-07-08 17:56               ` Rafael J. Wysocki
2011-07-08 14:37           ` Alan Stern
2011-07-08  0:29       ` Kevin Hilman
2011-06-25 21:28   ` [PATCH 6/10 v6] PM / Domains: System-wide transitions support for generic domains (v4) Rafael J. Wysocki
2011-06-25 21:29   ` [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions Rafael J. Wysocki
2011-06-25 21:29   ` Rafael J. Wysocki
2011-06-25 21:29     ` Rafael J. Wysocki
2011-06-29 23:50     ` Kevin Hilman
2011-06-29 23:50       ` Kevin Hilman
2011-06-30 19:37       ` Rafael J. Wysocki
2011-06-30 19:37       ` Rafael J. Wysocki
2011-06-30 19:37         ` Rafael J. Wysocki
2011-06-30 22:42         ` Kevin Hilman
2011-06-30 22:42         ` Kevin Hilman
2011-06-30 22:42           ` Kevin Hilman
2011-06-30 22:55           ` Rafael J. Wysocki
2011-06-30 22:55           ` Rafael J. Wysocki
2011-06-30 22:55             ` Rafael J. Wysocki
2011-06-30 23:14             ` Kevin Hilman
2011-06-30 23:14             ` Kevin Hilman
2011-06-30 23:14               ` Kevin Hilman
2011-06-30 23:28               ` Rafael J. Wysocki
2011-06-30 23:28               ` Rafael J. Wysocki
2011-06-30 23:28                 ` Rafael J. Wysocki
2011-07-01  0:01                 ` Kevin Hilman
2011-07-01  0:01                   ` Kevin Hilman
2011-07-01  0:01                   ` Kevin Hilman
2011-07-01  0:24                   ` Rafael J. Wysocki
2011-07-01  0:24                   ` Rafael J. Wysocki
2011-07-01  0:24                     ` Rafael J. Wysocki
2011-07-01 14:34                     ` Kevin Hilman
2011-07-01 14:34                       ` Kevin Hilman
2011-07-01 14:34                     ` Kevin Hilman
2011-06-30 23:25             ` Rafael J. Wysocki
2011-06-30 23:25             ` Rafael J. Wysocki
2011-06-30 23:25               ` Rafael J. Wysocki
2011-07-01 14:45               ` [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during Alan Stern
2011-07-01 14:45                 ` [PATCH 7/10 v6] PM / Domains: Don't stop wakeup devices during system sleep transitions Alan Stern
2011-07-01 20:06                 ` Rafael J. Wysocki
2011-07-01 20:06                   ` Rafael J. Wysocki
2011-07-01 20:06                 ` Rafael J. Wysocki
2011-07-01 14:45               ` Alan Stern
2011-06-29 23:50     ` Kevin Hilman
2011-06-25 21:30   ` [PATCH 8/10 v6] PM: Allow the clocks management code to be used during system suspend Rafael J. Wysocki
2011-06-25 21:30     ` Rafael J. Wysocki
2011-06-25 21:30   ` Rafael J. Wysocki
2011-06-25 21:30   ` [PATCH 9/10 v6] PM: Rename clock management functions Rafael J. Wysocki
2011-06-25 21:30   ` Rafael J. Wysocki
2011-06-25 21:30     ` Rafael J. Wysocki
2011-06-25 21:31   ` [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8) Rafael J. Wysocki
2011-06-25 21:31     ` Rafael J. Wysocki
2011-06-27  4:07     ` [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains Magnus Damm
2011-06-27  4:07       ` [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8) Magnus Damm
2011-06-27  4:07       ` Magnus Damm
2011-06-27 19:25       ` Rafael J. Wysocki
2011-06-27 19:25       ` Rafael J. Wysocki
2011-06-27 19:25         ` Rafael J. Wysocki
2011-06-27 23:21         ` [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains Magnus Damm
2011-06-27 23:21           ` [PATCH 10/10 v6] ARM / shmobile: Support for I/O power domains for SH7372 (v8) Magnus Damm
2011-06-27 23:21           ` Magnus Damm
2011-06-28 10:08           ` Rafael J. Wysocki
2011-06-28 10:08             ` Rafael J. Wysocki
2011-06-28 10:08           ` Rafael J. Wysocki
2011-06-25 21:31   ` Rafael J. Wysocki
2011-07-01 18:27   ` [PATCH 0/10 v6] PM / Domains: Support for generic I/O PM domains Kevin Hilman
2011-07-01 18:27   ` Kevin Hilman
2011-07-01 18:27     ` Kevin Hilman
2011-06-25 21:24 ` Rafael J. Wysocki

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.