All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 0/4] Add runtime PM support for clocks (on Exynos SoC example)
       [not found] <CGME20170322113553eucas1p20875eb2da524fd090c6a7cc725c9d944@eucas1p2.samsung.com>
  2017-03-22 11:35   ` Marek Szyprowski
@ 2017-03-22 11:35   ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Marek Szyprowski, Stephen Boyd, Michael Turquette, Ulf Hansson,
	Sylwester Nawrocki, Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

Dear All,

This patchset adds runtime PM support to common clock framework. This is an
attempt to implement support for clock controllers, which belongs to a power
domain. This approach works surprisingly well on Exynos 5433 SoC, what allowed
us to solve various freeze/crash issues related to power management.

The main idea behind this patchset is to keep clock's controller power domain
enabled every time when at least one of its clock is enabled or access to its
registers is being made. Clock controller driver (clock provider) can
supply a struct device pointer, which is the used by clock core for tracking and
managing clock's controller runtime pm state. Each clk_prepare() operation will
first call pm_runtime_get_sync() on the supplied device, while clk_unprepare()
will do pm_runtime_put() at the end.

This runtime PM feature has been tested with Exynos4412 (not included in this
patchset) and Exynos5433 clocks drivers. Both have some clocks, which belongs to
respective power domains and need special handling during power on/off
procedures. Till now it wasn't handled at all, what caused various problems.

Patches for clocks drivers change the way the clock provider is initialized.
Instead of CLK_OF_DECLARE based initialization, a complete platform device driver
infrastructure is being used. This is needed to let driver to use runtime PM
feature and integrate with generic power domains. The side-effect of this change
is a delay in clock provider registeration during system boot, so early
initialized drivers might get EPROBEDEFER error when requesting their clocks.
This is an issue for IOMMU drivers, so this patchset will be fully functional
once the deferred probe for IOMMU will be merged. v9 of the IOMMU deferred probe
support has been posted a few days ago:
https://www.spinics.net/lists/arm-kernel/msg567558.html

Patches are based on v4.11-rc3.

Stephen: this patchset finally got a review from Ulf (from PM point of
view). Is also touches only the Exynos5433 and Exynos Audio Subsystem
drivers, which both have separate DT nodes for each clock controllers and
each clock controller is entirely only in the one power domain, so everything is
clean from the current DT bindings perspective. Would it be okay to merge it?

Best regards
Marek Szyprowski
Samsung R&D Institute Poland


Changelog:

v6:
- addressed comments from Ulf Hanson and added his 'Reviewed-by' tags
- simplified exynos 5433 clock driver code a bit
- fixes issues pointed by kbuild test robot

v5:
- rebased onto next-20170125 kernel release
- added two more calls to runtime PM in clock's core:
	1. clk_recalc() should ensure active runtime pm state, because it might
	   read registers (in case of dividers) to calculate the rate
	2. clk_register() also needs to ensure active runtime pm state to read
	   initial rate and status of the clocks
- more fixes in Exynos5433 clocks drivers:
	1. PLL in DISP CMU must be enabled for suspend
	2. guard clocks registration with pm_runtime_get_sync/pm_runtime_put to
	   avoid turning the power domain on and off many times for no good
	   reason (registering each clock will call pm_runtime_get_sync(), then
	   pm_runtime_put, what in turn might disable power domain)
- still waiting for a review...

v4: http://www.spinics.net/lists/arm-kernel/msg550747.html
- Removed patch for Exynos4412 clocks from the patchset. DT bindings for power
  domain for ISP sub-controller needs more discussion. It will be handled
  separately when the runtime PM for clocks feature gets merged.
- Added patch with runtime PM support for Exynos AudioSS clock controller driver
  (needed to enable audio power domain on Exynos5 series).
- Fixes in Exynos5433 driver:
	1. added missing clock for Audio CMU
	2. added support for FSYS CMU
	3. improved support for DISP CMU (thanks to Andrzej Hajda for
	   investigating that).
- Rebased onto v4.10-rc1
- Waiting for a review...

v3: http://www.spinics.net/lists/arm-kernel/msg538122.html
- Removed CLK_RUNTIME_PM flag, core now simply checks if runtime pm is enabled
  for the provided device during clock registration as suggested by Ulf
- Simplified code for exynos4412 isp clock driver registration
- Resolved some other minor issues pointed by Ulf clk core code
- Rebased onto v4.9-rc1 and new version of IOMMU deferred probe patchset

v2: https://www.spinics.net/lists/arm-kernel/msg532798.html
- Simplified clk_pm_runtime_get/put functions, removed workaround for devices
  with disabled runtime pm. Such workaround is no longer needed since commit
  4d23a5e84806b202d9231929c9507ef7cf7a0185 ("PM / Domains: Allow runtime PM
  during system PM phases").
- Added CLK_RUNTIME_PM flag to indicate clocks, for which clock core should
  call runtime pm functions. This solves problem with clocks, for which struct
  device is already registered, but no runtime pm is enabled.
- Extended commit messages according to Ulf suggestions.
- Fixed some style issues pointed by Barlomiej.

v1: http://www.spinics.net/lists/arm-kernel/msg528128.html
- initial version


Patch summary:

Marek Szyprowski (4):
  clk: Add support for runtime PM
  clk: samsung: Add support for runtime PM
  clk: samsung: exynos5433: Add runtime PM support
  clk: samsung: exynos-audss: Use runtime PM

 .../devicetree/bindings/clock/clk-exynos-audss.txt |   6 +
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/clk.c                                  | 129 ++++++-
 drivers/clk/samsung/clk-exynos-audss.c             |  68 ++--
 drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   2 +-
 drivers/clk/samsung/clk.c                          |  12 +-
 drivers/clk/samsung/clk.h                          |   7 +
 8 files changed, 517 insertions(+), 136 deletions(-)

-- 
1.9.1

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

* [PATCH v6 0/4] Add runtime PM support for clocks (on Exynos SoC example)
@ 2017-03-22 11:35   ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Ulf Hansson, Bartlomiej Zolnierkiewicz, Michael Turquette,
	Stephen Boyd, Krzysztof Kozlowski, Inki Dae, Chanwoo Choi,
	Sylwester Nawrocki, Marek Szyprowski

Dear All,

This patchset adds runtime PM support to common clock framework. This is an
attempt to implement support for clock controllers, which belongs to a power
domain. This approach works surprisingly well on Exynos 5433 SoC, what allowed
us to solve various freeze/crash issues related to power management.

The main idea behind this patchset is to keep clock's controller power domain
enabled every time when at least one of its clock is enabled or access to its
registers is being made. Clock controller driver (clock provider) can
supply a struct device pointer, which is the used by clock core for tracking and
managing clock's controller runtime pm state. Each clk_prepare() operation will
first call pm_runtime_get_sync() on the supplied device, while clk_unprepare()
will do pm_runtime_put() at the end.

This runtime PM feature has been tested with Exynos4412 (not included in this
patchset) and Exynos5433 clocks drivers. Both have some clocks, which belongs to
respective power domains and need special handling during power on/off
procedures. Till now it wasn't handled at all, what caused various problems.

Patches for clocks drivers change the way the clock provider is initialized.
Instead of CLK_OF_DECLARE based initialization, a complete platform device driver
infrastructure is being used. This is needed to let driver to use runtime PM
feature and integrate with generic power domains. The side-effect of this change
is a delay in clock provider registeration during system boot, so early
initialized drivers might get EPROBEDEFER error when requesting their clocks.
This is an issue for IOMMU drivers, so this patchset will be fully functional
once the deferred probe for IOMMU will be merged. v9 of the IOMMU deferred probe
support has been posted a few days ago:
https://www.spinics.net/lists/arm-kernel/msg567558.html

Patches are based on v4.11-rc3.

Stephen: this patchset finally got a review from Ulf (from PM point of
view). Is also touches only the Exynos5433 and Exynos Audio Subsystem
drivers, which both have separate DT nodes for each clock controllers and
each clock controller is entirely only in the one power domain, so everything is
clean from the current DT bindings perspective. Would it be okay to merge it?

Best regards
Marek Szyprowski
Samsung R&D Institute Poland


Changelog:

v6:
- addressed comments from Ulf Hanson and added his 'Reviewed-by' tags
- simplified exynos 5433 clock driver code a bit
- fixes issues pointed by kbuild test robot

v5:
- rebased onto next-20170125 kernel release
- added two more calls to runtime PM in clock's core:
	1. clk_recalc() should ensure active runtime pm state, because it might
	   read registers (in case of dividers) to calculate the rate
	2. clk_register() also needs to ensure active runtime pm state to read
	   initial rate and status of the clocks
- more fixes in Exynos5433 clocks drivers:
	1. PLL in DISP CMU must be enabled for suspend
	2. guard clocks registration with pm_runtime_get_sync/pm_runtime_put to
	   avoid turning the power domain on and off many times for no good
	   reason (registering each clock will call pm_runtime_get_sync(), then
	   pm_runtime_put, what in turn might disable power domain)
- still waiting for a review...

v4: http://www.spinics.net/lists/arm-kernel/msg550747.html
- Removed patch for Exynos4412 clocks from the patchset. DT bindings for power
  domain for ISP sub-controller needs more discussion. It will be handled
  separately when the runtime PM for clocks feature gets merged.
- Added patch with runtime PM support for Exynos AudioSS clock controller driver
  (needed to enable audio power domain on Exynos5 series).
- Fixes in Exynos5433 driver:
	1. added missing clock for Audio CMU
	2. added support for FSYS CMU
	3. improved support for DISP CMU (thanks to Andrzej Hajda for
	   investigating that).
- Rebased onto v4.10-rc1
- Waiting for a review...

v3: http://www.spinics.net/lists/arm-kernel/msg538122.html
- Removed CLK_RUNTIME_PM flag, core now simply checks if runtime pm is enabled
  for the provided device during clock registration as suggested by Ulf
- Simplified code for exynos4412 isp clock driver registration
- Resolved some other minor issues pointed by Ulf clk core code
- Rebased onto v4.9-rc1 and new version of IOMMU deferred probe patchset

v2: https://www.spinics.net/lists/arm-kernel/msg532798.html
- Simplified clk_pm_runtime_get/put functions, removed workaround for devices
  with disabled runtime pm. Such workaround is no longer needed since commit
  4d23a5e84806b202d9231929c9507ef7cf7a0185 ("PM / Domains: Allow runtime PM
  during system PM phases").
- Added CLK_RUNTIME_PM flag to indicate clocks, for which clock core should
  call runtime pm functions. This solves problem with clocks, for which struct
  device is already registered, but no runtime pm is enabled.
- Extended commit messages according to Ulf suggestions.
- Fixed some style issues pointed by Barlomiej.

v1: http://www.spinics.net/lists/arm-kernel/msg528128.html
- initial version


Patch summary:

Marek Szyprowski (4):
  clk: Add support for runtime PM
  clk: samsung: Add support for runtime PM
  clk: samsung: exynos5433: Add runtime PM support
  clk: samsung: exynos-audss: Use runtime PM

 .../devicetree/bindings/clock/clk-exynos-audss.txt |   6 +
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/clk.c                                  | 129 ++++++-
 drivers/clk/samsung/clk-exynos-audss.c             |  68 ++--
 drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   2 +-
 drivers/clk/samsung/clk.c                          |  12 +-
 drivers/clk/samsung/clk.h                          |   7 +
 8 files changed, 517 insertions(+), 136 deletions(-)

-- 
1.9.1

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

* [PATCH v6 0/4] Add runtime PM support for clocks (on Exynos SoC example)
@ 2017-03-22 11:35   ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-arm-kernel

Dear All,

This patchset adds runtime PM support to common clock framework. This is an
attempt to implement support for clock controllers, which belongs to a power
domain. This approach works surprisingly well on Exynos 5433 SoC, what allowed
us to solve various freeze/crash issues related to power management.

The main idea behind this patchset is to keep clock's controller power domain
enabled every time when at least one of its clock is enabled or access to its
registers is being made. Clock controller driver (clock provider) can
supply a struct device pointer, which is the used by clock core for tracking and
managing clock's controller runtime pm state. Each clk_prepare() operation will
first call pm_runtime_get_sync() on the supplied device, while clk_unprepare()
will do pm_runtime_put() at the end.

This runtime PM feature has been tested with Exynos4412 (not included in this
patchset) and Exynos5433 clocks drivers. Both have some clocks, which belongs to
respective power domains and need special handling during power on/off
procedures. Till now it wasn't handled at all, what caused various problems.

Patches for clocks drivers change the way the clock provider is initialized.
Instead of CLK_OF_DECLARE based initialization, a complete platform device driver
infrastructure is being used. This is needed to let driver to use runtime PM
feature and integrate with generic power domains. The side-effect of this change
is a delay in clock provider registeration during system boot, so early
initialized drivers might get EPROBEDEFER error when requesting their clocks.
This is an issue for IOMMU drivers, so this patchset will be fully functional
once the deferred probe for IOMMU will be merged. v9 of the IOMMU deferred probe
support has been posted a few days ago:
https://www.spinics.net/lists/arm-kernel/msg567558.html

Patches are based on v4.11-rc3.

Stephen: this patchset finally got a review from Ulf (from PM point of
view). Is also touches only the Exynos5433 and Exynos Audio Subsystem
drivers, which both have separate DT nodes for each clock controllers and
each clock controller is entirely only in the one power domain, so everything is
clean from the current DT bindings perspective. Would it be okay to merge it?

Best regards
Marek Szyprowski
Samsung R&D Institute Poland


Changelog:

v6:
- addressed comments from Ulf Hanson and added his 'Reviewed-by' tags
- simplified exynos 5433 clock driver code a bit
- fixes issues pointed by kbuild test robot

v5:
- rebased onto next-20170125 kernel release
- added two more calls to runtime PM in clock's core:
	1. clk_recalc() should ensure active runtime pm state, because it might
	   read registers (in case of dividers) to calculate the rate
	2. clk_register() also needs to ensure active runtime pm state to read
	   initial rate and status of the clocks
- more fixes in Exynos5433 clocks drivers:
	1. PLL in DISP CMU must be enabled for suspend
	2. guard clocks registration with pm_runtime_get_sync/pm_runtime_put to
	   avoid turning the power domain on and off many times for no good
	   reason (registering each clock will call pm_runtime_get_sync(), then
	   pm_runtime_put, what in turn might disable power domain)
- still waiting for a review...

v4: http://www.spinics.net/lists/arm-kernel/msg550747.html
- Removed patch for Exynos4412 clocks from the patchset. DT bindings for power
  domain for ISP sub-controller needs more discussion. It will be handled
  separately when the runtime PM for clocks feature gets merged.
- Added patch with runtime PM support for Exynos AudioSS clock controller driver
  (needed to enable audio power domain on Exynos5 series).
- Fixes in Exynos5433 driver:
	1. added missing clock for Audio CMU
	2. added support for FSYS CMU
	3. improved support for DISP CMU (thanks to Andrzej Hajda for
	   investigating that).
- Rebased onto v4.10-rc1
- Waiting for a review...

v3: http://www.spinics.net/lists/arm-kernel/msg538122.html
- Removed CLK_RUNTIME_PM flag, core now simply checks if runtime pm is enabled
  for the provided device during clock registration as suggested by Ulf
- Simplified code for exynos4412 isp clock driver registration
- Resolved some other minor issues pointed by Ulf clk core code
- Rebased onto v4.9-rc1 and new version of IOMMU deferred probe patchset

v2: https://www.spinics.net/lists/arm-kernel/msg532798.html
- Simplified clk_pm_runtime_get/put functions, removed workaround for devices
  with disabled runtime pm. Such workaround is no longer needed since commit
  4d23a5e84806b202d9231929c9507ef7cf7a0185 ("PM / Domains: Allow runtime PM
  during system PM phases").
- Added CLK_RUNTIME_PM flag to indicate clocks, for which clock core should
  call runtime pm functions. This solves problem with clocks, for which struct
  device is already registered, but no runtime pm is enabled.
- Extended commit messages according to Ulf suggestions.
- Fixed some style issues pointed by Barlomiej.

v1: http://www.spinics.net/lists/arm-kernel/msg528128.html
- initial version


Patch summary:

Marek Szyprowski (4):
  clk: Add support for runtime PM
  clk: samsung: Add support for runtime PM
  clk: samsung: exynos5433: Add runtime PM support
  clk: samsung: exynos-audss: Use runtime PM

 .../devicetree/bindings/clock/clk-exynos-audss.txt |   6 +
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/clk.c                                  | 129 ++++++-
 drivers/clk/samsung/clk-exynos-audss.c             |  68 ++--
 drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   2 +-
 drivers/clk/samsung/clk.c                          |  12 +-
 drivers/clk/samsung/clk.h                          |   7 +
 8 files changed, 517 insertions(+), 136 deletions(-)

-- 
1.9.1

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

* [PATCH v6 1/4] clk: Add support for runtime PM
       [not found]   ` <CGME20170322113554eucas1p27471f0de278c55d5dce7fdf120395e09@eucas1p2.samsung.com>
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Marek Szyprowski, Stephen Boyd, Michael Turquette, Ulf Hansson,
	Sylwester Nawrocki, Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

Registers for some clocks might be located in the SOC area, which are under the
power domain. To enable access to those registers respective domain has to be
turned on. Additionally, registers for such clocks will usually loose its
contents when power domain is turned off, so additional saving and restoring of
them might be needed in the clock controller driver.

This patch adds basic infrastructure in the clocks core to allow implementing
driver for such clocks under power domains. Clock provider can supply a
struct device pointer, which is the used by clock core for tracking and managing
clock's controller runtime pm state. Each clk_prepare() operation
will first call pm_runtime_get_sync() on the supplied device, while
clk_unprepare() will do pm_runtime_put_sync() at the end.

Additional calls to pm_runtime_get/put functions are required to ensure that any
register access (like calculating/changing clock rates and unpreparing/disabling
unused clocks on boot) will be done with clock controller in runtime resumend
state.

When one wants to register clock controller, which make use of this feature, he
has to:
1. Provide a struct device to the core when registering the provider.
2. Ensure to enable runtime PM for that device before registering clocks.
3. Make sure that the runtime PM status of the controller device reflects
   the HW state.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 114 insertions(+), 15 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0fb39fe217d1..a6001b6e49a7 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
 	const struct clk_ops	*ops;
 	struct clk_hw		*hw;
 	struct module		*owner;
+	struct device		*dev;
 	struct clk_core		*parent;
 	const char		**parent_names;
 	struct clk_core		**parents;
@@ -87,6 +89,26 @@ struct clk {
 	struct hlist_node clks_node;
 };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+	int ret = 0;
+
+	if (!core->dev)
+		return 0;
+
+	ret = pm_runtime_get_sync(core->dev);
+	return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+	if (!core->dev)
+		return;
+
+	pm_runtime_put_sync(core->dev);
+}
+
 /***           locking             ***/
 static void clk_prepare_lock(void)
 {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static bool clk_core_is_prepared(struct clk_core *core)
 {
+	bool ret = false;
+
 	/*
 	 * .is_prepared is optional for clocks that can prepare
 	 * fall back to software usage counter if it is missing
@@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core)
 	if (!core->ops->is_prepared)
 		return core->prepare_count;
 
-	return core->ops->is_prepared(core->hw);
+	if (!clk_pm_runtime_get(core)) {
+		ret = core->ops->is_prepared(core->hw);
+		clk_pm_runtime_put(core);
+	}
+
+	return ret;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+	bool ret = false;
+
 	/*
 	 * .is_enabled is only mandatory for clocks that gate
 	 * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core)
 	if (!core->ops->is_enabled)
 		return core->enable_count;
 
-	return core->ops->is_enabled(core->hw);
+	/*
+	 * Check if clock controller's device is runtime active before
+	 * calling .is_enabled callback. If not, assume that clock is
+	 * disabled, because we might be called from atomic context, from
+	 * which pm_runtime_get() is not allowed.
+	 * This function is called mainly from clk_disable_unused_subtree,
+	 * which ensures proper runtime pm activation of controller before
+	 * taking enable spinlock, but the below check is needed if one tries
+	 * to call it from other places.
+	 */
+	if (core->dev) {
+		pm_runtime_get_noresume(core->dev);
+		if (!pm_runtime_active(core->dev)) {
+			ret = false;
+			goto done;
+		}
+	}
+
+	ret = core->ops->is_enabled(core->hw);
+done:
+	clk_pm_runtime_put(core);
+
+	return ret;
 }
 
 /***    helper functions   ***/
@@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
 	if (core->ops->unprepare)
 		core->ops->unprepare(core->hw);
 
+	clk_pm_runtime_put(core);
+
 	trace_clk_unprepare_complete(core);
 	clk_core_unprepare(core->parent);
 }
@@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
 		return 0;
 
 	if (core->prepare_count == 0) {
-		ret = clk_core_prepare(core->parent);
+		ret = clk_pm_runtime_get(core);
 		if (ret)
 			return ret;
 
+		ret = clk_core_prepare(core->parent);
+		if (ret)
+			goto runtime_put;
+
 		trace_clk_prepare(core);
 
 		if (core->ops->prepare)
@@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
 
 		trace_clk_prepare_complete(core);
 
-		if (ret) {
-			clk_core_unprepare(core->parent);
-			return ret;
-		}
+		if (ret)
+			goto unprepare;
 	}
 
 	core->prepare_count++;
 
 	return 0;
+unprepare:
+	clk_core_unprepare(core->parent);
+runtime_put:
+	clk_pm_runtime_put(core);
+	return ret;
 }
 
 static int clk_core_prepare_lock(struct clk_core *core)
@@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_IGNORE_UNUSED)
 		return;
 
+	if (clk_pm_runtime_get(core))
+		return;
+
 	if (clk_core_is_prepared(core)) {
 		trace_clk_unprepare(core);
 		if (core->ops->unprepare_unused)
@@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 			core->ops->unprepare(core->hw);
 		trace_clk_unprepare_complete(core);
 	}
+
+	clk_pm_runtime_put(core);
 }
 
 static void clk_disable_unused_subtree(struct clk_core *core)
@@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_prepare_enable(core->parent);
 
+	if (clk_pm_runtime_get(core))
+		goto unprepare_out;
+
 	flags = clk_enable_lock();
 
 	if (core->enable_count)
@@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 
 unlock_out:
 	clk_enable_unlock(flags);
+	clk_pm_runtime_put(core);
+unprepare_out:
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_disable_unprepare(core->parent);
 }
@@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk)
 static unsigned long clk_recalc(struct clk_core *core,
 				unsigned long parent_rate)
 {
-	if (core->ops->recalc_rate)
-		return core->ops->recalc_rate(core->hw, parent_rate);
-	return parent_rate;
+	unsigned long rate = parent_rate;
+
+	if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
+		rate = core->ops->recalc_rate(core->hw, parent_rate);
+		clk_pm_runtime_put(core);
+	}
+	return rate;
 }
 
 /**
@@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 {
 	struct clk_core *top, *fail_clk;
 	unsigned long rate = req_rate;
+	int ret = 0;
 
 	if (!core)
 		return 0;
@@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (!top)
 		return -EINVAL;
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		return ret;
+
 	/* notify that we are about to change rates */
 	fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
 	if (fail_clk) {
 		pr_debug("%s: failed to set %s rate\n", __func__,
 				fail_clk->name);
 		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err;
 	}
 
 	/* change the rates */
 	clk_change_rate(top);
 
 	core->req_rate = req_rate;
+err:
+	clk_pm_runtime_put(core);
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		p_rate = parent->rate;
 	}
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		goto out;
+
 	/* propagate PRE_RATE_CHANGE notifications */
 	ret = __clk_speculate_rates(core, p_rate);
 
 	/* abort if a driver objects */
 	if (ret & NOTIFY_STOP_MASK)
-		goto out;
+		goto runtime_put;
 
 	/* do the re-parent */
 	ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		__clk_recalc_accuracies(core);
 	}
 
+runtime_put:
+	clk_pm_runtime_put(core);
 out:
 	clk_prepare_unlock();
 
@@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		goto fail_name;
 	}
 	core->ops = hw->init->ops;
+	if (dev && pm_runtime_enabled(dev)) {
+		core->dev = dev;
+		ret = clk_pm_runtime_get(core);
+		if (ret)
+			goto fail_pm;
+	}
 	if (dev && dev->driver)
 		core->owner = dev->driver->owner;
 	core->hw = hw;
@@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 	}
 
 	ret = __clk_core_init(core);
-	if (!ret)
+	if (!ret) {
+		clk_pm_runtime_put(core);
 		return hw->clk;
+	}
 
 	__clk_free_clk(hw->clk);
 	hw->clk = NULL;
-
 fail_parents:
 	kfree(core->parents);
 fail_parent_names_copy:
@@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		kfree_const(core->parent_names[i]);
 	kfree(core->parent_names);
 fail_parent_names:
+	clk_pm_runtime_put(core);
+fail_pm:
 	kfree_const(core->name);
 fail_name:
 	kfree(core);
-- 
1.9.1

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Ulf Hansson, Bartlomiej Zolnierkiewicz, Michael Turquette,
	Stephen Boyd, Krzysztof Kozlowski, Inki Dae, Chanwoo Choi,
	Sylwester Nawrocki, Marek Szyprowski

Registers for some clocks might be located in the SOC area, which are under the
power domain. To enable access to those registers respective domain has to be
turned on. Additionally, registers for such clocks will usually loose its
contents when power domain is turned off, so additional saving and restoring of
them might be needed in the clock controller driver.

This patch adds basic infrastructure in the clocks core to allow implementing
driver for such clocks under power domains. Clock provider can supply a
struct device pointer, which is the used by clock core for tracking and managing
clock's controller runtime pm state. Each clk_prepare() operation
will first call pm_runtime_get_sync() on the supplied device, while
clk_unprepare() will do pm_runtime_put_sync() at the end.

Additional calls to pm_runtime_get/put functions are required to ensure that any
register access (like calculating/changing clock rates and unpreparing/disabling
unused clocks on boot) will be done with clock controller in runtime resumend
state.

When one wants to register clock controller, which make use of this feature, he
has to:
1. Provide a struct device to the core when registering the provider.
2. Ensure to enable runtime PM for that device before registering clocks.
3. Make sure that the runtime PM status of the controller device reflects
   the HW state.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 114 insertions(+), 15 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0fb39fe217d1..a6001b6e49a7 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
 	const struct clk_ops	*ops;
 	struct clk_hw		*hw;
 	struct module		*owner;
+	struct device		*dev;
 	struct clk_core		*parent;
 	const char		**parent_names;
 	struct clk_core		**parents;
@@ -87,6 +89,26 @@ struct clk {
 	struct hlist_node clks_node;
 };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+	int ret = 0;
+
+	if (!core->dev)
+		return 0;
+
+	ret = pm_runtime_get_sync(core->dev);
+	return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+	if (!core->dev)
+		return;
+
+	pm_runtime_put_sync(core->dev);
+}
+
 /***           locking             ***/
 static void clk_prepare_lock(void)
 {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static bool clk_core_is_prepared(struct clk_core *core)
 {
+	bool ret = false;
+
 	/*
 	 * .is_prepared is optional for clocks that can prepare
 	 * fall back to software usage counter if it is missing
@@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core)
 	if (!core->ops->is_prepared)
 		return core->prepare_count;
 
-	return core->ops->is_prepared(core->hw);
+	if (!clk_pm_runtime_get(core)) {
+		ret = core->ops->is_prepared(core->hw);
+		clk_pm_runtime_put(core);
+	}
+
+	return ret;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+	bool ret = false;
+
 	/*
 	 * .is_enabled is only mandatory for clocks that gate
 	 * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core)
 	if (!core->ops->is_enabled)
 		return core->enable_count;
 
-	return core->ops->is_enabled(core->hw);
+	/*
+	 * Check if clock controller's device is runtime active before
+	 * calling .is_enabled callback. If not, assume that clock is
+	 * disabled, because we might be called from atomic context, from
+	 * which pm_runtime_get() is not allowed.
+	 * This function is called mainly from clk_disable_unused_subtree,
+	 * which ensures proper runtime pm activation of controller before
+	 * taking enable spinlock, but the below check is needed if one tries
+	 * to call it from other places.
+	 */
+	if (core->dev) {
+		pm_runtime_get_noresume(core->dev);
+		if (!pm_runtime_active(core->dev)) {
+			ret = false;
+			goto done;
+		}
+	}
+
+	ret = core->ops->is_enabled(core->hw);
+done:
+	clk_pm_runtime_put(core);
+
+	return ret;
 }
 
 /***    helper functions   ***/
@@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
 	if (core->ops->unprepare)
 		core->ops->unprepare(core->hw);
 
+	clk_pm_runtime_put(core);
+
 	trace_clk_unprepare_complete(core);
 	clk_core_unprepare(core->parent);
 }
@@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
 		return 0;
 
 	if (core->prepare_count == 0) {
-		ret = clk_core_prepare(core->parent);
+		ret = clk_pm_runtime_get(core);
 		if (ret)
 			return ret;
 
+		ret = clk_core_prepare(core->parent);
+		if (ret)
+			goto runtime_put;
+
 		trace_clk_prepare(core);
 
 		if (core->ops->prepare)
@@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
 
 		trace_clk_prepare_complete(core);
 
-		if (ret) {
-			clk_core_unprepare(core->parent);
-			return ret;
-		}
+		if (ret)
+			goto unprepare;
 	}
 
 	core->prepare_count++;
 
 	return 0;
+unprepare:
+	clk_core_unprepare(core->parent);
+runtime_put:
+	clk_pm_runtime_put(core);
+	return ret;
 }
 
 static int clk_core_prepare_lock(struct clk_core *core)
@@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_IGNORE_UNUSED)
 		return;
 
+	if (clk_pm_runtime_get(core))
+		return;
+
 	if (clk_core_is_prepared(core)) {
 		trace_clk_unprepare(core);
 		if (core->ops->unprepare_unused)
@@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 			core->ops->unprepare(core->hw);
 		trace_clk_unprepare_complete(core);
 	}
+
+	clk_pm_runtime_put(core);
 }
 
 static void clk_disable_unused_subtree(struct clk_core *core)
@@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_prepare_enable(core->parent);
 
+	if (clk_pm_runtime_get(core))
+		goto unprepare_out;
+
 	flags = clk_enable_lock();
 
 	if (core->enable_count)
@@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 
 unlock_out:
 	clk_enable_unlock(flags);
+	clk_pm_runtime_put(core);
+unprepare_out:
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_disable_unprepare(core->parent);
 }
@@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk)
 static unsigned long clk_recalc(struct clk_core *core,
 				unsigned long parent_rate)
 {
-	if (core->ops->recalc_rate)
-		return core->ops->recalc_rate(core->hw, parent_rate);
-	return parent_rate;
+	unsigned long rate = parent_rate;
+
+	if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
+		rate = core->ops->recalc_rate(core->hw, parent_rate);
+		clk_pm_runtime_put(core);
+	}
+	return rate;
 }
 
 /**
@@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 {
 	struct clk_core *top, *fail_clk;
 	unsigned long rate = req_rate;
+	int ret = 0;
 
 	if (!core)
 		return 0;
@@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (!top)
 		return -EINVAL;
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		return ret;
+
 	/* notify that we are about to change rates */
 	fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
 	if (fail_clk) {
 		pr_debug("%s: failed to set %s rate\n", __func__,
 				fail_clk->name);
 		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err;
 	}
 
 	/* change the rates */
 	clk_change_rate(top);
 
 	core->req_rate = req_rate;
+err:
+	clk_pm_runtime_put(core);
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		p_rate = parent->rate;
 	}
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		goto out;
+
 	/* propagate PRE_RATE_CHANGE notifications */
 	ret = __clk_speculate_rates(core, p_rate);
 
 	/* abort if a driver objects */
 	if (ret & NOTIFY_STOP_MASK)
-		goto out;
+		goto runtime_put;
 
 	/* do the re-parent */
 	ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		__clk_recalc_accuracies(core);
 	}
 
+runtime_put:
+	clk_pm_runtime_put(core);
 out:
 	clk_prepare_unlock();
 
@@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		goto fail_name;
 	}
 	core->ops = hw->init->ops;
+	if (dev && pm_runtime_enabled(dev)) {
+		core->dev = dev;
+		ret = clk_pm_runtime_get(core);
+		if (ret)
+			goto fail_pm;
+	}
 	if (dev && dev->driver)
 		core->owner = dev->driver->owner;
 	core->hw = hw;
@@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 	}
 
 	ret = __clk_core_init(core);
-	if (!ret)
+	if (!ret) {
+		clk_pm_runtime_put(core);
 		return hw->clk;
+	}
 
 	__clk_free_clk(hw->clk);
 	hw->clk = NULL;
-
 fail_parents:
 	kfree(core->parents);
 fail_parent_names_copy:
@@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		kfree_const(core->parent_names[i]);
 	kfree(core->parent_names);
 fail_parent_names:
+	clk_pm_runtime_put(core);
+fail_pm:
 	kfree_const(core->name);
 fail_name:
 	kfree(core);
-- 
1.9.1

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-arm-kernel

Registers for some clocks might be located in the SOC area, which are under the
power domain. To enable access to those registers respective domain has to be
turned on. Additionally, registers for such clocks will usually loose its
contents when power domain is turned off, so additional saving and restoring of
them might be needed in the clock controller driver.

This patch adds basic infrastructure in the clocks core to allow implementing
driver for such clocks under power domains. Clock provider can supply a
struct device pointer, which is the used by clock core for tracking and managing
clock's controller runtime pm state. Each clk_prepare() operation
will first call pm_runtime_get_sync() on the supplied device, while
clk_unprepare() will do pm_runtime_put_sync() at the end.

Additional calls to pm_runtime_get/put functions are required to ensure that any
register access (like calculating/changing clock rates and unpreparing/disabling
unused clocks on boot) will be done with clock controller in runtime resumend
state.

When one wants to register clock controller, which make use of this feature, he
has to:
1. Provide a struct device to the core when registering the provider.
2. Ensure to enable runtime PM for that device before registering clocks.
3. Make sure that the runtime PM status of the controller device reflects
   the HW state.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 114 insertions(+), 15 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0fb39fe217d1..a6001b6e49a7 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
 	const struct clk_ops	*ops;
 	struct clk_hw		*hw;
 	struct module		*owner;
+	struct device		*dev;
 	struct clk_core		*parent;
 	const char		**parent_names;
 	struct clk_core		**parents;
@@ -87,6 +89,26 @@ struct clk {
 	struct hlist_node clks_node;
 };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+	int ret = 0;
+
+	if (!core->dev)
+		return 0;
+
+	ret = pm_runtime_get_sync(core->dev);
+	return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+	if (!core->dev)
+		return;
+
+	pm_runtime_put_sync(core->dev);
+}
+
 /***           locking             ***/
 static void clk_prepare_lock(void)
 {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static bool clk_core_is_prepared(struct clk_core *core)
 {
+	bool ret = false;
+
 	/*
 	 * .is_prepared is optional for clocks that can prepare
 	 * fall back to software usage counter if it is missing
@@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core)
 	if (!core->ops->is_prepared)
 		return core->prepare_count;
 
-	return core->ops->is_prepared(core->hw);
+	if (!clk_pm_runtime_get(core)) {
+		ret = core->ops->is_prepared(core->hw);
+		clk_pm_runtime_put(core);
+	}
+
+	return ret;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+	bool ret = false;
+
 	/*
 	 * .is_enabled is only mandatory for clocks that gate
 	 * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core)
 	if (!core->ops->is_enabled)
 		return core->enable_count;
 
-	return core->ops->is_enabled(core->hw);
+	/*
+	 * Check if clock controller's device is runtime active before
+	 * calling .is_enabled callback. If not, assume that clock is
+	 * disabled, because we might be called from atomic context, from
+	 * which pm_runtime_get() is not allowed.
+	 * This function is called mainly from clk_disable_unused_subtree,
+	 * which ensures proper runtime pm activation of controller before
+	 * taking enable spinlock, but the below check is needed if one tries
+	 * to call it from other places.
+	 */
+	if (core->dev) {
+		pm_runtime_get_noresume(core->dev);
+		if (!pm_runtime_active(core->dev)) {
+			ret = false;
+			goto done;
+		}
+	}
+
+	ret = core->ops->is_enabled(core->hw);
+done:
+	clk_pm_runtime_put(core);
+
+	return ret;
 }
 
 /***    helper functions   ***/
@@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
 	if (core->ops->unprepare)
 		core->ops->unprepare(core->hw);
 
+	clk_pm_runtime_put(core);
+
 	trace_clk_unprepare_complete(core);
 	clk_core_unprepare(core->parent);
 }
@@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
 		return 0;
 
 	if (core->prepare_count == 0) {
-		ret = clk_core_prepare(core->parent);
+		ret = clk_pm_runtime_get(core);
 		if (ret)
 			return ret;
 
+		ret = clk_core_prepare(core->parent);
+		if (ret)
+			goto runtime_put;
+
 		trace_clk_prepare(core);
 
 		if (core->ops->prepare)
@@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
 
 		trace_clk_prepare_complete(core);
 
-		if (ret) {
-			clk_core_unprepare(core->parent);
-			return ret;
-		}
+		if (ret)
+			goto unprepare;
 	}
 
 	core->prepare_count++;
 
 	return 0;
+unprepare:
+	clk_core_unprepare(core->parent);
+runtime_put:
+	clk_pm_runtime_put(core);
+	return ret;
 }
 
 static int clk_core_prepare_lock(struct clk_core *core)
@@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_IGNORE_UNUSED)
 		return;
 
+	if (clk_pm_runtime_get(core))
+		return;
+
 	if (clk_core_is_prepared(core)) {
 		trace_clk_unprepare(core);
 		if (core->ops->unprepare_unused)
@@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 			core->ops->unprepare(core->hw);
 		trace_clk_unprepare_complete(core);
 	}
+
+	clk_pm_runtime_put(core);
 }
 
 static void clk_disable_unused_subtree(struct clk_core *core)
@@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_prepare_enable(core->parent);
 
+	if (clk_pm_runtime_get(core))
+		goto unprepare_out;
+
 	flags = clk_enable_lock();
 
 	if (core->enable_count)
@@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
 
 unlock_out:
 	clk_enable_unlock(flags);
+	clk_pm_runtime_put(core);
+unprepare_out:
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_disable_unprepare(core->parent);
 }
@@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk)
 static unsigned long clk_recalc(struct clk_core *core,
 				unsigned long parent_rate)
 {
-	if (core->ops->recalc_rate)
-		return core->ops->recalc_rate(core->hw, parent_rate);
-	return parent_rate;
+	unsigned long rate = parent_rate;
+
+	if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
+		rate = core->ops->recalc_rate(core->hw, parent_rate);
+		clk_pm_runtime_put(core);
+	}
+	return rate;
 }
 
 /**
@@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 {
 	struct clk_core *top, *fail_clk;
 	unsigned long rate = req_rate;
+	int ret = 0;
 
 	if (!core)
 		return 0;
@@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (!top)
 		return -EINVAL;
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		return ret;
+
 	/* notify that we are about to change rates */
 	fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
 	if (fail_clk) {
 		pr_debug("%s: failed to set %s rate\n", __func__,
 				fail_clk->name);
 		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err;
 	}
 
 	/* change the rates */
 	clk_change_rate(top);
 
 	core->req_rate = req_rate;
+err:
+	clk_pm_runtime_put(core);
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		p_rate = parent->rate;
 	}
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		goto out;
+
 	/* propagate PRE_RATE_CHANGE notifications */
 	ret = __clk_speculate_rates(core, p_rate);
 
 	/* abort if a driver objects */
 	if (ret & NOTIFY_STOP_MASK)
-		goto out;
+		goto runtime_put;
 
 	/* do the re-parent */
 	ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		__clk_recalc_accuracies(core);
 	}
 
+runtime_put:
+	clk_pm_runtime_put(core);
 out:
 	clk_prepare_unlock();
 
@@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		goto fail_name;
 	}
 	core->ops = hw->init->ops;
+	if (dev && pm_runtime_enabled(dev)) {
+		core->dev = dev;
+		ret = clk_pm_runtime_get(core);
+		if (ret)
+			goto fail_pm;
+	}
 	if (dev && dev->driver)
 		core->owner = dev->driver->owner;
 	core->hw = hw;
@@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 	}
 
 	ret = __clk_core_init(core);
-	if (!ret)
+	if (!ret) {
+		clk_pm_runtime_put(core);
 		return hw->clk;
+	}
 
 	__clk_free_clk(hw->clk);
 	hw->clk = NULL;
-
 fail_parents:
 	kfree(core->parents);
 fail_parent_names_copy:
@@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		kfree_const(core->parent_names[i]);
 	kfree(core->parent_names);
 fail_parent_names:
+	clk_pm_runtime_put(core);
+fail_pm:
 	kfree_const(core->name);
 fail_name:
 	kfree(core);
-- 
1.9.1

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

* [PATCH v6 2/4] clk: samsung: Add support for runtime PM
       [not found]   ` <CGME20170322113555eucas1p261e9641f1ea64d2b95501dde51792b58@eucas1p2.samsung.com>
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Marek Szyprowski, Stephen Boyd, Michael Turquette, Ulf Hansson,
	Sylwester Nawrocki, Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

This patch adds struct device pointer to samsung_clk_provider and forwarding it
to clk_register_* functions, so drivers can register clocks, which use runtime
pm feature.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/clk/samsung/clk-pll.c |  2 +-
 drivers/clk/samsung/clk.c     | 12 ++++++------
 drivers/clk/samsung/clk.h     |  1 +
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 52290894857a..74c9ce89538a 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1376,7 +1376,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
 	pll->lock_reg = base + pll_clk->lock_offset;
 	pll->con_reg = base + pll_clk->con_offset;
 
-	clk = clk_register(NULL, &pll->hw);
+	clk = clk_register(ctx->dev, &pll->hw);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register pll clock %s : %ld\n",
 			__func__, pll_clk->name, PTR_ERR(clk));
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index b7d87d6db9dc..e6923714f024 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -143,7 +143,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_rate(NULL, list->name,
+		clk = clk_register_fixed_rate(ctx->dev, list->name,
 			list->parent_name, list->flags, list->fixed_rate);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -172,7 +172,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
 	unsigned int idx;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_factor(NULL, list->name,
+		clk = clk_register_fixed_factor(ctx->dev, list->name,
 			list->parent_name, list->flags, list->mult, list->div);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -193,7 +193,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_mux(NULL, list->name, list->parent_names,
+		clk = clk_register_mux(ctx->dev, list->name, list->parent_names,
 			list->num_parents, list->flags,
 			ctx->reg_base + list->offset,
 			list->shift, list->width, list->mux_flags, &ctx->lock);
@@ -226,13 +226,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
 		if (list->table)
-			clk = clk_register_divider_table(NULL, list->name,
+			clk = clk_register_divider_table(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset,
 				list->shift, list->width, list->div_flags,
 				list->table, &ctx->lock);
 		else
-			clk = clk_register_divider(NULL, list->name,
+			clk = clk_register_divider(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset, list->shift,
 				list->width, list->div_flags, &ctx->lock);
@@ -264,7 +264,7 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_gate(NULL, list->name, list->parent_name,
+		clk = clk_register_gate(ctx->dev, list->name, list->parent_name,
 				list->flags, ctx->reg_base + list->offset,
 				list->bit_idx, list->gate_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index da3bdebabf1e..9263d8a27c6b 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -26,6 +26,7 @@
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	struct clk_onecell_data clk_data;
 	spinlock_t lock;
 };
-- 
1.9.1

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

* [PATCH v6 2/4] clk: samsung: Add support for runtime PM
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Ulf Hansson, Bartlomiej Zolnierkiewicz, Michael Turquette,
	Stephen Boyd, Krzysztof Kozlowski, Inki Dae, Chanwoo Choi,
	Sylwester Nawrocki, Marek Szyprowski

This patch adds struct device pointer to samsung_clk_provider and forwarding it
to clk_register_* functions, so drivers can register clocks, which use runtime
pm feature.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/clk/samsung/clk-pll.c |  2 +-
 drivers/clk/samsung/clk.c     | 12 ++++++------
 drivers/clk/samsung/clk.h     |  1 +
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 52290894857a..74c9ce89538a 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1376,7 +1376,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
 	pll->lock_reg = base + pll_clk->lock_offset;
 	pll->con_reg = base + pll_clk->con_offset;
 
-	clk = clk_register(NULL, &pll->hw);
+	clk = clk_register(ctx->dev, &pll->hw);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register pll clock %s : %ld\n",
 			__func__, pll_clk->name, PTR_ERR(clk));
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index b7d87d6db9dc..e6923714f024 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -143,7 +143,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_rate(NULL, list->name,
+		clk = clk_register_fixed_rate(ctx->dev, list->name,
 			list->parent_name, list->flags, list->fixed_rate);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -172,7 +172,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
 	unsigned int idx;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_factor(NULL, list->name,
+		clk = clk_register_fixed_factor(ctx->dev, list->name,
 			list->parent_name, list->flags, list->mult, list->div);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -193,7 +193,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_mux(NULL, list->name, list->parent_names,
+		clk = clk_register_mux(ctx->dev, list->name, list->parent_names,
 			list->num_parents, list->flags,
 			ctx->reg_base + list->offset,
 			list->shift, list->width, list->mux_flags, &ctx->lock);
@@ -226,13 +226,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
 		if (list->table)
-			clk = clk_register_divider_table(NULL, list->name,
+			clk = clk_register_divider_table(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset,
 				list->shift, list->width, list->div_flags,
 				list->table, &ctx->lock);
 		else
-			clk = clk_register_divider(NULL, list->name,
+			clk = clk_register_divider(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset, list->shift,
 				list->width, list->div_flags, &ctx->lock);
@@ -264,7 +264,7 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_gate(NULL, list->name, list->parent_name,
+		clk = clk_register_gate(ctx->dev, list->name, list->parent_name,
 				list->flags, ctx->reg_base + list->offset,
 				list->bit_idx, list->gate_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index da3bdebabf1e..9263d8a27c6b 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -26,6 +26,7 @@
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	struct clk_onecell_data clk_data;
 	spinlock_t lock;
 };
-- 
1.9.1

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

* [PATCH v6 2/4] clk: samsung: Add support for runtime PM
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds struct device pointer to samsung_clk_provider and forwarding it
to clk_register_* functions, so drivers can register clocks, which use runtime
pm feature.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 drivers/clk/samsung/clk-pll.c |  2 +-
 drivers/clk/samsung/clk.c     | 12 ++++++------
 drivers/clk/samsung/clk.h     |  1 +
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/samsung/clk-pll.c b/drivers/clk/samsung/clk-pll.c
index 52290894857a..74c9ce89538a 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1376,7 +1376,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
 	pll->lock_reg = base + pll_clk->lock_offset;
 	pll->con_reg = base + pll_clk->con_offset;
 
-	clk = clk_register(NULL, &pll->hw);
+	clk = clk_register(ctx->dev, &pll->hw);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register pll clock %s : %ld\n",
 			__func__, pll_clk->name, PTR_ERR(clk));
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index b7d87d6db9dc..e6923714f024 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -143,7 +143,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_rate(NULL, list->name,
+		clk = clk_register_fixed_rate(ctx->dev, list->name,
 			list->parent_name, list->flags, list->fixed_rate);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -172,7 +172,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
 	unsigned int idx;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_factor(NULL, list->name,
+		clk = clk_register_fixed_factor(ctx->dev, list->name,
 			list->parent_name, list->flags, list->mult, list->div);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -193,7 +193,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_mux(NULL, list->name, list->parent_names,
+		clk = clk_register_mux(ctx->dev, list->name, list->parent_names,
 			list->num_parents, list->flags,
 			ctx->reg_base + list->offset,
 			list->shift, list->width, list->mux_flags, &ctx->lock);
@@ -226,13 +226,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
 		if (list->table)
-			clk = clk_register_divider_table(NULL, list->name,
+			clk = clk_register_divider_table(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset,
 				list->shift, list->width, list->div_flags,
 				list->table, &ctx->lock);
 		else
-			clk = clk_register_divider(NULL, list->name,
+			clk = clk_register_divider(ctx->dev, list->name,
 				list->parent_name, list->flags,
 				ctx->reg_base + list->offset, list->shift,
 				list->width, list->div_flags, &ctx->lock);
@@ -264,7 +264,7 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_gate(NULL, list->name, list->parent_name,
+		clk = clk_register_gate(ctx->dev, list->name, list->parent_name,
 				list->flags, ctx->reg_base + list->offset,
 				list->bit_idx, list->gate_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index da3bdebabf1e..9263d8a27c6b 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -26,6 +26,7 @@
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	struct clk_onecell_data clk_data;
 	spinlock_t lock;
 };
-- 
1.9.1

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

* [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
       [not found]   ` <CGME20170322113555eucas1p2e6d19d66de5dc48b9f34e4b639759f78@eucas1p2.samsung.com>
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Marek Szyprowski, Stephen Boyd, Michael Turquette, Ulf Hansson,
	Sylwester Nawrocki, Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

Add runtime pm support for all clock controller units (CMU), which belongs
to power domains and require special handling during on/off operations.
Typically special values has to be written to MUX registers to change
internal clocks parents to OSC clock before turning power off. During such
operation all clocks, which enters CMU has to be enabled to let MUX to
stabilize. Also for each CMU there is one special parent clock, which has
to be enabled all the time when any access to CMU registers is done.

This patch solves most of the mysterious external abort and freeze issues
caused by a lack of proper parent CMU clock enabled or incorrect turn off
procedure.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 350 insertions(+), 85 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
index 1dc80f8811fe..5c7dd12e667a 100644
--- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
@@ -168,6 +168,11 @@ Required Properties:
 		- aclk_cam1_400
 		- aclk_cam1_552
 
+Optional properties:
+  - power-domains: a phandle to respective power domain node as described by
+	generic PM domain bindings (see power/power_domain.txt for more
+	information).
+
 Each clock is assigned an identifier and client nodes can use this identifier
 to specify the clock which they consume.
 
@@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_G2D_266>,
 		       <&cmu_top CLK_ACLK_G2D_400>;
+		power-domains = <&pd_g2d>;
 	};
 
 	cmu_disp: clock-controller@13b90000 {
@@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
 		       <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
 		       <&cmu_mif CLK_ACLK_DISP_333>;
+		power-domains = <&pd_disp>;
 	};
 
 	cmu_aud: clock-controller@114c0000 {
@@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "fout_aud_pll";
 		clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
+		power-domains = <&pd_aud>;
 	};
 
 	cmu_bus0: clock-controller@13600000 {
@@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_g3d_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
+		power-domains = <&pd_g3d>;
 	};
 
 	cmu_gscl: clock-controller@13cf0000 {
@@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 			<&cmu_top CLK_ACLK_GSCL_111>,
 			<&cmu_top CLK_ACLK_GSCL_333>;
+		power-domains = <&pd_gscl>;
 	};
 
 	cmu_apollo: clock-controller@11900000 {
@@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_SCLK_JPEG_MSCL>,
 		       <&cmu_top CLK_ACLK_MSCL_400>;
+		power-domains = <&pd_mscl>;
 	};
 
 	cmu_mfc: clock-controller@15280000 {
@@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_mfc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
+		power-domains = <&pd_mfc>;
 	};
 
 	cmu_hevc: clock-controller@14f80000 {
@@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_hevc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
+		power-domains = <&pd_hevc>;
 	};
 
 	cmu_isp: clock-controller@146d0000 {
@@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_ISP_DIS_400>,
 		       <&cmu_top CLK_ACLK_ISP_400>;
+		power-domains = <&pd_isp>;
 	};
 
 	cmu_cam0: clock-controller@120d0000 {
@@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM0_333>,
 		       <&cmu_top CLK_ACLK_CAM0_400>,
 		       <&cmu_top CLK_ACLK_CAM0_552>;
+		power-domains = <&pd_cam0>;
 	};
 
 	cmu_cam1: clock-controller@145d0000 {
@@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM1_333>,
 		       <&cmu_top CLK_ACLK_CAM1_400>,
 		       <&cmu_top CLK_ACLK_CAM1_552>;
+		power-domains = <&pd_cam1>;
 	};
 
 Example 3: UART controller node that consumes the clock generated by the clock
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 11343a597093..a0d88ce948d7 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,14 @@
  * Common Clock Framework support for Exynos5433 SoC.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <dt-bindings/clock/exynos5433.h>
 
@@ -1991,6 +1996,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	ENABLE_IP_FSYS1,
 };
 
+static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
+	{ MUX_SEL_FSYS0, 0 },
+	{ MUX_SEL_FSYS1, 0 },
+	{ MUX_SEL_FSYS2, 0 },
+	{ MUX_SEL_FSYS3, 0 },
+	{ MUX_SEL_FSYS4, 0 },
+};
+
 static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
 	/* PHY clocks from USBDRD30_PHY */
 	FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
@@ -2296,16 +2309,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	.nr_clk_ids		= FSYS_NR_CLK,
 	.clk_regs		= fsys_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(fsys_clk_regs),
+	.suspend_regs		= fsys_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(fsys_suspend_regs),
+	.clk_name		= "aclk_fsys_200",
 };
 
-static void __init exynos5433_cmu_fsys_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &fsys_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
-		exynos5433_cmu_fsys_init);
-
 /*
  * Register offset definitions for CMU_G2D
  */
@@ -2335,6 +2343,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
 };
 
+static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
+	{ MUX_SEL_G2D0, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g2d_266_user_p)		= { "oscclk", "aclk_g2d_266", };
 PNAME(mout_aclk_g2d_400_user_p)		= { "oscclk", "aclk_g2d_400", };
@@ -2420,16 +2432,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	.nr_clk_ids		= G2D_NR_CLK,
 	.clk_regs		= g2d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g2d_clk_regs),
+	.suspend_regs		= g2d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g2d_suspend_regs),
+	.clk_name		= "aclk_g2d_400",
 };
 
-static void __init exynos5433_cmu_g2d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g2d_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
-		exynos5433_cmu_g2d_init);
-
 /*
  * Register offset definitions for CMU_DISP
  */
@@ -2494,6 +2501,18 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	CLKOUT_CMU_DISP_DIV_STAT,
 };
 
+static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
+	/* PLL has to be enabled for suspend */
+	{ DISP_PLL_CON0, 0x85f40502 },
+	/* ignore status of external PHY muxes during suspend to avoid hangs */
+	{ MUX_IGNORE_DISP2, 0x00111111 },
+	{ MUX_SEL_DISP0, 0 },
+	{ MUX_SEL_DISP1, 0 },
+	{ MUX_SEL_DISP2, 0 },
+	{ MUX_SEL_DISP3, 0 },
+	{ MUX_SEL_DISP4, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_disp_pll_p)			= { "oscclk", "fout_disp_pll", };
 PNAME(mout_sclk_dsim1_user_p)		= { "oscclk", "sclk_dsim1_disp", };
@@ -2841,16 +2860,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	.nr_clk_ids		= DISP_NR_CLK,
 	.clk_regs		= disp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(disp_clk_regs),
+	.suspend_regs		= disp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(disp_suspend_regs),
+	.clk_name		= "aclk_disp_333",
 };
 
-static void __init exynos5433_cmu_disp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &disp_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
-		exynos5433_cmu_disp_init);
-
 /*
  * Register offset definitions for CMU_AUD
  */
@@ -2885,6 +2899,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	ENABLE_IP_AUD1,
 };
 
+static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
+	{ MUX_SEL_AUD0, 0 },
+	{ MUX_SEL_AUD1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aud_pll_user_aud_p)	= { "oscclk", "fout_aud_pll", };
 PNAME(mout_sclk_aud_pcm_p)	= { "mout_aud_pll_user", "ioclk_audiocdclk0",};
@@ -3011,16 +3030,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	.nr_clk_ids		= AUD_NR_CLK,
 	.clk_regs		= aud_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(aud_clk_regs),
+	.suspend_regs		= aud_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(aud_suspend_regs),
+	.clk_name		= "fout_aud_pll",
 };
 
-static void __init exynos5433_cmu_aud_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &aud_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
-		exynos5433_cmu_aud_init);
-
-
 /*
  * Register offset definitions for CMU_BUS{0|1|2}
  */
@@ -3222,6 +3236,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	CLK_STOPCTRL,
 };
 
+static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
+	{ MUX_SEL_G3D, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g3d_400_p)	= { "mout_g3d_pll", "aclk_g3d_400", };
 PNAME(mout_g3d_pll_p)		= { "oscclk", "fout_g3d_pll", };
@@ -3295,15 +3313,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	.nr_clk_ids		= G3D_NR_CLK,
 	.clk_regs		= g3d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g3d_clk_regs),
+	.suspend_regs		= g3d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g3d_suspend_regs),
+	.clk_name		= "aclk_g3d_400",
 };
 
-static void __init exynos5433_cmu_g3d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g3d_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
-		exynos5433_cmu_g3d_init);
-
 /*
  * Register offset definitions for CMU_GSCL
  */
@@ -3342,6 +3356,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
 };
 
+static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
+	{ MUX_SEL_GSCL, 0 },
+	{ ENABLE_ACLK_GSCL, 0xfff },
+	{ ENABLE_PCLK_GSCL, 0xff },
+};
+
 /* list of all parent clock list */
 PNAME(aclk_gscl_111_user_p)	= { "oscclk", "aclk_gscl_111", };
 PNAME(aclk_gscl_333_user_p)	= { "oscclk", "aclk_gscl_333", };
@@ -3436,15 +3456,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	.nr_clk_ids		= GSCL_NR_CLK,
 	.clk_regs		= gscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(gscl_clk_regs),
+	.suspend_regs		= gscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(gscl_suspend_regs),
+	.clk_name		= "aclk_gscl_111",
 };
 
-static void __init exynos5433_cmu_gscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &gscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
-		exynos5433_cmu_gscl_init);
-
 /*
  * Register offset definitions for CMU_APOLLO
  */
@@ -3970,6 +3986,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
 };
 
+static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
+	{ MUX_SEL_MSCL0, 0 },
+	{ MUX_SEL_MSCL1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_sclk_jpeg_user_p)		= { "oscclk", "sclk_jpeg_mscl", };
 PNAME(mout_aclk_mscl_400_user_p)	= { "oscclk", "aclk_mscl_400", };
@@ -4082,15 +4103,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	.nr_clk_ids		= MSCL_NR_CLK,
 	.clk_regs		= mscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mscl_clk_regs),
+	.suspend_regs		= mscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mscl_suspend_regs),
+	.clk_name		= "aclk_mscl_400",
 };
 
-static void __init exynos5433_cmu_mscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
-		exynos5433_cmu_mscl_init);
-
 /*
  * Register offset definitions for CMU_MFC
  */
@@ -4120,6 +4137,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	ENABLE_IP_MFC_SECURE_SMMU_MFC,
 };
 
+static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
+	{ MUX_SEL_MFC, 0 },
+};
+
 PNAME(mout_aclk_mfc_400_user_p)		= { "oscclk", "aclk_mfc_400", };
 
 static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
@@ -4190,15 +4211,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	.nr_clk_ids		= MFC_NR_CLK,
 	.clk_regs		= mfc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mfc_clk_regs),
+	.suspend_regs		= mfc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mfc_suspend_regs),
+	.clk_name		= "aclk_mfc_400",
 };
 
-static void __init exynos5433_cmu_mfc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mfc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
-		exynos5433_cmu_mfc_init);
-
 /*
  * Register offset definitions for CMU_HEVC
  */
@@ -4228,6 +4245,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
 };
 
+static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
+	{ MUX_SEL_HEVC, 0 },
+};
+
 PNAME(mout_aclk_hevc_400_user_p)	= { "oscclk", "aclk_hevc_400", };
 
 static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
@@ -4300,15 +4321,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	.nr_clk_ids		= HEVC_NR_CLK,
 	.clk_regs		= hevc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(hevc_clk_regs),
+	.suspend_regs		= hevc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(hevc_suspend_regs),
+	.clk_name		= "aclk_hevc_400",
 };
 
-static void __init exynos5433_cmu_hevc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &hevc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
-		exynos5433_cmu_hevc_init);
-
 /*
  * Register offset definitions for CMU_ISP
  */
@@ -4342,6 +4359,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	ENABLE_IP_ISP3,
 };
 
+static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
+	{ MUX_SEL_ISP, 0 },
+};
+
 PNAME(mout_aclk_isp_dis_400_user_p)	= { "oscclk", "aclk_isp_dis_400", };
 PNAME(mout_aclk_isp_400_user_p)		= { "oscclk", "aclk_isp_400", };
 
@@ -4553,15 +4574,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	.nr_clk_ids		= ISP_NR_CLK,
 	.clk_regs		= isp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(isp_clk_regs),
+	.suspend_regs		= isp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(isp_suspend_regs),
+	.clk_name		= "aclk_isp_400",
 };
 
-static void __init exynos5433_cmu_isp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &isp_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
-		exynos5433_cmu_isp_init);
-
 /*
  * Register offset definitions for CMU_CAM0
  */
@@ -4625,6 +4642,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	ENABLE_IP_CAM02,
 	ENABLE_IP_CAM03,
 };
+
+static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
+	{ MUX_SEL_CAM00, 0 },
+	{ MUX_SEL_CAM01, 0 },
+	{ MUX_SEL_CAM02, 0 },
+	{ MUX_SEL_CAM03, 0 },
+	{ MUX_SEL_CAM04, 0 },
+};
+
 PNAME(mout_aclk_cam0_333_user_p)	= { "oscclk", "aclk_cam0_333", };
 PNAME(mout_aclk_cam0_400_user_p)	= { "oscclk", "aclk_cam0_400", };
 PNAME(mout_aclk_cam0_552_user_p)	= { "oscclk", "aclk_cam0_552", };
@@ -5030,15 +5056,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	.nr_clk_ids		= CAM0_NR_CLK,
 	.clk_regs		= cam0_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam0_clk_regs),
+	.suspend_regs		= cam0_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam0_suspend_regs),
+	.clk_name		= "aclk_cam0_400",
 };
 
-static void __init exynos5433_cmu_cam0_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &cam0_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
-		exynos5433_cmu_cam0_init);
-
 /*
  * Register offset definitions for CMU_CAM1
  */
@@ -5085,6 +5107,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	ENABLE_IP_CAM12,
 };
 
+static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
+	{ MUX_SEL_CAM10, 0 },
+	{ MUX_SEL_CAM11, 0 },
+	{ MUX_SEL_CAM12, 0 },
+};
+
 PNAME(mout_sclk_isp_uart_user_p)	= { "oscclk", "sclk_isp_uart_cam1", };
 PNAME(mout_sclk_isp_spi1_user_p)	= { "oscclk", "sclk_isp_spi1_cam1", };
 PNAME(mout_sclk_isp_spi0_user_p)	= { "oscclk", "sclk_isp_spi0_cam1", };
@@ -5403,11 +5431,226 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	.nr_clk_ids		= CAM1_NR_CLK,
 	.clk_regs		= cam1_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam1_clk_regs),
+	.suspend_regs		= cam1_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam1_suspend_regs),
+	.clk_name		= "aclk_cam1_400",
+};
+
+
+struct exynos5433_cmu_data {
+	struct samsung_clk_provider ctx;
+
+	struct samsung_clk_reg_dump *clk_save;
+	unsigned int nr_clk_save;
+	const struct samsung_clk_reg_dump *clk_suspend;
+	unsigned int nr_clk_suspend;
+
+	struct clk *clk;
+	struct clk **pclks;
+	int nr_pclks;
+};
+
+static int exynos5433_cmu_suspend(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	samsung_clk_save(data->ctx.reg_base, data->clk_save,
+			 data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
+			    data->nr_clk_suspend);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable_unprepare(data->pclks[i]);
+
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	clk_prepare_enable(data->clk);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_save,
+			    data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable_unprepare(data->pclks[i]);
+
+	return 0;
+}
+
+static int __init exynos5433_cmu_probe(struct platform_device *pdev)
+{
+	const struct samsung_cmu_info *info;
+	struct exynos5433_cmu_data *data;
+	struct samsung_clk_provider *ctx;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *reg_base;
+	struct clk **clk_table;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	ctx = &data->ctx;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base)) {
+		dev_err(dev, "failed to map registers\n");
+		return PTR_ERR(reg_base);
+	}
+
+	clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
+				 GFP_KERNEL);
+	if (!clk_table)
+		return -ENOMEM;
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = info->nr_clk_ids;
+	ctx->reg_base = reg_base;
+	ctx->dev = dev;
+	spin_lock_init(&ctx->lock);
+
+	data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
+						    info->nr_clk_regs);
+	data->nr_clk_save = info->nr_clk_regs;
+	data->clk_suspend = info->suspend_regs;
+	data->nr_clk_suspend = info->nr_suspend_regs;
+	data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
+						    "#clock-cells");
+	if (data->nr_pclks > 0) {
+		data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
+					   data->nr_pclks, GFP_KERNEL);
+
+		for (i = 0; i < data->nr_pclks; i++) {
+			struct clk *clk = of_clk_get(dev->of_node, i);
+
+			if (IS_ERR(clk))
+				return PTR_ERR(clk);
+			data->pclks[i] = clk;
+		}
+	}
+
+	if (info->clk_name)
+		data->clk = clk_get(dev, info->clk_name);
+	clk_prepare_enable(data->clk);
+
+	platform_set_drvdata(pdev, data);
+
+	/*
+	 * Enable runtime pm here, so clock core with use runtime pm for all
+	 * registered clocks. Additionally we increase the runtime PM usage
+	 * count during the clock registration, to avoid the clock core from
+	 * runtime suspending the device.
+	 */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	if (info->pll_clks)
+		samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
+					 reg_base);
+	if (info->mux_clks)
+		samsung_clk_register_mux(ctx, info->mux_clks,
+					 info->nr_mux_clks);
+	if (info->div_clks)
+		samsung_clk_register_div(ctx, info->div_clks,
+					 info->nr_div_clks);
+	if (info->gate_clks)
+		samsung_clk_register_gate(ctx, info->gate_clks,
+					  info->nr_gate_clks);
+	if (info->fixed_clks)
+		samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
+						info->nr_fixed_clks);
+	if (info->fixed_factor_clks)
+		samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
+						  info->nr_fixed_factor_clks);
+
+	samsung_clk_of_add_provider(dev->of_node, ctx);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static const struct of_device_id exynos5433_cmu_of_match[] = {
+	{
+		.compatible = "samsung,exynos5433-cmu-aud",
+		.data = &aud_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam0",
+		.data = &cam0_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam1",
+		.data = &cam1_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-disp",
+		.data = &disp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g2d",
+		.data = &g2d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g3d",
+		.data = &g3d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-fsys",
+		.data = &fsys_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-gscl",
+		.data = &gscl_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mfc",
+		.data = &mfc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-hevc",
+		.data = &hevc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-isp",
+		.data = &isp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mscl",
+		.data = &mscl_cmu_info,
+	}, {
+	},
+};
+
+static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver exynos5433_cmu_driver __refdata = {
+	.driver	= {
+		.name = "exynos5433-cmu",
+		.of_match_table = exynos5433_cmu_of_match,
+		.suppress_bind_attrs = true,
+		.pm = &exynos5433_cmu_pm_ops,
+	},
+	.probe = exynos5433_cmu_probe,
 };
 
-static void __init exynos5433_cmu_cam1_init(struct device_node *np)
+static int __init exynos5433_cmu_init(void)
 {
-	samsung_cmu_register_one(np, &cam1_cmu_info);
+	return platform_driver_register(&exynos5433_cmu_driver);
 }
-CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
-		exynos5433_cmu_cam1_init);
+core_initcall(exynos5433_cmu_init);
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 9263d8a27c6b..664020cb4794 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -354,6 +354,12 @@ struct samsung_cmu_info {
 	/* list and number of clocks registers */
 	const unsigned long *clk_regs;
 	unsigned int nr_clk_regs;
+
+	/* list and number of clocks registers to set before suspend */
+	const struct samsung_clk_reg_dump *suspend_regs;
+	unsigned int nr_suspend_regs;
+	/* name of the parent clock needed for CMU register access */
+	const char *clk_name;
 };
 
 extern struct samsung_clk_provider *__init samsung_clk_init(
-- 
1.9.1

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

* [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Ulf Hansson, Bartlomiej Zolnierkiewicz, Michael Turquette,
	Stephen Boyd, Krzysztof Kozlowski, Inki Dae, Chanwoo Choi,
	Sylwester Nawrocki, Marek Szyprowski

Add runtime pm support for all clock controller units (CMU), which belongs
to power domains and require special handling during on/off operations.
Typically special values has to be written to MUX registers to change
internal clocks parents to OSC clock before turning power off. During such
operation all clocks, which enters CMU has to be enabled to let MUX to
stabilize. Also for each CMU there is one special parent clock, which has
to be enabled all the time when any access to CMU registers is done.

This patch solves most of the mysterious external abort and freeze issues
caused by a lack of proper parent CMU clock enabled or incorrect turn off
procedure.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 350 insertions(+), 85 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
index 1dc80f8811fe..5c7dd12e667a 100644
--- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
@@ -168,6 +168,11 @@ Required Properties:
 		- aclk_cam1_400
 		- aclk_cam1_552
 
+Optional properties:
+  - power-domains: a phandle to respective power domain node as described by
+	generic PM domain bindings (see power/power_domain.txt for more
+	information).
+
 Each clock is assigned an identifier and client nodes can use this identifier
 to specify the clock which they consume.
 
@@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_G2D_266>,
 		       <&cmu_top CLK_ACLK_G2D_400>;
+		power-domains = <&pd_g2d>;
 	};
 
 	cmu_disp: clock-controller@13b90000 {
@@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
 		       <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
 		       <&cmu_mif CLK_ACLK_DISP_333>;
+		power-domains = <&pd_disp>;
 	};
 
 	cmu_aud: clock-controller@114c0000 {
@@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "fout_aud_pll";
 		clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
+		power-domains = <&pd_aud>;
 	};
 
 	cmu_bus0: clock-controller@13600000 {
@@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_g3d_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
+		power-domains = <&pd_g3d>;
 	};
 
 	cmu_gscl: clock-controller@13cf0000 {
@@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 			<&cmu_top CLK_ACLK_GSCL_111>,
 			<&cmu_top CLK_ACLK_GSCL_333>;
+		power-domains = <&pd_gscl>;
 	};
 
 	cmu_apollo: clock-controller@11900000 {
@@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_SCLK_JPEG_MSCL>,
 		       <&cmu_top CLK_ACLK_MSCL_400>;
+		power-domains = <&pd_mscl>;
 	};
 
 	cmu_mfc: clock-controller@15280000 {
@@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_mfc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
+		power-domains = <&pd_mfc>;
 	};
 
 	cmu_hevc: clock-controller@14f80000 {
@@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_hevc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
+		power-domains = <&pd_hevc>;
 	};
 
 	cmu_isp: clock-controller@146d0000 {
@@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_ISP_DIS_400>,
 		       <&cmu_top CLK_ACLK_ISP_400>;
+		power-domains = <&pd_isp>;
 	};
 
 	cmu_cam0: clock-controller@120d0000 {
@@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM0_333>,
 		       <&cmu_top CLK_ACLK_CAM0_400>,
 		       <&cmu_top CLK_ACLK_CAM0_552>;
+		power-domains = <&pd_cam0>;
 	};
 
 	cmu_cam1: clock-controller@145d0000 {
@@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM1_333>,
 		       <&cmu_top CLK_ACLK_CAM1_400>,
 		       <&cmu_top CLK_ACLK_CAM1_552>;
+		power-domains = <&pd_cam1>;
 	};
 
 Example 3: UART controller node that consumes the clock generated by the clock
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 11343a597093..a0d88ce948d7 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,14 @@
  * Common Clock Framework support for Exynos5433 SoC.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <dt-bindings/clock/exynos5433.h>
 
@@ -1991,6 +1996,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	ENABLE_IP_FSYS1,
 };
 
+static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
+	{ MUX_SEL_FSYS0, 0 },
+	{ MUX_SEL_FSYS1, 0 },
+	{ MUX_SEL_FSYS2, 0 },
+	{ MUX_SEL_FSYS3, 0 },
+	{ MUX_SEL_FSYS4, 0 },
+};
+
 static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
 	/* PHY clocks from USBDRD30_PHY */
 	FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
@@ -2296,16 +2309,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	.nr_clk_ids		= FSYS_NR_CLK,
 	.clk_regs		= fsys_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(fsys_clk_regs),
+	.suspend_regs		= fsys_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(fsys_suspend_regs),
+	.clk_name		= "aclk_fsys_200",
 };
 
-static void __init exynos5433_cmu_fsys_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &fsys_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
-		exynos5433_cmu_fsys_init);
-
 /*
  * Register offset definitions for CMU_G2D
  */
@@ -2335,6 +2343,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
 };
 
+static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
+	{ MUX_SEL_G2D0, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g2d_266_user_p)		= { "oscclk", "aclk_g2d_266", };
 PNAME(mout_aclk_g2d_400_user_p)		= { "oscclk", "aclk_g2d_400", };
@@ -2420,16 +2432,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	.nr_clk_ids		= G2D_NR_CLK,
 	.clk_regs		= g2d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g2d_clk_regs),
+	.suspend_regs		= g2d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g2d_suspend_regs),
+	.clk_name		= "aclk_g2d_400",
 };
 
-static void __init exynos5433_cmu_g2d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g2d_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
-		exynos5433_cmu_g2d_init);
-
 /*
  * Register offset definitions for CMU_DISP
  */
@@ -2494,6 +2501,18 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	CLKOUT_CMU_DISP_DIV_STAT,
 };
 
+static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
+	/* PLL has to be enabled for suspend */
+	{ DISP_PLL_CON0, 0x85f40502 },
+	/* ignore status of external PHY muxes during suspend to avoid hangs */
+	{ MUX_IGNORE_DISP2, 0x00111111 },
+	{ MUX_SEL_DISP0, 0 },
+	{ MUX_SEL_DISP1, 0 },
+	{ MUX_SEL_DISP2, 0 },
+	{ MUX_SEL_DISP3, 0 },
+	{ MUX_SEL_DISP4, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_disp_pll_p)			= { "oscclk", "fout_disp_pll", };
 PNAME(mout_sclk_dsim1_user_p)		= { "oscclk", "sclk_dsim1_disp", };
@@ -2841,16 +2860,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	.nr_clk_ids		= DISP_NR_CLK,
 	.clk_regs		= disp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(disp_clk_regs),
+	.suspend_regs		= disp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(disp_suspend_regs),
+	.clk_name		= "aclk_disp_333",
 };
 
-static void __init exynos5433_cmu_disp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &disp_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
-		exynos5433_cmu_disp_init);
-
 /*
  * Register offset definitions for CMU_AUD
  */
@@ -2885,6 +2899,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	ENABLE_IP_AUD1,
 };
 
+static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
+	{ MUX_SEL_AUD0, 0 },
+	{ MUX_SEL_AUD1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aud_pll_user_aud_p)	= { "oscclk", "fout_aud_pll", };
 PNAME(mout_sclk_aud_pcm_p)	= { "mout_aud_pll_user", "ioclk_audiocdclk0",};
@@ -3011,16 +3030,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	.nr_clk_ids		= AUD_NR_CLK,
 	.clk_regs		= aud_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(aud_clk_regs),
+	.suspend_regs		= aud_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(aud_suspend_regs),
+	.clk_name		= "fout_aud_pll",
 };
 
-static void __init exynos5433_cmu_aud_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &aud_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
-		exynos5433_cmu_aud_init);
-
-
 /*
  * Register offset definitions for CMU_BUS{0|1|2}
  */
@@ -3222,6 +3236,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	CLK_STOPCTRL,
 };
 
+static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
+	{ MUX_SEL_G3D, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g3d_400_p)	= { "mout_g3d_pll", "aclk_g3d_400", };
 PNAME(mout_g3d_pll_p)		= { "oscclk", "fout_g3d_pll", };
@@ -3295,15 +3313,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	.nr_clk_ids		= G3D_NR_CLK,
 	.clk_regs		= g3d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g3d_clk_regs),
+	.suspend_regs		= g3d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g3d_suspend_regs),
+	.clk_name		= "aclk_g3d_400",
 };
 
-static void __init exynos5433_cmu_g3d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g3d_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
-		exynos5433_cmu_g3d_init);
-
 /*
  * Register offset definitions for CMU_GSCL
  */
@@ -3342,6 +3356,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
 };
 
+static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
+	{ MUX_SEL_GSCL, 0 },
+	{ ENABLE_ACLK_GSCL, 0xfff },
+	{ ENABLE_PCLK_GSCL, 0xff },
+};
+
 /* list of all parent clock list */
 PNAME(aclk_gscl_111_user_p)	= { "oscclk", "aclk_gscl_111", };
 PNAME(aclk_gscl_333_user_p)	= { "oscclk", "aclk_gscl_333", };
@@ -3436,15 +3456,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	.nr_clk_ids		= GSCL_NR_CLK,
 	.clk_regs		= gscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(gscl_clk_regs),
+	.suspend_regs		= gscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(gscl_suspend_regs),
+	.clk_name		= "aclk_gscl_111",
 };
 
-static void __init exynos5433_cmu_gscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &gscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
-		exynos5433_cmu_gscl_init);
-
 /*
  * Register offset definitions for CMU_APOLLO
  */
@@ -3970,6 +3986,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
 };
 
+static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
+	{ MUX_SEL_MSCL0, 0 },
+	{ MUX_SEL_MSCL1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_sclk_jpeg_user_p)		= { "oscclk", "sclk_jpeg_mscl", };
 PNAME(mout_aclk_mscl_400_user_p)	= { "oscclk", "aclk_mscl_400", };
@@ -4082,15 +4103,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	.nr_clk_ids		= MSCL_NR_CLK,
 	.clk_regs		= mscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mscl_clk_regs),
+	.suspend_regs		= mscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mscl_suspend_regs),
+	.clk_name		= "aclk_mscl_400",
 };
 
-static void __init exynos5433_cmu_mscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
-		exynos5433_cmu_mscl_init);
-
 /*
  * Register offset definitions for CMU_MFC
  */
@@ -4120,6 +4137,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	ENABLE_IP_MFC_SECURE_SMMU_MFC,
 };
 
+static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
+	{ MUX_SEL_MFC, 0 },
+};
+
 PNAME(mout_aclk_mfc_400_user_p)		= { "oscclk", "aclk_mfc_400", };
 
 static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
@@ -4190,15 +4211,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	.nr_clk_ids		= MFC_NR_CLK,
 	.clk_regs		= mfc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mfc_clk_regs),
+	.suspend_regs		= mfc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mfc_suspend_regs),
+	.clk_name		= "aclk_mfc_400",
 };
 
-static void __init exynos5433_cmu_mfc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mfc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
-		exynos5433_cmu_mfc_init);
-
 /*
  * Register offset definitions for CMU_HEVC
  */
@@ -4228,6 +4245,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
 };
 
+static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
+	{ MUX_SEL_HEVC, 0 },
+};
+
 PNAME(mout_aclk_hevc_400_user_p)	= { "oscclk", "aclk_hevc_400", };
 
 static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
@@ -4300,15 +4321,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	.nr_clk_ids		= HEVC_NR_CLK,
 	.clk_regs		= hevc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(hevc_clk_regs),
+	.suspend_regs		= hevc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(hevc_suspend_regs),
+	.clk_name		= "aclk_hevc_400",
 };
 
-static void __init exynos5433_cmu_hevc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &hevc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
-		exynos5433_cmu_hevc_init);
-
 /*
  * Register offset definitions for CMU_ISP
  */
@@ -4342,6 +4359,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	ENABLE_IP_ISP3,
 };
 
+static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
+	{ MUX_SEL_ISP, 0 },
+};
+
 PNAME(mout_aclk_isp_dis_400_user_p)	= { "oscclk", "aclk_isp_dis_400", };
 PNAME(mout_aclk_isp_400_user_p)		= { "oscclk", "aclk_isp_400", };
 
@@ -4553,15 +4574,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	.nr_clk_ids		= ISP_NR_CLK,
 	.clk_regs		= isp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(isp_clk_regs),
+	.suspend_regs		= isp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(isp_suspend_regs),
+	.clk_name		= "aclk_isp_400",
 };
 
-static void __init exynos5433_cmu_isp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &isp_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
-		exynos5433_cmu_isp_init);
-
 /*
  * Register offset definitions for CMU_CAM0
  */
@@ -4625,6 +4642,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	ENABLE_IP_CAM02,
 	ENABLE_IP_CAM03,
 };
+
+static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
+	{ MUX_SEL_CAM00, 0 },
+	{ MUX_SEL_CAM01, 0 },
+	{ MUX_SEL_CAM02, 0 },
+	{ MUX_SEL_CAM03, 0 },
+	{ MUX_SEL_CAM04, 0 },
+};
+
 PNAME(mout_aclk_cam0_333_user_p)	= { "oscclk", "aclk_cam0_333", };
 PNAME(mout_aclk_cam0_400_user_p)	= { "oscclk", "aclk_cam0_400", };
 PNAME(mout_aclk_cam0_552_user_p)	= { "oscclk", "aclk_cam0_552", };
@@ -5030,15 +5056,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	.nr_clk_ids		= CAM0_NR_CLK,
 	.clk_regs		= cam0_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam0_clk_regs),
+	.suspend_regs		= cam0_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam0_suspend_regs),
+	.clk_name		= "aclk_cam0_400",
 };
 
-static void __init exynos5433_cmu_cam0_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &cam0_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
-		exynos5433_cmu_cam0_init);
-
 /*
  * Register offset definitions for CMU_CAM1
  */
@@ -5085,6 +5107,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	ENABLE_IP_CAM12,
 };
 
+static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
+	{ MUX_SEL_CAM10, 0 },
+	{ MUX_SEL_CAM11, 0 },
+	{ MUX_SEL_CAM12, 0 },
+};
+
 PNAME(mout_sclk_isp_uart_user_p)	= { "oscclk", "sclk_isp_uart_cam1", };
 PNAME(mout_sclk_isp_spi1_user_p)	= { "oscclk", "sclk_isp_spi1_cam1", };
 PNAME(mout_sclk_isp_spi0_user_p)	= { "oscclk", "sclk_isp_spi0_cam1", };
@@ -5403,11 +5431,226 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	.nr_clk_ids		= CAM1_NR_CLK,
 	.clk_regs		= cam1_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam1_clk_regs),
+	.suspend_regs		= cam1_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam1_suspend_regs),
+	.clk_name		= "aclk_cam1_400",
+};
+
+
+struct exynos5433_cmu_data {
+	struct samsung_clk_provider ctx;
+
+	struct samsung_clk_reg_dump *clk_save;
+	unsigned int nr_clk_save;
+	const struct samsung_clk_reg_dump *clk_suspend;
+	unsigned int nr_clk_suspend;
+
+	struct clk *clk;
+	struct clk **pclks;
+	int nr_pclks;
+};
+
+static int exynos5433_cmu_suspend(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	samsung_clk_save(data->ctx.reg_base, data->clk_save,
+			 data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
+			    data->nr_clk_suspend);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable_unprepare(data->pclks[i]);
+
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	clk_prepare_enable(data->clk);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_save,
+			    data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable_unprepare(data->pclks[i]);
+
+	return 0;
+}
+
+static int __init exynos5433_cmu_probe(struct platform_device *pdev)
+{
+	const struct samsung_cmu_info *info;
+	struct exynos5433_cmu_data *data;
+	struct samsung_clk_provider *ctx;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *reg_base;
+	struct clk **clk_table;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	ctx = &data->ctx;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base)) {
+		dev_err(dev, "failed to map registers\n");
+		return PTR_ERR(reg_base);
+	}
+
+	clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
+				 GFP_KERNEL);
+	if (!clk_table)
+		return -ENOMEM;
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = info->nr_clk_ids;
+	ctx->reg_base = reg_base;
+	ctx->dev = dev;
+	spin_lock_init(&ctx->lock);
+
+	data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
+						    info->nr_clk_regs);
+	data->nr_clk_save = info->nr_clk_regs;
+	data->clk_suspend = info->suspend_regs;
+	data->nr_clk_suspend = info->nr_suspend_regs;
+	data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
+						    "#clock-cells");
+	if (data->nr_pclks > 0) {
+		data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
+					   data->nr_pclks, GFP_KERNEL);
+
+		for (i = 0; i < data->nr_pclks; i++) {
+			struct clk *clk = of_clk_get(dev->of_node, i);
+
+			if (IS_ERR(clk))
+				return PTR_ERR(clk);
+			data->pclks[i] = clk;
+		}
+	}
+
+	if (info->clk_name)
+		data->clk = clk_get(dev, info->clk_name);
+	clk_prepare_enable(data->clk);
+
+	platform_set_drvdata(pdev, data);
+
+	/*
+	 * Enable runtime pm here, so clock core with use runtime pm for all
+	 * registered clocks. Additionally we increase the runtime PM usage
+	 * count during the clock registration, to avoid the clock core from
+	 * runtime suspending the device.
+	 */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	if (info->pll_clks)
+		samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
+					 reg_base);
+	if (info->mux_clks)
+		samsung_clk_register_mux(ctx, info->mux_clks,
+					 info->nr_mux_clks);
+	if (info->div_clks)
+		samsung_clk_register_div(ctx, info->div_clks,
+					 info->nr_div_clks);
+	if (info->gate_clks)
+		samsung_clk_register_gate(ctx, info->gate_clks,
+					  info->nr_gate_clks);
+	if (info->fixed_clks)
+		samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
+						info->nr_fixed_clks);
+	if (info->fixed_factor_clks)
+		samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
+						  info->nr_fixed_factor_clks);
+
+	samsung_clk_of_add_provider(dev->of_node, ctx);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static const struct of_device_id exynos5433_cmu_of_match[] = {
+	{
+		.compatible = "samsung,exynos5433-cmu-aud",
+		.data = &aud_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam0",
+		.data = &cam0_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam1",
+		.data = &cam1_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-disp",
+		.data = &disp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g2d",
+		.data = &g2d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g3d",
+		.data = &g3d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-fsys",
+		.data = &fsys_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-gscl",
+		.data = &gscl_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mfc",
+		.data = &mfc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-hevc",
+		.data = &hevc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-isp",
+		.data = &isp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mscl",
+		.data = &mscl_cmu_info,
+	}, {
+	},
+};
+
+static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver exynos5433_cmu_driver __refdata = {
+	.driver	= {
+		.name = "exynos5433-cmu",
+		.of_match_table = exynos5433_cmu_of_match,
+		.suppress_bind_attrs = true,
+		.pm = &exynos5433_cmu_pm_ops,
+	},
+	.probe = exynos5433_cmu_probe,
 };
 
-static void __init exynos5433_cmu_cam1_init(struct device_node *np)
+static int __init exynos5433_cmu_init(void)
 {
-	samsung_cmu_register_one(np, &cam1_cmu_info);
+	return platform_driver_register(&exynos5433_cmu_driver);
 }
-CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
-		exynos5433_cmu_cam1_init);
+core_initcall(exynos5433_cmu_init);
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 9263d8a27c6b..664020cb4794 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -354,6 +354,12 @@ struct samsung_cmu_info {
 	/* list and number of clocks registers */
 	const unsigned long *clk_regs;
 	unsigned int nr_clk_regs;
+
+	/* list and number of clocks registers to set before suspend */
+	const struct samsung_clk_reg_dump *suspend_regs;
+	unsigned int nr_suspend_regs;
+	/* name of the parent clock needed for CMU register access */
+	const char *clk_name;
 };
 
 extern struct samsung_clk_provider *__init samsung_clk_init(
-- 
1.9.1

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

* [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-arm-kernel

Add runtime pm support for all clock controller units (CMU), which belongs
to power domains and require special handling during on/off operations.
Typically special values has to be written to MUX registers to change
internal clocks parents to OSC clock before turning power off. During such
operation all clocks, which enters CMU has to be enabled to let MUX to
stabilize. Also for each CMU there is one special parent clock, which has
to be enabled all the time when any access to CMU registers is done.

This patch solves most of the mysterious external abort and freeze issues
caused by a lack of proper parent CMU clock enabled or incorrect turn off
procedure.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 350 insertions(+), 85 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
index 1dc80f8811fe..5c7dd12e667a 100644
--- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
+++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
@@ -168,6 +168,11 @@ Required Properties:
 		- aclk_cam1_400
 		- aclk_cam1_552
 
+Optional properties:
+  - power-domains: a phandle to respective power domain node as described by
+	generic PM domain bindings (see power/power_domain.txt for more
+	information).
+
 Each clock is assigned an identifier and client nodes can use this identifier
 to specify the clock which they consume.
 
@@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_G2D_266>,
 		       <&cmu_top CLK_ACLK_G2D_400>;
+		power-domains = <&pd_g2d>;
 	};
 
 	cmu_disp: clock-controller at 13b90000 {
@@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
 		       <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
 		       <&cmu_mif CLK_ACLK_DISP_333>;
+		power-domains = <&pd_disp>;
 	};
 
 	cmu_aud: clock-controller at 114c0000 {
@@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "fout_aud_pll";
 		clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
+		power-domains = <&pd_aud>;
 	};
 
 	cmu_bus0: clock-controller at 13600000 {
@@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_g3d_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
+		power-domains = <&pd_g3d>;
 	};
 
 	cmu_gscl: clock-controller at 13cf0000 {
@@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 			<&cmu_top CLK_ACLK_GSCL_111>,
 			<&cmu_top CLK_ACLK_GSCL_333>;
+		power-domains = <&pd_gscl>;
 	};
 
 	cmu_apollo: clock-controller at 11900000 {
@@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_SCLK_JPEG_MSCL>,
 		       <&cmu_top CLK_ACLK_MSCL_400>;
+		power-domains = <&pd_mscl>;
 	};
 
 	cmu_mfc: clock-controller at 15280000 {
@@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_mfc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
+		power-domains = <&pd_mfc>;
 	};
 
 	cmu_hevc: clock-controller at 14f80000 {
@@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
 
 		clock-names = "oscclk", "aclk_hevc_400";
 		clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
+		power-domains = <&pd_hevc>;
 	};
 
 	cmu_isp: clock-controller at 146d0000 {
@@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		clocks = <&xxti>,
 		       <&cmu_top CLK_ACLK_ISP_DIS_400>,
 		       <&cmu_top CLK_ACLK_ISP_400>;
+		power-domains = <&pd_isp>;
 	};
 
 	cmu_cam0: clock-controller at 120d0000 {
@@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM0_333>,
 		       <&cmu_top CLK_ACLK_CAM0_400>,
 		       <&cmu_top CLK_ACLK_CAM0_552>;
+		power-domains = <&pd_cam0>;
 	};
 
 	cmu_cam1: clock-controller at 145d0000 {
@@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
 		       <&cmu_top CLK_ACLK_CAM1_333>,
 		       <&cmu_top CLK_ACLK_CAM1_400>,
 		       <&cmu_top CLK_ACLK_CAM1_552>;
+		power-domains = <&pd_cam1>;
 	};
 
 Example 3: UART controller node that consumes the clock generated by the clock
diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
index 11343a597093..a0d88ce948d7 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,14 @@
  * Common Clock Framework support for Exynos5433 SoC.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <dt-bindings/clock/exynos5433.h>
 
@@ -1991,6 +1996,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	ENABLE_IP_FSYS1,
 };
 
+static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
+	{ MUX_SEL_FSYS0, 0 },
+	{ MUX_SEL_FSYS1, 0 },
+	{ MUX_SEL_FSYS2, 0 },
+	{ MUX_SEL_FSYS3, 0 },
+	{ MUX_SEL_FSYS4, 0 },
+};
+
 static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
 	/* PHY clocks from USBDRD30_PHY */
 	FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
@@ -2296,16 +2309,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
 	.nr_clk_ids		= FSYS_NR_CLK,
 	.clk_regs		= fsys_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(fsys_clk_regs),
+	.suspend_regs		= fsys_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(fsys_suspend_regs),
+	.clk_name		= "aclk_fsys_200",
 };
 
-static void __init exynos5433_cmu_fsys_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &fsys_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
-		exynos5433_cmu_fsys_init);
-
 /*
  * Register offset definitions for CMU_G2D
  */
@@ -2335,6 +2343,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
 };
 
+static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
+	{ MUX_SEL_G2D0, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g2d_266_user_p)		= { "oscclk", "aclk_g2d_266", };
 PNAME(mout_aclk_g2d_400_user_p)		= { "oscclk", "aclk_g2d_400", };
@@ -2420,16 +2432,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
 	.nr_clk_ids		= G2D_NR_CLK,
 	.clk_regs		= g2d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g2d_clk_regs),
+	.suspend_regs		= g2d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g2d_suspend_regs),
+	.clk_name		= "aclk_g2d_400",
 };
 
-static void __init exynos5433_cmu_g2d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g2d_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
-		exynos5433_cmu_g2d_init);
-
 /*
  * Register offset definitions for CMU_DISP
  */
@@ -2494,6 +2501,18 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	CLKOUT_CMU_DISP_DIV_STAT,
 };
 
+static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
+	/* PLL has to be enabled for suspend */
+	{ DISP_PLL_CON0, 0x85f40502 },
+	/* ignore status of external PHY muxes during suspend to avoid hangs */
+	{ MUX_IGNORE_DISP2, 0x00111111 },
+	{ MUX_SEL_DISP0, 0 },
+	{ MUX_SEL_DISP1, 0 },
+	{ MUX_SEL_DISP2, 0 },
+	{ MUX_SEL_DISP3, 0 },
+	{ MUX_SEL_DISP4, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_disp_pll_p)			= { "oscclk", "fout_disp_pll", };
 PNAME(mout_sclk_dsim1_user_p)		= { "oscclk", "sclk_dsim1_disp", };
@@ -2841,16 +2860,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
 	.nr_clk_ids		= DISP_NR_CLK,
 	.clk_regs		= disp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(disp_clk_regs),
+	.suspend_regs		= disp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(disp_suspend_regs),
+	.clk_name		= "aclk_disp_333",
 };
 
-static void __init exynos5433_cmu_disp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &disp_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
-		exynos5433_cmu_disp_init);
-
 /*
  * Register offset definitions for CMU_AUD
  */
@@ -2885,6 +2899,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	ENABLE_IP_AUD1,
 };
 
+static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
+	{ MUX_SEL_AUD0, 0 },
+	{ MUX_SEL_AUD1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aud_pll_user_aud_p)	= { "oscclk", "fout_aud_pll", };
 PNAME(mout_sclk_aud_pcm_p)	= { "mout_aud_pll_user", "ioclk_audiocdclk0",};
@@ -3011,16 +3030,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
 	.nr_clk_ids		= AUD_NR_CLK,
 	.clk_regs		= aud_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(aud_clk_regs),
+	.suspend_regs		= aud_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(aud_suspend_regs),
+	.clk_name		= "fout_aud_pll",
 };
 
-static void __init exynos5433_cmu_aud_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &aud_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
-		exynos5433_cmu_aud_init);
-
-
 /*
  * Register offset definitions for CMU_BUS{0|1|2}
  */
@@ -3222,6 +3236,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	CLK_STOPCTRL,
 };
 
+static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
+	{ MUX_SEL_G3D, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g3d_400_p)	= { "mout_g3d_pll", "aclk_g3d_400", };
 PNAME(mout_g3d_pll_p)		= { "oscclk", "fout_g3d_pll", };
@@ -3295,15 +3313,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
 	.nr_clk_ids		= G3D_NR_CLK,
 	.clk_regs		= g3d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g3d_clk_regs),
+	.suspend_regs		= g3d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g3d_suspend_regs),
+	.clk_name		= "aclk_g3d_400",
 };
 
-static void __init exynos5433_cmu_g3d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g3d_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
-		exynos5433_cmu_g3d_init);
-
 /*
  * Register offset definitions for CMU_GSCL
  */
@@ -3342,6 +3356,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
 };
 
+static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
+	{ MUX_SEL_GSCL, 0 },
+	{ ENABLE_ACLK_GSCL, 0xfff },
+	{ ENABLE_PCLK_GSCL, 0xff },
+};
+
 /* list of all parent clock list */
 PNAME(aclk_gscl_111_user_p)	= { "oscclk", "aclk_gscl_111", };
 PNAME(aclk_gscl_333_user_p)	= { "oscclk", "aclk_gscl_333", };
@@ -3436,15 +3456,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
 	.nr_clk_ids		= GSCL_NR_CLK,
 	.clk_regs		= gscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(gscl_clk_regs),
+	.suspend_regs		= gscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(gscl_suspend_regs),
+	.clk_name		= "aclk_gscl_111",
 };
 
-static void __init exynos5433_cmu_gscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &gscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
-		exynos5433_cmu_gscl_init);
-
 /*
  * Register offset definitions for CMU_APOLLO
  */
@@ -3970,6 +3986,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
 };
 
+static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
+	{ MUX_SEL_MSCL0, 0 },
+	{ MUX_SEL_MSCL1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_sclk_jpeg_user_p)		= { "oscclk", "sclk_jpeg_mscl", };
 PNAME(mout_aclk_mscl_400_user_p)	= { "oscclk", "aclk_mscl_400", };
@@ -4082,15 +4103,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
 	.nr_clk_ids		= MSCL_NR_CLK,
 	.clk_regs		= mscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mscl_clk_regs),
+	.suspend_regs		= mscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mscl_suspend_regs),
+	.clk_name		= "aclk_mscl_400",
 };
 
-static void __init exynos5433_cmu_mscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
-		exynos5433_cmu_mscl_init);
-
 /*
  * Register offset definitions for CMU_MFC
  */
@@ -4120,6 +4137,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	ENABLE_IP_MFC_SECURE_SMMU_MFC,
 };
 
+static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
+	{ MUX_SEL_MFC, 0 },
+};
+
 PNAME(mout_aclk_mfc_400_user_p)		= { "oscclk", "aclk_mfc_400", };
 
 static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
@@ -4190,15 +4211,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
 	.nr_clk_ids		= MFC_NR_CLK,
 	.clk_regs		= mfc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mfc_clk_regs),
+	.suspend_regs		= mfc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mfc_suspend_regs),
+	.clk_name		= "aclk_mfc_400",
 };
 
-static void __init exynos5433_cmu_mfc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mfc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
-		exynos5433_cmu_mfc_init);
-
 /*
  * Register offset definitions for CMU_HEVC
  */
@@ -4228,6 +4245,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
 };
 
+static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
+	{ MUX_SEL_HEVC, 0 },
+};
+
 PNAME(mout_aclk_hevc_400_user_p)	= { "oscclk", "aclk_hevc_400", };
 
 static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
@@ -4300,15 +4321,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
 	.nr_clk_ids		= HEVC_NR_CLK,
 	.clk_regs		= hevc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(hevc_clk_regs),
+	.suspend_regs		= hevc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(hevc_suspend_regs),
+	.clk_name		= "aclk_hevc_400",
 };
 
-static void __init exynos5433_cmu_hevc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &hevc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
-		exynos5433_cmu_hevc_init);
-
 /*
  * Register offset definitions for CMU_ISP
  */
@@ -4342,6 +4359,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	ENABLE_IP_ISP3,
 };
 
+static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
+	{ MUX_SEL_ISP, 0 },
+};
+
 PNAME(mout_aclk_isp_dis_400_user_p)	= { "oscclk", "aclk_isp_dis_400", };
 PNAME(mout_aclk_isp_400_user_p)		= { "oscclk", "aclk_isp_400", };
 
@@ -4553,15 +4574,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
 	.nr_clk_ids		= ISP_NR_CLK,
 	.clk_regs		= isp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(isp_clk_regs),
+	.suspend_regs		= isp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(isp_suspend_regs),
+	.clk_name		= "aclk_isp_400",
 };
 
-static void __init exynos5433_cmu_isp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &isp_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
-		exynos5433_cmu_isp_init);
-
 /*
  * Register offset definitions for CMU_CAM0
  */
@@ -4625,6 +4642,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	ENABLE_IP_CAM02,
 	ENABLE_IP_CAM03,
 };
+
+static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
+	{ MUX_SEL_CAM00, 0 },
+	{ MUX_SEL_CAM01, 0 },
+	{ MUX_SEL_CAM02, 0 },
+	{ MUX_SEL_CAM03, 0 },
+	{ MUX_SEL_CAM04, 0 },
+};
+
 PNAME(mout_aclk_cam0_333_user_p)	= { "oscclk", "aclk_cam0_333", };
 PNAME(mout_aclk_cam0_400_user_p)	= { "oscclk", "aclk_cam0_400", };
 PNAME(mout_aclk_cam0_552_user_p)	= { "oscclk", "aclk_cam0_552", };
@@ -5030,15 +5056,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
 	.nr_clk_ids		= CAM0_NR_CLK,
 	.clk_regs		= cam0_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam0_clk_regs),
+	.suspend_regs		= cam0_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam0_suspend_regs),
+	.clk_name		= "aclk_cam0_400",
 };
 
-static void __init exynos5433_cmu_cam0_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &cam0_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
-		exynos5433_cmu_cam0_init);
-
 /*
  * Register offset definitions for CMU_CAM1
  */
@@ -5085,6 +5107,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	ENABLE_IP_CAM12,
 };
 
+static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
+	{ MUX_SEL_CAM10, 0 },
+	{ MUX_SEL_CAM11, 0 },
+	{ MUX_SEL_CAM12, 0 },
+};
+
 PNAME(mout_sclk_isp_uart_user_p)	= { "oscclk", "sclk_isp_uart_cam1", };
 PNAME(mout_sclk_isp_spi1_user_p)	= { "oscclk", "sclk_isp_spi1_cam1", };
 PNAME(mout_sclk_isp_spi0_user_p)	= { "oscclk", "sclk_isp_spi0_cam1", };
@@ -5403,11 +5431,226 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
 	.nr_clk_ids		= CAM1_NR_CLK,
 	.clk_regs		= cam1_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam1_clk_regs),
+	.suspend_regs		= cam1_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam1_suspend_regs),
+	.clk_name		= "aclk_cam1_400",
+};
+
+
+struct exynos5433_cmu_data {
+	struct samsung_clk_provider ctx;
+
+	struct samsung_clk_reg_dump *clk_save;
+	unsigned int nr_clk_save;
+	const struct samsung_clk_reg_dump *clk_suspend;
+	unsigned int nr_clk_suspend;
+
+	struct clk *clk;
+	struct clk **pclks;
+	int nr_pclks;
+};
+
+static int exynos5433_cmu_suspend(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	samsung_clk_save(data->ctx.reg_base, data->clk_save,
+			 data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
+			    data->nr_clk_suspend);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable_unprepare(data->pclks[i]);
+
+	clk_disable_unprepare(data->clk);
+
+	return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	clk_prepare_enable(data->clk);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_save,
+			    data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable_unprepare(data->pclks[i]);
+
+	return 0;
+}
+
+static int __init exynos5433_cmu_probe(struct platform_device *pdev)
+{
+	const struct samsung_cmu_info *info;
+	struct exynos5433_cmu_data *data;
+	struct samsung_clk_provider *ctx;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *reg_base;
+	struct clk **clk_table;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	ctx = &data->ctx;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg_base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(reg_base)) {
+		dev_err(dev, "failed to map registers\n");
+		return PTR_ERR(reg_base);
+	}
+
+	clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
+				 GFP_KERNEL);
+	if (!clk_table)
+		return -ENOMEM;
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = info->nr_clk_ids;
+	ctx->reg_base = reg_base;
+	ctx->dev = dev;
+	spin_lock_init(&ctx->lock);
+
+	data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
+						    info->nr_clk_regs);
+	data->nr_clk_save = info->nr_clk_regs;
+	data->clk_suspend = info->suspend_regs;
+	data->nr_clk_suspend = info->nr_suspend_regs;
+	data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
+						    "#clock-cells");
+	if (data->nr_pclks > 0) {
+		data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
+					   data->nr_pclks, GFP_KERNEL);
+
+		for (i = 0; i < data->nr_pclks; i++) {
+			struct clk *clk = of_clk_get(dev->of_node, i);
+
+			if (IS_ERR(clk))
+				return PTR_ERR(clk);
+			data->pclks[i] = clk;
+		}
+	}
+
+	if (info->clk_name)
+		data->clk = clk_get(dev, info->clk_name);
+	clk_prepare_enable(data->clk);
+
+	platform_set_drvdata(pdev, data);
+
+	/*
+	 * Enable runtime pm here, so clock core with use runtime pm for all
+	 * registered clocks. Additionally we increase the runtime PM usage
+	 * count during the clock registration, to avoid the clock core from
+	 * runtime suspending the device.
+	 */
+	pm_runtime_get_noresume(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	if (info->pll_clks)
+		samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
+					 reg_base);
+	if (info->mux_clks)
+		samsung_clk_register_mux(ctx, info->mux_clks,
+					 info->nr_mux_clks);
+	if (info->div_clks)
+		samsung_clk_register_div(ctx, info->div_clks,
+					 info->nr_div_clks);
+	if (info->gate_clks)
+		samsung_clk_register_gate(ctx, info->gate_clks,
+					  info->nr_gate_clks);
+	if (info->fixed_clks)
+		samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
+						info->nr_fixed_clks);
+	if (info->fixed_factor_clks)
+		samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
+						  info->nr_fixed_factor_clks);
+
+	samsung_clk_of_add_provider(dev->of_node, ctx);
+	pm_runtime_put_sync(dev);
+
+	return 0;
+}
+
+static const struct of_device_id exynos5433_cmu_of_match[] = {
+	{
+		.compatible = "samsung,exynos5433-cmu-aud",
+		.data = &aud_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam0",
+		.data = &cam0_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam1",
+		.data = &cam1_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-disp",
+		.data = &disp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g2d",
+		.data = &g2d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g3d",
+		.data = &g3d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-fsys",
+		.data = &fsys_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-gscl",
+		.data = &gscl_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mfc",
+		.data = &mfc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-hevc",
+		.data = &hevc_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-isp",
+		.data = &isp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-mscl",
+		.data = &mscl_cmu_info,
+	}, {
+	},
+};
+
+static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
+	SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
+};
+
+static struct platform_driver exynos5433_cmu_driver __refdata = {
+	.driver	= {
+		.name = "exynos5433-cmu",
+		.of_match_table = exynos5433_cmu_of_match,
+		.suppress_bind_attrs = true,
+		.pm = &exynos5433_cmu_pm_ops,
+	},
+	.probe = exynos5433_cmu_probe,
 };
 
-static void __init exynos5433_cmu_cam1_init(struct device_node *np)
+static int __init exynos5433_cmu_init(void)
 {
-	samsung_cmu_register_one(np, &cam1_cmu_info);
+	return platform_driver_register(&exynos5433_cmu_driver);
 }
-CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
-		exynos5433_cmu_cam1_init);
+core_initcall(exynos5433_cmu_init);
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index 9263d8a27c6b..664020cb4794 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -354,6 +354,12 @@ struct samsung_cmu_info {
 	/* list and number of clocks registers */
 	const unsigned long *clk_regs;
 	unsigned int nr_clk_regs;
+
+	/* list and number of clocks registers to set before suspend */
+	const struct samsung_clk_reg_dump *suspend_regs;
+	unsigned int nr_suspend_regs;
+	/* name of the parent clock needed for CMU register access */
+	const char *clk_name;
 };
 
 extern struct samsung_clk_provider *__init samsung_clk_init(
-- 
1.9.1

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

* [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
       [not found]   ` <CGME20170322113556eucas1p1b3434935701aba1db7868415a9c3b4f9@eucas1p1.samsung.com>
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Marek Szyprowski, Stephen Boyd, Michael Turquette, Ulf Hansson,
	Sylwester Nawrocki, Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

This patch adds support for runtime PM to Exynos Audio SubSystem driver to
enable full support for audio power domain on Exynos5 SoCs. The main change
is moving register saving and restoring code from system sleep PM ops to
runtime PM ops and implementing system sleep PM ops with generic
pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
device is managed from clock core depending on the preparation status
of the provided clocks.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 ++
 drivers/clk/samsung/clk-exynos-audss.c             | 68 +++++++++++++---------
 2 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
index 0c3d6015868d..f3635d5aeba4 100644
--- a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
+++ b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
@@ -33,6 +33,12 @@ Required Properties:
 - clock-names: Aliases for the above clocks. They should be "pll_ref",
   "pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
 
+Optional Properties:
+
+  - power-domains: a phandle to respective power domain node as described by
+    generic PM domain bindings (see power/power_domain.txt for more
+    information).
+
 The following is the list of clocks generated by the controller. Each clock is
 assigned an identifier and client nodes use this identifier to specify the
 clock which they consume. Some of the clocks are available only on a particular
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index cb7df358a27d..2af07e159538 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -18,6 +18,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <dt-bindings/clock/exynos-audss-clk.h>
 
@@ -37,14 +38,13 @@
 #define ASS_CLK_DIV 0x4
 #define ASS_CLK_GATE 0x8
 
-#ifdef CONFIG_PM_SLEEP
 static unsigned long reg_save[][2] = {
 	{ ASS_CLK_SRC,  0 },
 	{ ASS_CLK_DIV,  0 },
 	{ ASS_CLK_GATE, 0 },
 };
 
-static int exynos_audss_clk_suspend(struct device *dev)
+static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
 {
 	int i;
 
@@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
 	return 0;
 }
 
-static int exynos_audss_clk_resume(struct device *dev)
+static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
 {
 	int i;
 
@@ -63,7 +63,6 @@ static int exynos_audss_clk_resume(struct device *dev)
 
 	return 0;
 }
-#endif /* CONFIG_PM_SLEEP */
 
 struct exynos_audss_clk_drvdata {
 	unsigned int has_adma_clk:1;
@@ -134,6 +133,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
 	const struct exynos_audss_clk_drvdata *variant;
 	struct resource *res;
+	struct device *dev = &pdev->dev;
 	int i, ret = 0;
 
 	variant = of_device_get_match_data(&pdev->dev);
@@ -141,15 +141,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	reg_base = devm_ioremap_resource(&pdev->dev, res);
+	reg_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(reg_base)) {
-		dev_err(&pdev->dev, "failed to map audss registers\n");
+		dev_err(dev, "failed to map audss registers\n");
 		return PTR_ERR(reg_base);
 	}
 
 	epll = ERR_PTR(-ENODEV);
 
-	clk_table = devm_kzalloc(&pdev->dev,
+	clk_table = devm_kzalloc(dev,
 				sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
 				GFP_KERNEL);
 	if (!clk_table)
@@ -158,8 +158,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	clk_data.clks = clk_table;
 	clk_data.clk_num = variant->num_clks;
 
-	pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
-	pll_in = devm_clk_get(&pdev->dev, "pll_in");
+	pll_ref = devm_clk_get(dev, "pll_ref");
+	pll_in = devm_clk_get(dev, "pll_in");
 	if (!IS_ERR(pll_ref))
 		mout_audss_p[0] = __clk_get_name(pll_ref);
 	if (!IS_ERR(pll_in)) {
@@ -170,81 +170,89 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 
 			ret = clk_prepare_enable(epll);
 			if (ret) {
-				dev_err(&pdev->dev,
+				dev_err(dev,
 					"failed to prepare the epll clock\n");
 				return ret;
 			}
 		}
 	}
-	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
+
+	/*
+	 * Enable runtime PM here, so clock core with use runtime PM for all
+	 * registered clocks.
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(dev, "mout_audss",
 				mout_audss_p, ARRAY_SIZE(mout_audss_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
 
-	cdclk = devm_clk_get(&pdev->dev, "cdclk");
-	sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
+	cdclk = devm_clk_get(dev, "cdclk");
+	sclk_audio = devm_clk_get(dev, "sclk_audio");
 	if (!IS_ERR(cdclk))
 		mout_i2s_p[1] = __clk_get_name(cdclk);
 	if (!IS_ERR(sclk_audio))
 		mout_i2s_p[2] = __clk_get_name(sclk_audio);
-	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
+	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(dev, "mout_i2s",
 				mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp",
+	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(dev, "dout_srp",
 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 				0, &lock);
 
-	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL,
+	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(dev,
 				"dout_aud_bus", "dout_srp", 0,
 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s",
+	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(dev, "dout_i2s",
 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
 				&lock);
 
-	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk",
+	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(dev, "srp_clk",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 0, 0, &lock);
 
-	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus",
+	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(dev, "i2s_bus",
 				"dout_aud_bus", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 2, 0, &lock);
 
-	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s",
+	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(dev, "sclk_i2s",
 				"dout_i2s", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 3, 0, &lock);
 
-	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus",
+	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(dev, "pcm_bus",
 				 "sclk_pcm", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 4, 0, &lock);
 
-	sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
+	sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
 	if (!IS_ERR(sclk_pcm_in))
 		sclk_pcm_p = __clk_get_name(sclk_pcm_in);
-	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
+	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(dev, "sclk_pcm",
 				sclk_pcm_p, CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 5, 0, &lock);
 
 	if (variant->has_adma_clk) {
-		clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
+		clk_table[EXYNOS_ADMA] = clk_register_gate(dev, "adma",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 9, 0, &lock);
 	}
 
 	for (i = 0; i < clk_data.clk_num; i++) {
 		if (IS_ERR(clk_table[i])) {
-			dev_err(&pdev->dev, "failed to register clock %d\n", i);
+			dev_err(dev, "failed to register clock %d\n", i);
 			ret = PTR_ERR(clk_table[i]);
 			goto unregister;
 		}
 	}
 
-	ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
 					&clk_data);
 	if (ret) {
-		dev_err(&pdev->dev, "failed to add clock provider\n");
+		dev_err(dev, "failed to add clock provider\n");
 		goto unregister;
 	}
 
@@ -272,8 +280,10 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
-	SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
-				     exynos_audss_clk_resume)
+	SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
 };
 
 static struct platform_driver exynos_audss_clk_driver = {
-- 
1.9.1

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

* [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Ulf Hansson, Bartlomiej Zolnierkiewicz, Michael Turquette,
	Stephen Boyd, Krzysztof Kozlowski, Inki Dae, Chanwoo Choi,
	Sylwester Nawrocki, Marek Szyprowski

This patch adds support for runtime PM to Exynos Audio SubSystem driver to
enable full support for audio power domain on Exynos5 SoCs. The main change
is moving register saving and restoring code from system sleep PM ops to
runtime PM ops and implementing system sleep PM ops with generic
pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
device is managed from clock core depending on the preparation status
of the provided clocks.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 ++
 drivers/clk/samsung/clk-exynos-audss.c             | 68 +++++++++++++---------
 2 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
index 0c3d6015868d..f3635d5aeba4 100644
--- a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
+++ b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
@@ -33,6 +33,12 @@ Required Properties:
 - clock-names: Aliases for the above clocks. They should be "pll_ref",
   "pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
 
+Optional Properties:
+
+  - power-domains: a phandle to respective power domain node as described by
+    generic PM domain bindings (see power/power_domain.txt for more
+    information).
+
 The following is the list of clocks generated by the controller. Each clock is
 assigned an identifier and client nodes use this identifier to specify the
 clock which they consume. Some of the clocks are available only on a particular
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index cb7df358a27d..2af07e159538 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -18,6 +18,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <dt-bindings/clock/exynos-audss-clk.h>
 
@@ -37,14 +38,13 @@
 #define ASS_CLK_DIV 0x4
 #define ASS_CLK_GATE 0x8
 
-#ifdef CONFIG_PM_SLEEP
 static unsigned long reg_save[][2] = {
 	{ ASS_CLK_SRC,  0 },
 	{ ASS_CLK_DIV,  0 },
 	{ ASS_CLK_GATE, 0 },
 };
 
-static int exynos_audss_clk_suspend(struct device *dev)
+static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
 {
 	int i;
 
@@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
 	return 0;
 }
 
-static int exynos_audss_clk_resume(struct device *dev)
+static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
 {
 	int i;
 
@@ -63,7 +63,6 @@ static int exynos_audss_clk_resume(struct device *dev)
 
 	return 0;
 }
-#endif /* CONFIG_PM_SLEEP */
 
 struct exynos_audss_clk_drvdata {
 	unsigned int has_adma_clk:1;
@@ -134,6 +133,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
 	const struct exynos_audss_clk_drvdata *variant;
 	struct resource *res;
+	struct device *dev = &pdev->dev;
 	int i, ret = 0;
 
 	variant = of_device_get_match_data(&pdev->dev);
@@ -141,15 +141,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	reg_base = devm_ioremap_resource(&pdev->dev, res);
+	reg_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(reg_base)) {
-		dev_err(&pdev->dev, "failed to map audss registers\n");
+		dev_err(dev, "failed to map audss registers\n");
 		return PTR_ERR(reg_base);
 	}
 
 	epll = ERR_PTR(-ENODEV);
 
-	clk_table = devm_kzalloc(&pdev->dev,
+	clk_table = devm_kzalloc(dev,
 				sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
 				GFP_KERNEL);
 	if (!clk_table)
@@ -158,8 +158,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	clk_data.clks = clk_table;
 	clk_data.clk_num = variant->num_clks;
 
-	pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
-	pll_in = devm_clk_get(&pdev->dev, "pll_in");
+	pll_ref = devm_clk_get(dev, "pll_ref");
+	pll_in = devm_clk_get(dev, "pll_in");
 	if (!IS_ERR(pll_ref))
 		mout_audss_p[0] = __clk_get_name(pll_ref);
 	if (!IS_ERR(pll_in)) {
@@ -170,81 +170,89 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 
 			ret = clk_prepare_enable(epll);
 			if (ret) {
-				dev_err(&pdev->dev,
+				dev_err(dev,
 					"failed to prepare the epll clock\n");
 				return ret;
 			}
 		}
 	}
-	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
+
+	/*
+	 * Enable runtime PM here, so clock core with use runtime PM for all
+	 * registered clocks.
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(dev, "mout_audss",
 				mout_audss_p, ARRAY_SIZE(mout_audss_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
 
-	cdclk = devm_clk_get(&pdev->dev, "cdclk");
-	sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
+	cdclk = devm_clk_get(dev, "cdclk");
+	sclk_audio = devm_clk_get(dev, "sclk_audio");
 	if (!IS_ERR(cdclk))
 		mout_i2s_p[1] = __clk_get_name(cdclk);
 	if (!IS_ERR(sclk_audio))
 		mout_i2s_p[2] = __clk_get_name(sclk_audio);
-	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
+	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(dev, "mout_i2s",
 				mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp",
+	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(dev, "dout_srp",
 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 				0, &lock);
 
-	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL,
+	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(dev,
 				"dout_aud_bus", "dout_srp", 0,
 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s",
+	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(dev, "dout_i2s",
 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
 				&lock);
 
-	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk",
+	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(dev, "srp_clk",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 0, 0, &lock);
 
-	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus",
+	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(dev, "i2s_bus",
 				"dout_aud_bus", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 2, 0, &lock);
 
-	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s",
+	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(dev, "sclk_i2s",
 				"dout_i2s", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 3, 0, &lock);
 
-	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus",
+	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(dev, "pcm_bus",
 				 "sclk_pcm", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 4, 0, &lock);
 
-	sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
+	sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
 	if (!IS_ERR(sclk_pcm_in))
 		sclk_pcm_p = __clk_get_name(sclk_pcm_in);
-	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
+	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(dev, "sclk_pcm",
 				sclk_pcm_p, CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 5, 0, &lock);
 
 	if (variant->has_adma_clk) {
-		clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
+		clk_table[EXYNOS_ADMA] = clk_register_gate(dev, "adma",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 9, 0, &lock);
 	}
 
 	for (i = 0; i < clk_data.clk_num; i++) {
 		if (IS_ERR(clk_table[i])) {
-			dev_err(&pdev->dev, "failed to register clock %d\n", i);
+			dev_err(dev, "failed to register clock %d\n", i);
 			ret = PTR_ERR(clk_table[i]);
 			goto unregister;
 		}
 	}
 
-	ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
 					&clk_data);
 	if (ret) {
-		dev_err(&pdev->dev, "failed to add clock provider\n");
+		dev_err(dev, "failed to add clock provider\n");
 		goto unregister;
 	}
 
@@ -272,8 +280,10 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
-	SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
-				     exynos_audss_clk_resume)
+	SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
 };
 
 static struct platform_driver exynos_audss_clk_driver = {
-- 
1.9.1

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

* [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
@ 2017-03-22 11:35       ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 11:35 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds support for runtime PM to Exynos Audio SubSystem driver to
enable full support for audio power domain on Exynos5 SoCs. The main change
is moving register saving and restoring code from system sleep PM ops to
runtime PM ops and implementing system sleep PM ops with generic
pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
device is managed from clock core depending on the preparation status
of the provided clocks.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 ++
 drivers/clk/samsung/clk-exynos-audss.c             | 68 +++++++++++++---------
 2 files changed, 45 insertions(+), 29 deletions(-)

diff --git a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
index 0c3d6015868d..f3635d5aeba4 100644
--- a/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
+++ b/Documentation/devicetree/bindings/clock/clk-exynos-audss.txt
@@ -33,6 +33,12 @@ Required Properties:
 - clock-names: Aliases for the above clocks. They should be "pll_ref",
   "pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
 
+Optional Properties:
+
+  - power-domains: a phandle to respective power domain node as described by
+    generic PM domain bindings (see power/power_domain.txt for more
+    information).
+
 The following is the list of clocks generated by the controller. Each clock is
 assigned an identifier and client nodes use this identifier to specify the
 clock which they consume. Some of the clocks are available only on a particular
diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index cb7df358a27d..2af07e159538 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -18,6 +18,7 @@
 #include <linux/syscore_ops.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 
 #include <dt-bindings/clock/exynos-audss-clk.h>
 
@@ -37,14 +38,13 @@
 #define ASS_CLK_DIV 0x4
 #define ASS_CLK_GATE 0x8
 
-#ifdef CONFIG_PM_SLEEP
 static unsigned long reg_save[][2] = {
 	{ ASS_CLK_SRC,  0 },
 	{ ASS_CLK_DIV,  0 },
 	{ ASS_CLK_GATE, 0 },
 };
 
-static int exynos_audss_clk_suspend(struct device *dev)
+static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
 {
 	int i;
 
@@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
 	return 0;
 }
 
-static int exynos_audss_clk_resume(struct device *dev)
+static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
 {
 	int i;
 
@@ -63,7 +63,6 @@ static int exynos_audss_clk_resume(struct device *dev)
 
 	return 0;
 }
-#endif /* CONFIG_PM_SLEEP */
 
 struct exynos_audss_clk_drvdata {
 	unsigned int has_adma_clk:1;
@@ -134,6 +133,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
 	const struct exynos_audss_clk_drvdata *variant;
 	struct resource *res;
+	struct device *dev = &pdev->dev;
 	int i, ret = 0;
 
 	variant = of_device_get_match_data(&pdev->dev);
@@ -141,15 +141,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		return -EINVAL;
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	reg_base = devm_ioremap_resource(&pdev->dev, res);
+	reg_base = devm_ioremap_resource(dev, res);
 	if (IS_ERR(reg_base)) {
-		dev_err(&pdev->dev, "failed to map audss registers\n");
+		dev_err(dev, "failed to map audss registers\n");
 		return PTR_ERR(reg_base);
 	}
 
 	epll = ERR_PTR(-ENODEV);
 
-	clk_table = devm_kzalloc(&pdev->dev,
+	clk_table = devm_kzalloc(dev,
 				sizeof(struct clk *) * EXYNOS_AUDSS_MAX_CLKS,
 				GFP_KERNEL);
 	if (!clk_table)
@@ -158,8 +158,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	clk_data.clks = clk_table;
 	clk_data.clk_num = variant->num_clks;
 
-	pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
-	pll_in = devm_clk_get(&pdev->dev, "pll_in");
+	pll_ref = devm_clk_get(dev, "pll_ref");
+	pll_in = devm_clk_get(dev, "pll_in");
 	if (!IS_ERR(pll_ref))
 		mout_audss_p[0] = __clk_get_name(pll_ref);
 	if (!IS_ERR(pll_in)) {
@@ -170,81 +170,89 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 
 			ret = clk_prepare_enable(epll);
 			if (ret) {
-				dev_err(&pdev->dev,
+				dev_err(dev,
 					"failed to prepare the epll clock\n");
 				return ret;
 			}
 		}
 	}
-	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(NULL, "mout_audss",
+
+	/*
+	 * Enable runtime PM here, so clock core with use runtime PM for all
+	 * registered clocks.
+	 */
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	clk_table[EXYNOS_MOUT_AUDSS] = clk_register_mux(dev, "mout_audss",
 				mout_audss_p, ARRAY_SIZE(mout_audss_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
 
-	cdclk = devm_clk_get(&pdev->dev, "cdclk");
-	sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
+	cdclk = devm_clk_get(dev, "cdclk");
+	sclk_audio = devm_clk_get(dev, "sclk_audio");
 	if (!IS_ERR(cdclk))
 		mout_i2s_p[1] = __clk_get_name(cdclk);
 	if (!IS_ERR(sclk_audio))
 		mout_i2s_p[2] = __clk_get_name(sclk_audio);
-	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(NULL, "mout_i2s",
+	clk_table[EXYNOS_MOUT_I2S] = clk_register_mux(dev, "mout_i2s",
 				mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
 				CLK_SET_RATE_NO_REPARENT,
 				reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(NULL, "dout_srp",
+	clk_table[EXYNOS_DOUT_SRP] = clk_register_divider(dev, "dout_srp",
 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 				0, &lock);
 
-	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(NULL,
+	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_register_divider(dev,
 				"dout_aud_bus", "dout_srp", 0,
 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(NULL, "dout_i2s",
+	clk_table[EXYNOS_DOUT_I2S] = clk_register_divider(dev, "dout_i2s",
 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
 				&lock);
 
-	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(NULL, "srp_clk",
+	clk_table[EXYNOS_SRP_CLK] = clk_register_gate(dev, "srp_clk",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 0, 0, &lock);
 
-	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(NULL, "i2s_bus",
+	clk_table[EXYNOS_I2S_BUS] = clk_register_gate(dev, "i2s_bus",
 				"dout_aud_bus", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 2, 0, &lock);
 
-	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(NULL, "sclk_i2s",
+	clk_table[EXYNOS_SCLK_I2S] = clk_register_gate(dev, "sclk_i2s",
 				"dout_i2s", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 3, 0, &lock);
 
-	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(NULL, "pcm_bus",
+	clk_table[EXYNOS_PCM_BUS] = clk_register_gate(dev, "pcm_bus",
 				 "sclk_pcm", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 4, 0, &lock);
 
-	sclk_pcm_in = devm_clk_get(&pdev->dev, "sclk_pcm_in");
+	sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
 	if (!IS_ERR(sclk_pcm_in))
 		sclk_pcm_p = __clk_get_name(sclk_pcm_in);
-	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(NULL, "sclk_pcm",
+	clk_table[EXYNOS_SCLK_PCM] = clk_register_gate(dev, "sclk_pcm",
 				sclk_pcm_p, CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 5, 0, &lock);
 
 	if (variant->has_adma_clk) {
-		clk_table[EXYNOS_ADMA] = clk_register_gate(NULL, "adma",
+		clk_table[EXYNOS_ADMA] = clk_register_gate(dev, "adma",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 9, 0, &lock);
 	}
 
 	for (i = 0; i < clk_data.clk_num; i++) {
 		if (IS_ERR(clk_table[i])) {
-			dev_err(&pdev->dev, "failed to register clock %d\n", i);
+			dev_err(dev, "failed to register clock %d\n", i);
 			ret = PTR_ERR(clk_table[i]);
 			goto unregister;
 		}
 	}
 
-	ret = of_clk_add_provider(pdev->dev.of_node, of_clk_src_onecell_get,
+	ret = of_clk_add_provider(dev->of_node, of_clk_src_onecell_get,
 					&clk_data);
 	if (ret) {
-		dev_err(&pdev->dev, "failed to add clock provider\n");
+		dev_err(dev, "failed to add clock provider\n");
 		goto unregister;
 	}
 
@@ -272,8 +280,10 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
 }
 
 static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
-	SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
-				     exynos_audss_clk_resume)
+	SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
+			   NULL)
+	SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+				     pm_runtime_force_resume)
 };
 
 static struct platform_driver exynos_audss_clk_driver = {
-- 
1.9.1

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

* Re: [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
  2017-03-22 11:35       ` Marek Szyprowski
  (?)
@ 2017-03-22 11:52         ` Ulf Hansson
  -1 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-22 11:52 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel,
	Stephen Boyd, Michael Turquette, Sylwester Nawrocki,
	Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> Add runtime pm support for all clock controller units (CMU), which belongs
> to power domains and require special handling during on/off operations.
> Typically special values has to be written to MUX registers to change
> internal clocks parents to OSC clock before turning power off. During such
> operation all clocks, which enters CMU has to be enabled to let MUX to
> stabilize. Also for each CMU there is one special parent clock, which has
> to be enabled all the time when any access to CMU registers is done.
>
> This patch solves most of the mysterious external abort and freeze issues
> caused by a lack of proper parent CMU clock enabled or incorrect turn off
> procedure.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Kind regards
Uffe

> ---
>  .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
>  drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
>  drivers/clk/samsung/clk.h                          |   6 +
>  3 files changed, 350 insertions(+), 85 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> index 1dc80f8811fe..5c7dd12e667a 100644
> --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> @@ -168,6 +168,11 @@ Required Properties:
>                 - aclk_cam1_400
>                 - aclk_cam1_552
>
> +Optional properties:
> +  - power-domains: a phandle to respective power domain node as described by
> +       generic PM domain bindings (see power/power_domain.txt for more
> +       information).
> +
>  Each clock is assigned an identifier and client nodes can use this identifier
>  to specify the clock which they consume.
>
> @@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_ACLK_G2D_266>,
>                        <&cmu_top CLK_ACLK_G2D_400>;
> +               power-domains = <&pd_g2d>;
>         };
>
>         cmu_disp: clock-controller@13b90000 {
> @@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
>                        <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
>                        <&cmu_mif CLK_ACLK_DISP_333>;
> +               power-domains = <&pd_disp>;
>         };
>
>         cmu_aud: clock-controller@114c0000 {
> @@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "fout_aud_pll";
>                 clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
> +               power-domains = <&pd_aud>;
>         };
>
>         cmu_bus0: clock-controller@13600000 {
> @@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_g3d_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
> +               power-domains = <&pd_g3d>;
>         };
>
>         cmu_gscl: clock-controller@13cf0000 {
> @@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                         <&cmu_top CLK_ACLK_GSCL_111>,
>                         <&cmu_top CLK_ACLK_GSCL_333>;
> +               power-domains = <&pd_gscl>;
>         };
>
>         cmu_apollo: clock-controller@11900000 {
> @@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_SCLK_JPEG_MSCL>,
>                        <&cmu_top CLK_ACLK_MSCL_400>;
> +               power-domains = <&pd_mscl>;
>         };
>
>         cmu_mfc: clock-controller@15280000 {
> @@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_mfc_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
> +               power-domains = <&pd_mfc>;
>         };
>
>         cmu_hevc: clock-controller@14f80000 {
> @@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_hevc_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
> +               power-domains = <&pd_hevc>;
>         };
>
>         cmu_isp: clock-controller@146d0000 {
> @@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_ACLK_ISP_DIS_400>,
>                        <&cmu_top CLK_ACLK_ISP_400>;
> +               power-domains = <&pd_isp>;
>         };
>
>         cmu_cam0: clock-controller@120d0000 {
> @@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_top CLK_ACLK_CAM0_333>,
>                        <&cmu_top CLK_ACLK_CAM0_400>,
>                        <&cmu_top CLK_ACLK_CAM0_552>;
> +               power-domains = <&pd_cam0>;
>         };
>
>         cmu_cam1: clock-controller@145d0000 {
> @@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_top CLK_ACLK_CAM1_333>,
>                        <&cmu_top CLK_ACLK_CAM1_400>,
>                        <&cmu_top CLK_ACLK_CAM1_552>;
> +               power-domains = <&pd_cam1>;
>         };
>
>  Example 3: UART controller node that consumes the clock generated by the clock
> diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
> index 11343a597093..a0d88ce948d7 100644
> --- a/drivers/clk/samsung/clk-exynos5433.c
> +++ b/drivers/clk/samsung/clk-exynos5433.c
> @@ -9,9 +9,14 @@
>   * Common Clock Framework support for Exynos5433 SoC.
>   */
>
> +#include <linux/clk.h>
>  #include <linux/clk-provider.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
>
>  #include <dt-bindings/clock/exynos5433.h>
>
> @@ -1991,6 +1996,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
>         ENABLE_IP_FSYS1,
>  };
>
> +static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
> +       { MUX_SEL_FSYS0, 0 },
> +       { MUX_SEL_FSYS1, 0 },
> +       { MUX_SEL_FSYS2, 0 },
> +       { MUX_SEL_FSYS3, 0 },
> +       { MUX_SEL_FSYS4, 0 },
> +};
> +
>  static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
>         /* PHY clocks from USBDRD30_PHY */
>         FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
> @@ -2296,16 +2309,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
>         .nr_clk_ids             = FSYS_NR_CLK,
>         .clk_regs               = fsys_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(fsys_clk_regs),
> +       .suspend_regs           = fsys_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(fsys_suspend_regs),
> +       .clk_name               = "aclk_fsys_200",
>  };
>
> -static void __init exynos5433_cmu_fsys_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &fsys_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
> -               exynos5433_cmu_fsys_init);
> -
>  /*
>   * Register offset definitions for CMU_G2D
>   */
> @@ -2335,6 +2343,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
>         DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
>  };
>
> +static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
> +       { MUX_SEL_G2D0, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aclk_g2d_266_user_p)                = { "oscclk", "aclk_g2d_266", };
>  PNAME(mout_aclk_g2d_400_user_p)                = { "oscclk", "aclk_g2d_400", };
> @@ -2420,16 +2432,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
>         .nr_clk_ids             = G2D_NR_CLK,
>         .clk_regs               = g2d_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(g2d_clk_regs),
> +       .suspend_regs           = g2d_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(g2d_suspend_regs),
> +       .clk_name               = "aclk_g2d_400",
>  };
>
> -static void __init exynos5433_cmu_g2d_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &g2d_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
> -               exynos5433_cmu_g2d_init);
> -
>  /*
>   * Register offset definitions for CMU_DISP
>   */
> @@ -2494,6 +2501,18 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
>         CLKOUT_CMU_DISP_DIV_STAT,
>  };
>
> +static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
> +       /* PLL has to be enabled for suspend */
> +       { DISP_PLL_CON0, 0x85f40502 },
> +       /* ignore status of external PHY muxes during suspend to avoid hangs */
> +       { MUX_IGNORE_DISP2, 0x00111111 },
> +       { MUX_SEL_DISP0, 0 },
> +       { MUX_SEL_DISP1, 0 },
> +       { MUX_SEL_DISP2, 0 },
> +       { MUX_SEL_DISP3, 0 },
> +       { MUX_SEL_DISP4, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_disp_pll_p)                 = { "oscclk", "fout_disp_pll", };
>  PNAME(mout_sclk_dsim1_user_p)          = { "oscclk", "sclk_dsim1_disp", };
> @@ -2841,16 +2860,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
>         .nr_clk_ids             = DISP_NR_CLK,
>         .clk_regs               = disp_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(disp_clk_regs),
> +       .suspend_regs           = disp_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(disp_suspend_regs),
> +       .clk_name               = "aclk_disp_333",
>  };
>
> -static void __init exynos5433_cmu_disp_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &disp_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
> -               exynos5433_cmu_disp_init);
> -
>  /*
>   * Register offset definitions for CMU_AUD
>   */
> @@ -2885,6 +2899,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
>         ENABLE_IP_AUD1,
>  };
>
> +static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
> +       { MUX_SEL_AUD0, 0 },
> +       { MUX_SEL_AUD1, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", };
>  PNAME(mout_sclk_aud_pcm_p)     = { "mout_aud_pll_user", "ioclk_audiocdclk0",};
> @@ -3011,16 +3030,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
>         .nr_clk_ids             = AUD_NR_CLK,
>         .clk_regs               = aud_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(aud_clk_regs),
> +       .suspend_regs           = aud_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(aud_suspend_regs),
> +       .clk_name               = "fout_aud_pll",
>  };
>
> -static void __init exynos5433_cmu_aud_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &aud_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
> -               exynos5433_cmu_aud_init);
> -
> -
>  /*
>   * Register offset definitions for CMU_BUS{0|1|2}
>   */
> @@ -3222,6 +3236,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
>         CLK_STOPCTRL,
>  };
>
> +static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
> +       { MUX_SEL_G3D, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aclk_g3d_400_p)     = { "mout_g3d_pll", "aclk_g3d_400", };
>  PNAME(mout_g3d_pll_p)          = { "oscclk", "fout_g3d_pll", };
> @@ -3295,15 +3313,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
>         .nr_clk_ids             = G3D_NR_CLK,
>         .clk_regs               = g3d_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(g3d_clk_regs),
> +       .suspend_regs           = g3d_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(g3d_suspend_regs),
> +       .clk_name               = "aclk_g3d_400",
>  };
>
> -static void __init exynos5433_cmu_g3d_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &g3d_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
> -               exynos5433_cmu_g3d_init);
> -
>  /*
>   * Register offset definitions for CMU_GSCL
>   */
> @@ -3342,6 +3356,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
>         ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
>  };
>
> +static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
> +       { MUX_SEL_GSCL, 0 },
> +       { ENABLE_ACLK_GSCL, 0xfff },
> +       { ENABLE_PCLK_GSCL, 0xff },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(aclk_gscl_111_user_p)    = { "oscclk", "aclk_gscl_111", };
>  PNAME(aclk_gscl_333_user_p)    = { "oscclk", "aclk_gscl_333", };
> @@ -3436,15 +3456,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
>         .nr_clk_ids             = GSCL_NR_CLK,
>         .clk_regs               = gscl_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(gscl_clk_regs),
> +       .suspend_regs           = gscl_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(gscl_suspend_regs),
> +       .clk_name               = "aclk_gscl_111",
>  };
>
> -static void __init exynos5433_cmu_gscl_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &gscl_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
> -               exynos5433_cmu_gscl_init);
> -
>  /*
>   * Register offset definitions for CMU_APOLLO
>   */
> @@ -3970,6 +3986,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
>         ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
>  };
>
> +static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
> +       { MUX_SEL_MSCL0, 0 },
> +       { MUX_SEL_MSCL1, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_sclk_jpeg_user_p)           = { "oscclk", "sclk_jpeg_mscl", };
>  PNAME(mout_aclk_mscl_400_user_p)       = { "oscclk", "aclk_mscl_400", };
> @@ -4082,15 +4103,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
>         .nr_clk_ids             = MSCL_NR_CLK,
>         .clk_regs               = mscl_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(mscl_clk_regs),
> +       .suspend_regs           = mscl_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(mscl_suspend_regs),
> +       .clk_name               = "aclk_mscl_400",
>  };
>
> -static void __init exynos5433_cmu_mscl_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &mscl_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
> -               exynos5433_cmu_mscl_init);
> -
>  /*
>   * Register offset definitions for CMU_MFC
>   */
> @@ -4120,6 +4137,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
>         ENABLE_IP_MFC_SECURE_SMMU_MFC,
>  };
>
> +static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
> +       { MUX_SEL_MFC, 0 },
> +};
> +
>  PNAME(mout_aclk_mfc_400_user_p)                = { "oscclk", "aclk_mfc_400", };
>
>  static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
> @@ -4190,15 +4211,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
>         .nr_clk_ids             = MFC_NR_CLK,
>         .clk_regs               = mfc_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(mfc_clk_regs),
> +       .suspend_regs           = mfc_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(mfc_suspend_regs),
> +       .clk_name               = "aclk_mfc_400",
>  };
>
> -static void __init exynos5433_cmu_mfc_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &mfc_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
> -               exynos5433_cmu_mfc_init);
> -
>  /*
>   * Register offset definitions for CMU_HEVC
>   */
> @@ -4228,6 +4245,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
>         ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
>  };
>
> +static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
> +       { MUX_SEL_HEVC, 0 },
> +};
> +
>  PNAME(mout_aclk_hevc_400_user_p)       = { "oscclk", "aclk_hevc_400", };
>
>  static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
> @@ -4300,15 +4321,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
>         .nr_clk_ids             = HEVC_NR_CLK,
>         .clk_regs               = hevc_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(hevc_clk_regs),
> +       .suspend_regs           = hevc_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(hevc_suspend_regs),
> +       .clk_name               = "aclk_hevc_400",
>  };
>
> -static void __init exynos5433_cmu_hevc_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &hevc_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
> -               exynos5433_cmu_hevc_init);
> -
>  /*
>   * Register offset definitions for CMU_ISP
>   */
> @@ -4342,6 +4359,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
>         ENABLE_IP_ISP3,
>  };
>
> +static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
> +       { MUX_SEL_ISP, 0 },
> +};
> +
>  PNAME(mout_aclk_isp_dis_400_user_p)    = { "oscclk", "aclk_isp_dis_400", };
>  PNAME(mout_aclk_isp_400_user_p)                = { "oscclk", "aclk_isp_400", };
>
> @@ -4553,15 +4574,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
>         .nr_clk_ids             = ISP_NR_CLK,
>         .clk_regs               = isp_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(isp_clk_regs),
> +       .suspend_regs           = isp_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(isp_suspend_regs),
> +       .clk_name               = "aclk_isp_400",
>  };
>
> -static void __init exynos5433_cmu_isp_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &isp_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
> -               exynos5433_cmu_isp_init);
> -
>  /*
>   * Register offset definitions for CMU_CAM0
>   */
> @@ -4625,6 +4642,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
>         ENABLE_IP_CAM02,
>         ENABLE_IP_CAM03,
>  };
> +
> +static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
> +       { MUX_SEL_CAM00, 0 },
> +       { MUX_SEL_CAM01, 0 },
> +       { MUX_SEL_CAM02, 0 },
> +       { MUX_SEL_CAM03, 0 },
> +       { MUX_SEL_CAM04, 0 },
> +};
> +
>  PNAME(mout_aclk_cam0_333_user_p)       = { "oscclk", "aclk_cam0_333", };
>  PNAME(mout_aclk_cam0_400_user_p)       = { "oscclk", "aclk_cam0_400", };
>  PNAME(mout_aclk_cam0_552_user_p)       = { "oscclk", "aclk_cam0_552", };
> @@ -5030,15 +5056,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
>         .nr_clk_ids             = CAM0_NR_CLK,
>         .clk_regs               = cam0_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(cam0_clk_regs),
> +       .suspend_regs           = cam0_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(cam0_suspend_regs),
> +       .clk_name               = "aclk_cam0_400",
>  };
>
> -static void __init exynos5433_cmu_cam0_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &cam0_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
> -               exynos5433_cmu_cam0_init);
> -
>  /*
>   * Register offset definitions for CMU_CAM1
>   */
> @@ -5085,6 +5107,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
>         ENABLE_IP_CAM12,
>  };
>
> +static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
> +       { MUX_SEL_CAM10, 0 },
> +       { MUX_SEL_CAM11, 0 },
> +       { MUX_SEL_CAM12, 0 },
> +};
> +
>  PNAME(mout_sclk_isp_uart_user_p)       = { "oscclk", "sclk_isp_uart_cam1", };
>  PNAME(mout_sclk_isp_spi1_user_p)       = { "oscclk", "sclk_isp_spi1_cam1", };
>  PNAME(mout_sclk_isp_spi0_user_p)       = { "oscclk", "sclk_isp_spi0_cam1", };
> @@ -5403,11 +5431,226 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
>         .nr_clk_ids             = CAM1_NR_CLK,
>         .clk_regs               = cam1_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(cam1_clk_regs),
> +       .suspend_regs           = cam1_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(cam1_suspend_regs),
> +       .clk_name               = "aclk_cam1_400",
> +};
> +
> +
> +struct exynos5433_cmu_data {
> +       struct samsung_clk_provider ctx;
> +
> +       struct samsung_clk_reg_dump *clk_save;
> +       unsigned int nr_clk_save;
> +       const struct samsung_clk_reg_dump *clk_suspend;
> +       unsigned int nr_clk_suspend;
> +
> +       struct clk *clk;
> +       struct clk **pclks;
> +       int nr_pclks;
> +};
> +
> +static int exynos5433_cmu_suspend(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
> +       int i;
> +
> +       samsung_clk_save(data->ctx.reg_base, data->clk_save,
> +                        data->nr_clk_save);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
> +                           data->nr_clk_suspend);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_disable_unprepare(data->pclks[i]);
> +
> +       clk_disable_unprepare(data->clk);
> +
> +       return 0;
> +}
> +
> +static int exynos5433_cmu_resume(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
> +       int i;
> +
> +       clk_prepare_enable(data->clk);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_save,
> +                           data->nr_clk_save);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_disable_unprepare(data->pclks[i]);
> +
> +       return 0;
> +}
> +
> +static int __init exynos5433_cmu_probe(struct platform_device *pdev)
> +{
> +       const struct samsung_cmu_info *info;
> +       struct exynos5433_cmu_data *data;
> +       struct samsung_clk_provider *ctx;
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       void __iomem *reg_base;
> +       struct clk **clk_table;
> +       int i;
> +
> +       info = of_device_get_match_data(dev);
> +
> +       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +       ctx = &data->ctx;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       reg_base = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(reg_base)) {
> +               dev_err(dev, "failed to map registers\n");
> +               return PTR_ERR(reg_base);
> +       }
> +
> +       clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
> +                                GFP_KERNEL);
> +       if (!clk_table)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < info->nr_clk_ids; ++i)
> +               clk_table[i] = ERR_PTR(-ENOENT);
> +
> +       ctx->clk_data.clks = clk_table;
> +       ctx->clk_data.clk_num = info->nr_clk_ids;
> +       ctx->reg_base = reg_base;
> +       ctx->dev = dev;
> +       spin_lock_init(&ctx->lock);
> +
> +       data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
> +                                                   info->nr_clk_regs);
> +       data->nr_clk_save = info->nr_clk_regs;
> +       data->clk_suspend = info->suspend_regs;
> +       data->nr_clk_suspend = info->nr_suspend_regs;
> +       data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
> +                                                   "#clock-cells");
> +       if (data->nr_pclks > 0) {
> +               data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
> +                                          data->nr_pclks, GFP_KERNEL);
> +
> +               for (i = 0; i < data->nr_pclks; i++) {
> +                       struct clk *clk = of_clk_get(dev->of_node, i);
> +
> +                       if (IS_ERR(clk))
> +                               return PTR_ERR(clk);
> +                       data->pclks[i] = clk;
> +               }
> +       }
> +
> +       if (info->clk_name)
> +               data->clk = clk_get(dev, info->clk_name);
> +       clk_prepare_enable(data->clk);
> +
> +       platform_set_drvdata(pdev, data);
> +
> +       /*
> +        * Enable runtime pm here, so clock core with use runtime pm for all
> +        * registered clocks. Additionally we increase the runtime PM usage
> +        * count during the clock registration, to avoid the clock core from
> +        * runtime suspending the device.
> +        */
> +       pm_runtime_get_noresume(dev);
> +       pm_runtime_set_active(dev);
> +       pm_runtime_enable(dev);
> +
> +       if (info->pll_clks)
> +               samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
> +                                        reg_base);
> +       if (info->mux_clks)
> +               samsung_clk_register_mux(ctx, info->mux_clks,
> +                                        info->nr_mux_clks);
> +       if (info->div_clks)
> +               samsung_clk_register_div(ctx, info->div_clks,
> +                                        info->nr_div_clks);
> +       if (info->gate_clks)
> +               samsung_clk_register_gate(ctx, info->gate_clks,
> +                                         info->nr_gate_clks);
> +       if (info->fixed_clks)
> +               samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
> +                                               info->nr_fixed_clks);
> +       if (info->fixed_factor_clks)
> +               samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
> +                                                 info->nr_fixed_factor_clks);
> +
> +       samsung_clk_of_add_provider(dev->of_node, ctx);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id exynos5433_cmu_of_match[] = {
> +       {
> +               .compatible = "samsung,exynos5433-cmu-aud",
> +               .data = &aud_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-cam0",
> +               .data = &cam0_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-cam1",
> +               .data = &cam1_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-disp",
> +               .data = &disp_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-g2d",
> +               .data = &g2d_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-g3d",
> +               .data = &g3d_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-fsys",
> +               .data = &fsys_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-gscl",
> +               .data = &gscl_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-mfc",
> +               .data = &mfc_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-hevc",
> +               .data = &hevc_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-isp",
> +               .data = &isp_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-mscl",
> +               .data = &mscl_cmu_info,
> +       }, {
> +       },
> +};
> +
> +static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
> +       SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
> +                          NULL)
> +       SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +                                    pm_runtime_force_resume)
> +};
> +
> +static struct platform_driver exynos5433_cmu_driver __refdata = {
> +       .driver = {
> +               .name = "exynos5433-cmu",
> +               .of_match_table = exynos5433_cmu_of_match,
> +               .suppress_bind_attrs = true,
> +               .pm = &exynos5433_cmu_pm_ops,
> +       },
> +       .probe = exynos5433_cmu_probe,
>  };
>
> -static void __init exynos5433_cmu_cam1_init(struct device_node *np)
> +static int __init exynos5433_cmu_init(void)
>  {
> -       samsung_cmu_register_one(np, &cam1_cmu_info);
> +       return platform_driver_register(&exynos5433_cmu_driver);
>  }
> -CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
> -               exynos5433_cmu_cam1_init);
> +core_initcall(exynos5433_cmu_init);
> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
> index 9263d8a27c6b..664020cb4794 100644
> --- a/drivers/clk/samsung/clk.h
> +++ b/drivers/clk/samsung/clk.h
> @@ -354,6 +354,12 @@ struct samsung_cmu_info {
>         /* list and number of clocks registers */
>         const unsigned long *clk_regs;
>         unsigned int nr_clk_regs;
> +
> +       /* list and number of clocks registers to set before suspend */
> +       const struct samsung_clk_reg_dump *suspend_regs;
> +       unsigned int nr_suspend_regs;
> +       /* name of the parent clock needed for CMU register access */
> +       const char *clk_name;
>  };
>
>  extern struct samsung_clk_provider *__init samsung_clk_init(
> --
> 1.9.1
>

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

* Re: [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
@ 2017-03-22 11:52         ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-22 11:52 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-samsung-soc, linux-pm, Michael Turquette,
	Bartlomiej Zolnierkiewicz, Stephen Boyd, Krzysztof Kozlowski,
	Inki Dae, Chanwoo Choi, Sylwester Nawrocki, linux-clk,
	linux-arm-kernel

On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> Add runtime pm support for all clock controller units (CMU), which belongs
> to power domains and require special handling during on/off operations.
> Typically special values has to be written to MUX registers to change
> internal clocks parents to OSC clock before turning power off. During such
> operation all clocks, which enters CMU has to be enabled to let MUX to
> stabilize. Also for each CMU there is one special parent clock, which has
> to be enabled all the time when any access to CMU registers is done.
>
> This patch solves most of the mysterious external abort and freeze issues
> caused by a lack of proper parent CMU clock enabled or incorrect turn off
> procedure.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Kind regards
Uffe

> ---
>  .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
>  drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
>  drivers/clk/samsung/clk.h                          |   6 +
>  3 files changed, 350 insertions(+), 85 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> index 1dc80f8811fe..5c7dd12e667a 100644
> --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> @@ -168,6 +168,11 @@ Required Properties:
>                 - aclk_cam1_400
>                 - aclk_cam1_552
>
> +Optional properties:
> +  - power-domains: a phandle to respective power domain node as described by
> +       generic PM domain bindings (see power/power_domain.txt for more
> +       information).
> +
>  Each clock is assigned an identifier and client nodes can use this identifier
>  to specify the clock which they consume.
>
> @@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_ACLK_G2D_266>,
>                        <&cmu_top CLK_ACLK_G2D_400>;
> +               power-domains = <&pd_g2d>;
>         };
>
>         cmu_disp: clock-controller@13b90000 {
> @@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
>                        <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
>                        <&cmu_mif CLK_ACLK_DISP_333>;
> +               power-domains = <&pd_disp>;
>         };
>
>         cmu_aud: clock-controller@114c0000 {
> @@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "fout_aud_pll";
>                 clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
> +               power-domains = <&pd_aud>;
>         };
>
>         cmu_bus0: clock-controller@13600000 {
> @@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_g3d_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
> +               power-domains = <&pd_g3d>;
>         };
>
>         cmu_gscl: clock-controller@13cf0000 {
> @@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                         <&cmu_top CLK_ACLK_GSCL_111>,
>                         <&cmu_top CLK_ACLK_GSCL_333>;
> +               power-domains = <&pd_gscl>;
>         };
>
>         cmu_apollo: clock-controller@11900000 {
> @@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_SCLK_JPEG_MSCL>,
>                        <&cmu_top CLK_ACLK_MSCL_400>;
> +               power-domains = <&pd_mscl>;
>         };
>
>         cmu_mfc: clock-controller@15280000 {
> @@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_mfc_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
> +               power-domains = <&pd_mfc>;
>         };
>
>         cmu_hevc: clock-controller@14f80000 {
> @@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_hevc_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
> +               power-domains = <&pd_hevc>;
>         };
>
>         cmu_isp: clock-controller@146d0000 {
> @@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_ACLK_ISP_DIS_400>,
>                        <&cmu_top CLK_ACLK_ISP_400>;
> +               power-domains = <&pd_isp>;
>         };
>
>         cmu_cam0: clock-controller@120d0000 {
> @@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_top CLK_ACLK_CAM0_333>,
>                        <&cmu_top CLK_ACLK_CAM0_400>,
>                        <&cmu_top CLK_ACLK_CAM0_552>;
> +               power-domains = <&pd_cam0>;
>         };
>
>         cmu_cam1: clock-controller@145d0000 {
> @@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_top CLK_ACLK_CAM1_333>,
>                        <&cmu_top CLK_ACLK_CAM1_400>,
>                        <&cmu_top CLK_ACLK_CAM1_552>;
> +               power-domains = <&pd_cam1>;
>         };
>
>  Example 3: UART controller node that consumes the clock generated by the clock
> diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
> index 11343a597093..a0d88ce948d7 100644
> --- a/drivers/clk/samsung/clk-exynos5433.c
> +++ b/drivers/clk/samsung/clk-exynos5433.c
> @@ -9,9 +9,14 @@
>   * Common Clock Framework support for Exynos5433 SoC.
>   */
>
> +#include <linux/clk.h>
>  #include <linux/clk-provider.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
>
>  #include <dt-bindings/clock/exynos5433.h>
>
> @@ -1991,6 +1996,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
>         ENABLE_IP_FSYS1,
>  };
>
> +static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
> +       { MUX_SEL_FSYS0, 0 },
> +       { MUX_SEL_FSYS1, 0 },
> +       { MUX_SEL_FSYS2, 0 },
> +       { MUX_SEL_FSYS3, 0 },
> +       { MUX_SEL_FSYS4, 0 },
> +};
> +
>  static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
>         /* PHY clocks from USBDRD30_PHY */
>         FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
> @@ -2296,16 +2309,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
>         .nr_clk_ids             = FSYS_NR_CLK,
>         .clk_regs               = fsys_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(fsys_clk_regs),
> +       .suspend_regs           = fsys_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(fsys_suspend_regs),
> +       .clk_name               = "aclk_fsys_200",
>  };
>
> -static void __init exynos5433_cmu_fsys_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &fsys_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
> -               exynos5433_cmu_fsys_init);
> -
>  /*
>   * Register offset definitions for CMU_G2D
>   */
> @@ -2335,6 +2343,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
>         DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
>  };
>
> +static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
> +       { MUX_SEL_G2D0, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aclk_g2d_266_user_p)                = { "oscclk", "aclk_g2d_266", };
>  PNAME(mout_aclk_g2d_400_user_p)                = { "oscclk", "aclk_g2d_400", };
> @@ -2420,16 +2432,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
>         .nr_clk_ids             = G2D_NR_CLK,
>         .clk_regs               = g2d_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(g2d_clk_regs),
> +       .suspend_regs           = g2d_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(g2d_suspend_regs),
> +       .clk_name               = "aclk_g2d_400",
>  };
>
> -static void __init exynos5433_cmu_g2d_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &g2d_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
> -               exynos5433_cmu_g2d_init);
> -
>  /*
>   * Register offset definitions for CMU_DISP
>   */
> @@ -2494,6 +2501,18 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
>         CLKOUT_CMU_DISP_DIV_STAT,
>  };
>
> +static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
> +       /* PLL has to be enabled for suspend */
> +       { DISP_PLL_CON0, 0x85f40502 },
> +       /* ignore status of external PHY muxes during suspend to avoid hangs */
> +       { MUX_IGNORE_DISP2, 0x00111111 },
> +       { MUX_SEL_DISP0, 0 },
> +       { MUX_SEL_DISP1, 0 },
> +       { MUX_SEL_DISP2, 0 },
> +       { MUX_SEL_DISP3, 0 },
> +       { MUX_SEL_DISP4, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_disp_pll_p)                 = { "oscclk", "fout_disp_pll", };
>  PNAME(mout_sclk_dsim1_user_p)          = { "oscclk", "sclk_dsim1_disp", };
> @@ -2841,16 +2860,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
>         .nr_clk_ids             = DISP_NR_CLK,
>         .clk_regs               = disp_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(disp_clk_regs),
> +       .suspend_regs           = disp_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(disp_suspend_regs),
> +       .clk_name               = "aclk_disp_333",
>  };
>
> -static void __init exynos5433_cmu_disp_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &disp_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
> -               exynos5433_cmu_disp_init);
> -
>  /*
>   * Register offset definitions for CMU_AUD
>   */
> @@ -2885,6 +2899,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
>         ENABLE_IP_AUD1,
>  };
>
> +static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
> +       { MUX_SEL_AUD0, 0 },
> +       { MUX_SEL_AUD1, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", };
>  PNAME(mout_sclk_aud_pcm_p)     = { "mout_aud_pll_user", "ioclk_audiocdclk0",};
> @@ -3011,16 +3030,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
>         .nr_clk_ids             = AUD_NR_CLK,
>         .clk_regs               = aud_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(aud_clk_regs),
> +       .suspend_regs           = aud_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(aud_suspend_regs),
> +       .clk_name               = "fout_aud_pll",
>  };
>
> -static void __init exynos5433_cmu_aud_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &aud_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
> -               exynos5433_cmu_aud_init);
> -
> -
>  /*
>   * Register offset definitions for CMU_BUS{0|1|2}
>   */
> @@ -3222,6 +3236,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
>         CLK_STOPCTRL,
>  };
>
> +static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
> +       { MUX_SEL_G3D, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aclk_g3d_400_p)     = { "mout_g3d_pll", "aclk_g3d_400", };
>  PNAME(mout_g3d_pll_p)          = { "oscclk", "fout_g3d_pll", };
> @@ -3295,15 +3313,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
>         .nr_clk_ids             = G3D_NR_CLK,
>         .clk_regs               = g3d_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(g3d_clk_regs),
> +       .suspend_regs           = g3d_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(g3d_suspend_regs),
> +       .clk_name               = "aclk_g3d_400",
>  };
>
> -static void __init exynos5433_cmu_g3d_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &g3d_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
> -               exynos5433_cmu_g3d_init);
> -
>  /*
>   * Register offset definitions for CMU_GSCL
>   */
> @@ -3342,6 +3356,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
>         ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
>  };
>
> +static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
> +       { MUX_SEL_GSCL, 0 },
> +       { ENABLE_ACLK_GSCL, 0xfff },
> +       { ENABLE_PCLK_GSCL, 0xff },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(aclk_gscl_111_user_p)    = { "oscclk", "aclk_gscl_111", };
>  PNAME(aclk_gscl_333_user_p)    = { "oscclk", "aclk_gscl_333", };
> @@ -3436,15 +3456,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
>         .nr_clk_ids             = GSCL_NR_CLK,
>         .clk_regs               = gscl_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(gscl_clk_regs),
> +       .suspend_regs           = gscl_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(gscl_suspend_regs),
> +       .clk_name               = "aclk_gscl_111",
>  };
>
> -static void __init exynos5433_cmu_gscl_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &gscl_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
> -               exynos5433_cmu_gscl_init);
> -
>  /*
>   * Register offset definitions for CMU_APOLLO
>   */
> @@ -3970,6 +3986,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
>         ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
>  };
>
> +static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
> +       { MUX_SEL_MSCL0, 0 },
> +       { MUX_SEL_MSCL1, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_sclk_jpeg_user_p)           = { "oscclk", "sclk_jpeg_mscl", };
>  PNAME(mout_aclk_mscl_400_user_p)       = { "oscclk", "aclk_mscl_400", };
> @@ -4082,15 +4103,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
>         .nr_clk_ids             = MSCL_NR_CLK,
>         .clk_regs               = mscl_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(mscl_clk_regs),
> +       .suspend_regs           = mscl_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(mscl_suspend_regs),
> +       .clk_name               = "aclk_mscl_400",
>  };
>
> -static void __init exynos5433_cmu_mscl_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &mscl_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
> -               exynos5433_cmu_mscl_init);
> -
>  /*
>   * Register offset definitions for CMU_MFC
>   */
> @@ -4120,6 +4137,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
>         ENABLE_IP_MFC_SECURE_SMMU_MFC,
>  };
>
> +static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
> +       { MUX_SEL_MFC, 0 },
> +};
> +
>  PNAME(mout_aclk_mfc_400_user_p)                = { "oscclk", "aclk_mfc_400", };
>
>  static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
> @@ -4190,15 +4211,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
>         .nr_clk_ids             = MFC_NR_CLK,
>         .clk_regs               = mfc_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(mfc_clk_regs),
> +       .suspend_regs           = mfc_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(mfc_suspend_regs),
> +       .clk_name               = "aclk_mfc_400",
>  };
>
> -static void __init exynos5433_cmu_mfc_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &mfc_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
> -               exynos5433_cmu_mfc_init);
> -
>  /*
>   * Register offset definitions for CMU_HEVC
>   */
> @@ -4228,6 +4245,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
>         ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
>  };
>
> +static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
> +       { MUX_SEL_HEVC, 0 },
> +};
> +
>  PNAME(mout_aclk_hevc_400_user_p)       = { "oscclk", "aclk_hevc_400", };
>
>  static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
> @@ -4300,15 +4321,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
>         .nr_clk_ids             = HEVC_NR_CLK,
>         .clk_regs               = hevc_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(hevc_clk_regs),
> +       .suspend_regs           = hevc_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(hevc_suspend_regs),
> +       .clk_name               = "aclk_hevc_400",
>  };
>
> -static void __init exynos5433_cmu_hevc_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &hevc_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
> -               exynos5433_cmu_hevc_init);
> -
>  /*
>   * Register offset definitions for CMU_ISP
>   */
> @@ -4342,6 +4359,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
>         ENABLE_IP_ISP3,
>  };
>
> +static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
> +       { MUX_SEL_ISP, 0 },
> +};
> +
>  PNAME(mout_aclk_isp_dis_400_user_p)    = { "oscclk", "aclk_isp_dis_400", };
>  PNAME(mout_aclk_isp_400_user_p)                = { "oscclk", "aclk_isp_400", };
>
> @@ -4553,15 +4574,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
>         .nr_clk_ids             = ISP_NR_CLK,
>         .clk_regs               = isp_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(isp_clk_regs),
> +       .suspend_regs           = isp_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(isp_suspend_regs),
> +       .clk_name               = "aclk_isp_400",
>  };
>
> -static void __init exynos5433_cmu_isp_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &isp_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
> -               exynos5433_cmu_isp_init);
> -
>  /*
>   * Register offset definitions for CMU_CAM0
>   */
> @@ -4625,6 +4642,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
>         ENABLE_IP_CAM02,
>         ENABLE_IP_CAM03,
>  };
> +
> +static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
> +       { MUX_SEL_CAM00, 0 },
> +       { MUX_SEL_CAM01, 0 },
> +       { MUX_SEL_CAM02, 0 },
> +       { MUX_SEL_CAM03, 0 },
> +       { MUX_SEL_CAM04, 0 },
> +};
> +
>  PNAME(mout_aclk_cam0_333_user_p)       = { "oscclk", "aclk_cam0_333", };
>  PNAME(mout_aclk_cam0_400_user_p)       = { "oscclk", "aclk_cam0_400", };
>  PNAME(mout_aclk_cam0_552_user_p)       = { "oscclk", "aclk_cam0_552", };
> @@ -5030,15 +5056,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
>         .nr_clk_ids             = CAM0_NR_CLK,
>         .clk_regs               = cam0_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(cam0_clk_regs),
> +       .suspend_regs           = cam0_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(cam0_suspend_regs),
> +       .clk_name               = "aclk_cam0_400",
>  };
>
> -static void __init exynos5433_cmu_cam0_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &cam0_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
> -               exynos5433_cmu_cam0_init);
> -
>  /*
>   * Register offset definitions for CMU_CAM1
>   */
> @@ -5085,6 +5107,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
>         ENABLE_IP_CAM12,
>  };
>
> +static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
> +       { MUX_SEL_CAM10, 0 },
> +       { MUX_SEL_CAM11, 0 },
> +       { MUX_SEL_CAM12, 0 },
> +};
> +
>  PNAME(mout_sclk_isp_uart_user_p)       = { "oscclk", "sclk_isp_uart_cam1", };
>  PNAME(mout_sclk_isp_spi1_user_p)       = { "oscclk", "sclk_isp_spi1_cam1", };
>  PNAME(mout_sclk_isp_spi0_user_p)       = { "oscclk", "sclk_isp_spi0_cam1", };
> @@ -5403,11 +5431,226 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
>         .nr_clk_ids             = CAM1_NR_CLK,
>         .clk_regs               = cam1_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(cam1_clk_regs),
> +       .suspend_regs           = cam1_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(cam1_suspend_regs),
> +       .clk_name               = "aclk_cam1_400",
> +};
> +
> +
> +struct exynos5433_cmu_data {
> +       struct samsung_clk_provider ctx;
> +
> +       struct samsung_clk_reg_dump *clk_save;
> +       unsigned int nr_clk_save;
> +       const struct samsung_clk_reg_dump *clk_suspend;
> +       unsigned int nr_clk_suspend;
> +
> +       struct clk *clk;
> +       struct clk **pclks;
> +       int nr_pclks;
> +};
> +
> +static int exynos5433_cmu_suspend(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
> +       int i;
> +
> +       samsung_clk_save(data->ctx.reg_base, data->clk_save,
> +                        data->nr_clk_save);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
> +                           data->nr_clk_suspend);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_disable_unprepare(data->pclks[i]);
> +
> +       clk_disable_unprepare(data->clk);
> +
> +       return 0;
> +}
> +
> +static int exynos5433_cmu_resume(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
> +       int i;
> +
> +       clk_prepare_enable(data->clk);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_save,
> +                           data->nr_clk_save);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_disable_unprepare(data->pclks[i]);
> +
> +       return 0;
> +}
> +
> +static int __init exynos5433_cmu_probe(struct platform_device *pdev)
> +{
> +       const struct samsung_cmu_info *info;
> +       struct exynos5433_cmu_data *data;
> +       struct samsung_clk_provider *ctx;
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       void __iomem *reg_base;
> +       struct clk **clk_table;
> +       int i;
> +
> +       info = of_device_get_match_data(dev);
> +
> +       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +       ctx = &data->ctx;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       reg_base = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(reg_base)) {
> +               dev_err(dev, "failed to map registers\n");
> +               return PTR_ERR(reg_base);
> +       }
> +
> +       clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
> +                                GFP_KERNEL);
> +       if (!clk_table)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < info->nr_clk_ids; ++i)
> +               clk_table[i] = ERR_PTR(-ENOENT);
> +
> +       ctx->clk_data.clks = clk_table;
> +       ctx->clk_data.clk_num = info->nr_clk_ids;
> +       ctx->reg_base = reg_base;
> +       ctx->dev = dev;
> +       spin_lock_init(&ctx->lock);
> +
> +       data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
> +                                                   info->nr_clk_regs);
> +       data->nr_clk_save = info->nr_clk_regs;
> +       data->clk_suspend = info->suspend_regs;
> +       data->nr_clk_suspend = info->nr_suspend_regs;
> +       data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
> +                                                   "#clock-cells");
> +       if (data->nr_pclks > 0) {
> +               data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
> +                                          data->nr_pclks, GFP_KERNEL);
> +
> +               for (i = 0; i < data->nr_pclks; i++) {
> +                       struct clk *clk = of_clk_get(dev->of_node, i);
> +
> +                       if (IS_ERR(clk))
> +                               return PTR_ERR(clk);
> +                       data->pclks[i] = clk;
> +               }
> +       }
> +
> +       if (info->clk_name)
> +               data->clk = clk_get(dev, info->clk_name);
> +       clk_prepare_enable(data->clk);
> +
> +       platform_set_drvdata(pdev, data);
> +
> +       /*
> +        * Enable runtime pm here, so clock core with use runtime pm for all
> +        * registered clocks. Additionally we increase the runtime PM usage
> +        * count during the clock registration, to avoid the clock core from
> +        * runtime suspending the device.
> +        */
> +       pm_runtime_get_noresume(dev);
> +       pm_runtime_set_active(dev);
> +       pm_runtime_enable(dev);
> +
> +       if (info->pll_clks)
> +               samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
> +                                        reg_base);
> +       if (info->mux_clks)
> +               samsung_clk_register_mux(ctx, info->mux_clks,
> +                                        info->nr_mux_clks);
> +       if (info->div_clks)
> +               samsung_clk_register_div(ctx, info->div_clks,
> +                                        info->nr_div_clks);
> +       if (info->gate_clks)
> +               samsung_clk_register_gate(ctx, info->gate_clks,
> +                                         info->nr_gate_clks);
> +       if (info->fixed_clks)
> +               samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
> +                                               info->nr_fixed_clks);
> +       if (info->fixed_factor_clks)
> +               samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
> +                                                 info->nr_fixed_factor_clks);
> +
> +       samsung_clk_of_add_provider(dev->of_node, ctx);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id exynos5433_cmu_of_match[] = {
> +       {
> +               .compatible = "samsung,exynos5433-cmu-aud",
> +               .data = &aud_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-cam0",
> +               .data = &cam0_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-cam1",
> +               .data = &cam1_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-disp",
> +               .data = &disp_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-g2d",
> +               .data = &g2d_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-g3d",
> +               .data = &g3d_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-fsys",
> +               .data = &fsys_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-gscl",
> +               .data = &gscl_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-mfc",
> +               .data = &mfc_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-hevc",
> +               .data = &hevc_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-isp",
> +               .data = &isp_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-mscl",
> +               .data = &mscl_cmu_info,
> +       }, {
> +       },
> +};
> +
> +static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
> +       SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
> +                          NULL)
> +       SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +                                    pm_runtime_force_resume)
> +};
> +
> +static struct platform_driver exynos5433_cmu_driver __refdata = {
> +       .driver = {
> +               .name = "exynos5433-cmu",
> +               .of_match_table = exynos5433_cmu_of_match,
> +               .suppress_bind_attrs = true,
> +               .pm = &exynos5433_cmu_pm_ops,
> +       },
> +       .probe = exynos5433_cmu_probe,
>  };
>
> -static void __init exynos5433_cmu_cam1_init(struct device_node *np)
> +static int __init exynos5433_cmu_init(void)
>  {
> -       samsung_cmu_register_one(np, &cam1_cmu_info);
> +       return platform_driver_register(&exynos5433_cmu_driver);
>  }
> -CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
> -               exynos5433_cmu_cam1_init);
> +core_initcall(exynos5433_cmu_init);
> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
> index 9263d8a27c6b..664020cb4794 100644
> --- a/drivers/clk/samsung/clk.h
> +++ b/drivers/clk/samsung/clk.h
> @@ -354,6 +354,12 @@ struct samsung_cmu_info {
>         /* list and number of clocks registers */
>         const unsigned long *clk_regs;
>         unsigned int nr_clk_regs;
> +
> +       /* list and number of clocks registers to set before suspend */
> +       const struct samsung_clk_reg_dump *suspend_regs;
> +       unsigned int nr_suspend_regs;
> +       /* name of the parent clock needed for CMU register access */
> +       const char *clk_name;
>  };
>
>  extern struct samsung_clk_provider *__init samsung_clk_init(
> --
> 1.9.1
>

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

* [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
@ 2017-03-22 11:52         ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-22 11:52 UTC (permalink / raw)
  To: linux-arm-kernel

On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> Add runtime pm support for all clock controller units (CMU), which belongs
> to power domains and require special handling during on/off operations.
> Typically special values has to be written to MUX registers to change
> internal clocks parents to OSC clock before turning power off. During such
> operation all clocks, which enters CMU has to be enabled to let MUX to
> stabilize. Also for each CMU there is one special parent clock, which has
> to be enabled all the time when any access to CMU registers is done.
>
> This patch solves most of the mysterious external abort and freeze issues
> caused by a lack of proper parent CMU clock enabled or incorrect turn off
> procedure.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Kind regards
Uffe

> ---
>  .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
>  drivers/clk/samsung/clk-exynos5433.c               | 413 ++++++++++++++++-----
>  drivers/clk/samsung/clk.h                          |   6 +
>  3 files changed, 350 insertions(+), 85 deletions(-)
>
> diff --git a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> index 1dc80f8811fe..5c7dd12e667a 100644
> --- a/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> +++ b/Documentation/devicetree/bindings/clock/exynos5433-clock.txt
> @@ -168,6 +168,11 @@ Required Properties:
>                 - aclk_cam1_400
>                 - aclk_cam1_552
>
> +Optional properties:
> +  - power-domains: a phandle to respective power domain node as described by
> +       generic PM domain bindings (see power/power_domain.txt for more
> +       information).
> +
>  Each clock is assigned an identifier and client nodes can use this identifier
>  to specify the clock which they consume.
>
> @@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_ACLK_G2D_266>,
>                        <&cmu_top CLK_ACLK_G2D_400>;
> +               power-domains = <&pd_g2d>;
>         };
>
>         cmu_disp: clock-controller at 13b90000 {
> @@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
>                        <&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
>                        <&cmu_mif CLK_ACLK_DISP_333>;
> +               power-domains = <&pd_disp>;
>         };
>
>         cmu_aud: clock-controller at 114c0000 {
> @@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "fout_aud_pll";
>                 clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
> +               power-domains = <&pd_aud>;
>         };
>
>         cmu_bus0: clock-controller at 13600000 {
> @@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_g3d_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
> +               power-domains = <&pd_g3d>;
>         };
>
>         cmu_gscl: clock-controller at 13cf0000 {
> @@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                         <&cmu_top CLK_ACLK_GSCL_111>,
>                         <&cmu_top CLK_ACLK_GSCL_333>;
> +               power-domains = <&pd_gscl>;
>         };
>
>         cmu_apollo: clock-controller at 11900000 {
> @@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_SCLK_JPEG_MSCL>,
>                        <&cmu_top CLK_ACLK_MSCL_400>;
> +               power-domains = <&pd_mscl>;
>         };
>
>         cmu_mfc: clock-controller at 15280000 {
> @@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_mfc_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
> +               power-domains = <&pd_mfc>;
>         };
>
>         cmu_hevc: clock-controller at 14f80000 {
> @@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
>
>                 clock-names = "oscclk", "aclk_hevc_400";
>                 clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
> +               power-domains = <&pd_hevc>;
>         };
>
>         cmu_isp: clock-controller at 146d0000 {
> @@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                 clocks = <&xxti>,
>                        <&cmu_top CLK_ACLK_ISP_DIS_400>,
>                        <&cmu_top CLK_ACLK_ISP_400>;
> +               power-domains = <&pd_isp>;
>         };
>
>         cmu_cam0: clock-controller at 120d0000 {
> @@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_top CLK_ACLK_CAM0_333>,
>                        <&cmu_top CLK_ACLK_CAM0_400>,
>                        <&cmu_top CLK_ACLK_CAM0_552>;
> +               power-domains = <&pd_cam0>;
>         };
>
>         cmu_cam1: clock-controller at 145d0000 {
> @@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
>                        <&cmu_top CLK_ACLK_CAM1_333>,
>                        <&cmu_top CLK_ACLK_CAM1_400>,
>                        <&cmu_top CLK_ACLK_CAM1_552>;
> +               power-domains = <&pd_cam1>;
>         };
>
>  Example 3: UART controller node that consumes the clock generated by the clock
> diff --git a/drivers/clk/samsung/clk-exynos5433.c b/drivers/clk/samsung/clk-exynos5433.c
> index 11343a597093..a0d88ce948d7 100644
> --- a/drivers/clk/samsung/clk-exynos5433.c
> +++ b/drivers/clk/samsung/clk-exynos5433.c
> @@ -9,9 +9,14 @@
>   * Common Clock Framework support for Exynos5433 SoC.
>   */
>
> +#include <linux/clk.h>
>  #include <linux/clk-provider.h>
>  #include <linux/of.h>
>  #include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
>
>  #include <dt-bindings/clock/exynos5433.h>
>
> @@ -1991,6 +1996,14 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
>         ENABLE_IP_FSYS1,
>  };
>
> +static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
> +       { MUX_SEL_FSYS0, 0 },
> +       { MUX_SEL_FSYS1, 0 },
> +       { MUX_SEL_FSYS2, 0 },
> +       { MUX_SEL_FSYS3, 0 },
> +       { MUX_SEL_FSYS4, 0 },
> +};
> +
>  static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
>         /* PHY clocks from USBDRD30_PHY */
>         FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
> @@ -2296,16 +2309,11 @@ static void __init exynos5433_cmu_peris_init(struct device_node *np)
>         .nr_clk_ids             = FSYS_NR_CLK,
>         .clk_regs               = fsys_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(fsys_clk_regs),
> +       .suspend_regs           = fsys_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(fsys_suspend_regs),
> +       .clk_name               = "aclk_fsys_200",
>  };
>
> -static void __init exynos5433_cmu_fsys_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &fsys_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
> -               exynos5433_cmu_fsys_init);
> -
>  /*
>   * Register offset definitions for CMU_G2D
>   */
> @@ -2335,6 +2343,10 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
>         DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
>  };
>
> +static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
> +       { MUX_SEL_G2D0, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aclk_g2d_266_user_p)                = { "oscclk", "aclk_g2d_266", };
>  PNAME(mout_aclk_g2d_400_user_p)                = { "oscclk", "aclk_g2d_400", };
> @@ -2420,16 +2432,11 @@ static void __init exynos5433_cmu_fsys_init(struct device_node *np)
>         .nr_clk_ids             = G2D_NR_CLK,
>         .clk_regs               = g2d_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(g2d_clk_regs),
> +       .suspend_regs           = g2d_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(g2d_suspend_regs),
> +       .clk_name               = "aclk_g2d_400",
>  };
>
> -static void __init exynos5433_cmu_g2d_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &g2d_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
> -               exynos5433_cmu_g2d_init);
> -
>  /*
>   * Register offset definitions for CMU_DISP
>   */
> @@ -2494,6 +2501,18 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
>         CLKOUT_CMU_DISP_DIV_STAT,
>  };
>
> +static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
> +       /* PLL has to be enabled for suspend */
> +       { DISP_PLL_CON0, 0x85f40502 },
> +       /* ignore status of external PHY muxes during suspend to avoid hangs */
> +       { MUX_IGNORE_DISP2, 0x00111111 },
> +       { MUX_SEL_DISP0, 0 },
> +       { MUX_SEL_DISP1, 0 },
> +       { MUX_SEL_DISP2, 0 },
> +       { MUX_SEL_DISP3, 0 },
> +       { MUX_SEL_DISP4, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_disp_pll_p)                 = { "oscclk", "fout_disp_pll", };
>  PNAME(mout_sclk_dsim1_user_p)          = { "oscclk", "sclk_dsim1_disp", };
> @@ -2841,16 +2860,11 @@ static void __init exynos5433_cmu_g2d_init(struct device_node *np)
>         .nr_clk_ids             = DISP_NR_CLK,
>         .clk_regs               = disp_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(disp_clk_regs),
> +       .suspend_regs           = disp_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(disp_suspend_regs),
> +       .clk_name               = "aclk_disp_333",
>  };
>
> -static void __init exynos5433_cmu_disp_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &disp_cmu_info);
> -}
> -
> -CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
> -               exynos5433_cmu_disp_init);
> -
>  /*
>   * Register offset definitions for CMU_AUD
>   */
> @@ -2885,6 +2899,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
>         ENABLE_IP_AUD1,
>  };
>
> +static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
> +       { MUX_SEL_AUD0, 0 },
> +       { MUX_SEL_AUD1, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", };
>  PNAME(mout_sclk_aud_pcm_p)     = { "mout_aud_pll_user", "ioclk_audiocdclk0",};
> @@ -3011,16 +3030,11 @@ static void __init exynos5433_cmu_disp_init(struct device_node *np)
>         .nr_clk_ids             = AUD_NR_CLK,
>         .clk_regs               = aud_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(aud_clk_regs),
> +       .suspend_regs           = aud_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(aud_suspend_regs),
> +       .clk_name               = "fout_aud_pll",
>  };
>
> -static void __init exynos5433_cmu_aud_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &aud_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
> -               exynos5433_cmu_aud_init);
> -
> -
>  /*
>   * Register offset definitions for CMU_BUS{0|1|2}
>   */
> @@ -3222,6 +3236,10 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
>         CLK_STOPCTRL,
>  };
>
> +static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
> +       { MUX_SEL_G3D, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_aclk_g3d_400_p)     = { "mout_g3d_pll", "aclk_g3d_400", };
>  PNAME(mout_g3d_pll_p)          = { "oscclk", "fout_g3d_pll", };
> @@ -3295,15 +3313,11 @@ static void __init exynos5433_cmu_aud_init(struct device_node *np)
>         .nr_clk_ids             = G3D_NR_CLK,
>         .clk_regs               = g3d_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(g3d_clk_regs),
> +       .suspend_regs           = g3d_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(g3d_suspend_regs),
> +       .clk_name               = "aclk_g3d_400",
>  };
>
> -static void __init exynos5433_cmu_g3d_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &g3d_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
> -               exynos5433_cmu_g3d_init);
> -
>  /*
>   * Register offset definitions for CMU_GSCL
>   */
> @@ -3342,6 +3356,12 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
>         ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
>  };
>
> +static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
> +       { MUX_SEL_GSCL, 0 },
> +       { ENABLE_ACLK_GSCL, 0xfff },
> +       { ENABLE_PCLK_GSCL, 0xff },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(aclk_gscl_111_user_p)    = { "oscclk", "aclk_gscl_111", };
>  PNAME(aclk_gscl_333_user_p)    = { "oscclk", "aclk_gscl_333", };
> @@ -3436,15 +3456,11 @@ static void __init exynos5433_cmu_g3d_init(struct device_node *np)
>         .nr_clk_ids             = GSCL_NR_CLK,
>         .clk_regs               = gscl_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(gscl_clk_regs),
> +       .suspend_regs           = gscl_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(gscl_suspend_regs),
> +       .clk_name               = "aclk_gscl_111",
>  };
>
> -static void __init exynos5433_cmu_gscl_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &gscl_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
> -               exynos5433_cmu_gscl_init);
> -
>  /*
>   * Register offset definitions for CMU_APOLLO
>   */
> @@ -3970,6 +3986,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
>         ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
>  };
>
> +static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
> +       { MUX_SEL_MSCL0, 0 },
> +       { MUX_SEL_MSCL1, 0 },
> +};
> +
>  /* list of all parent clock list */
>  PNAME(mout_sclk_jpeg_user_p)           = { "oscclk", "sclk_jpeg_mscl", };
>  PNAME(mout_aclk_mscl_400_user_p)       = { "oscclk", "aclk_mscl_400", };
> @@ -4082,15 +4103,11 @@ static void __init exynos5433_cmu_atlas_init(struct device_node *np)
>         .nr_clk_ids             = MSCL_NR_CLK,
>         .clk_regs               = mscl_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(mscl_clk_regs),
> +       .suspend_regs           = mscl_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(mscl_suspend_regs),
> +       .clk_name               = "aclk_mscl_400",
>  };
>
> -static void __init exynos5433_cmu_mscl_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &mscl_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
> -               exynos5433_cmu_mscl_init);
> -
>  /*
>   * Register offset definitions for CMU_MFC
>   */
> @@ -4120,6 +4137,10 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
>         ENABLE_IP_MFC_SECURE_SMMU_MFC,
>  };
>
> +static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
> +       { MUX_SEL_MFC, 0 },
> +};
> +
>  PNAME(mout_aclk_mfc_400_user_p)                = { "oscclk", "aclk_mfc_400", };
>
>  static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
> @@ -4190,15 +4211,11 @@ static void __init exynos5433_cmu_mscl_init(struct device_node *np)
>         .nr_clk_ids             = MFC_NR_CLK,
>         .clk_regs               = mfc_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(mfc_clk_regs),
> +       .suspend_regs           = mfc_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(mfc_suspend_regs),
> +       .clk_name               = "aclk_mfc_400",
>  };
>
> -static void __init exynos5433_cmu_mfc_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &mfc_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
> -               exynos5433_cmu_mfc_init);
> -
>  /*
>   * Register offset definitions for CMU_HEVC
>   */
> @@ -4228,6 +4245,10 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
>         ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
>  };
>
> +static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
> +       { MUX_SEL_HEVC, 0 },
> +};
> +
>  PNAME(mout_aclk_hevc_400_user_p)       = { "oscclk", "aclk_hevc_400", };
>
>  static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
> @@ -4300,15 +4321,11 @@ static void __init exynos5433_cmu_mfc_init(struct device_node *np)
>         .nr_clk_ids             = HEVC_NR_CLK,
>         .clk_regs               = hevc_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(hevc_clk_regs),
> +       .suspend_regs           = hevc_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(hevc_suspend_regs),
> +       .clk_name               = "aclk_hevc_400",
>  };
>
> -static void __init exynos5433_cmu_hevc_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &hevc_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
> -               exynos5433_cmu_hevc_init);
> -
>  /*
>   * Register offset definitions for CMU_ISP
>   */
> @@ -4342,6 +4359,10 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
>         ENABLE_IP_ISP3,
>  };
>
> +static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
> +       { MUX_SEL_ISP, 0 },
> +};
> +
>  PNAME(mout_aclk_isp_dis_400_user_p)    = { "oscclk", "aclk_isp_dis_400", };
>  PNAME(mout_aclk_isp_400_user_p)                = { "oscclk", "aclk_isp_400", };
>
> @@ -4553,15 +4574,11 @@ static void __init exynos5433_cmu_hevc_init(struct device_node *np)
>         .nr_clk_ids             = ISP_NR_CLK,
>         .clk_regs               = isp_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(isp_clk_regs),
> +       .suspend_regs           = isp_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(isp_suspend_regs),
> +       .clk_name               = "aclk_isp_400",
>  };
>
> -static void __init exynos5433_cmu_isp_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &isp_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
> -               exynos5433_cmu_isp_init);
> -
>  /*
>   * Register offset definitions for CMU_CAM0
>   */
> @@ -4625,6 +4642,15 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
>         ENABLE_IP_CAM02,
>         ENABLE_IP_CAM03,
>  };
> +
> +static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
> +       { MUX_SEL_CAM00, 0 },
> +       { MUX_SEL_CAM01, 0 },
> +       { MUX_SEL_CAM02, 0 },
> +       { MUX_SEL_CAM03, 0 },
> +       { MUX_SEL_CAM04, 0 },
> +};
> +
>  PNAME(mout_aclk_cam0_333_user_p)       = { "oscclk", "aclk_cam0_333", };
>  PNAME(mout_aclk_cam0_400_user_p)       = { "oscclk", "aclk_cam0_400", };
>  PNAME(mout_aclk_cam0_552_user_p)       = { "oscclk", "aclk_cam0_552", };
> @@ -5030,15 +5056,11 @@ static void __init exynos5433_cmu_isp_init(struct device_node *np)
>         .nr_clk_ids             = CAM0_NR_CLK,
>         .clk_regs               = cam0_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(cam0_clk_regs),
> +       .suspend_regs           = cam0_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(cam0_suspend_regs),
> +       .clk_name               = "aclk_cam0_400",
>  };
>
> -static void __init exynos5433_cmu_cam0_init(struct device_node *np)
> -{
> -       samsung_cmu_register_one(np, &cam0_cmu_info);
> -}
> -CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
> -               exynos5433_cmu_cam0_init);
> -
>  /*
>   * Register offset definitions for CMU_CAM1
>   */
> @@ -5085,6 +5107,12 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
>         ENABLE_IP_CAM12,
>  };
>
> +static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
> +       { MUX_SEL_CAM10, 0 },
> +       { MUX_SEL_CAM11, 0 },
> +       { MUX_SEL_CAM12, 0 },
> +};
> +
>  PNAME(mout_sclk_isp_uart_user_p)       = { "oscclk", "sclk_isp_uart_cam1", };
>  PNAME(mout_sclk_isp_spi1_user_p)       = { "oscclk", "sclk_isp_spi1_cam1", };
>  PNAME(mout_sclk_isp_spi0_user_p)       = { "oscclk", "sclk_isp_spi0_cam1", };
> @@ -5403,11 +5431,226 @@ static void __init exynos5433_cmu_cam0_init(struct device_node *np)
>         .nr_clk_ids             = CAM1_NR_CLK,
>         .clk_regs               = cam1_clk_regs,
>         .nr_clk_regs            = ARRAY_SIZE(cam1_clk_regs),
> +       .suspend_regs           = cam1_suspend_regs,
> +       .nr_suspend_regs        = ARRAY_SIZE(cam1_suspend_regs),
> +       .clk_name               = "aclk_cam1_400",
> +};
> +
> +
> +struct exynos5433_cmu_data {
> +       struct samsung_clk_provider ctx;
> +
> +       struct samsung_clk_reg_dump *clk_save;
> +       unsigned int nr_clk_save;
> +       const struct samsung_clk_reg_dump *clk_suspend;
> +       unsigned int nr_clk_suspend;
> +
> +       struct clk *clk;
> +       struct clk **pclks;
> +       int nr_pclks;
> +};
> +
> +static int exynos5433_cmu_suspend(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
> +       int i;
> +
> +       samsung_clk_save(data->ctx.reg_base, data->clk_save,
> +                        data->nr_clk_save);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
> +                           data->nr_clk_suspend);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_disable_unprepare(data->pclks[i]);
> +
> +       clk_disable_unprepare(data->clk);
> +
> +       return 0;
> +}
> +
> +static int exynos5433_cmu_resume(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
> +       int i;
> +
> +       clk_prepare_enable(data->clk);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_save,
> +                           data->nr_clk_save);
> +
> +       for (i = 0; i < data->nr_pclks; i++)
> +               clk_disable_unprepare(data->pclks[i]);
> +
> +       return 0;
> +}
> +
> +static int __init exynos5433_cmu_probe(struct platform_device *pdev)
> +{
> +       const struct samsung_cmu_info *info;
> +       struct exynos5433_cmu_data *data;
> +       struct samsung_clk_provider *ctx;
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       void __iomem *reg_base;
> +       struct clk **clk_table;
> +       int i;
> +
> +       info = of_device_get_match_data(dev);
> +
> +       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +       ctx = &data->ctx;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       reg_base = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(reg_base)) {
> +               dev_err(dev, "failed to map registers\n");
> +               return PTR_ERR(reg_base);
> +       }
> +
> +       clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
> +                                GFP_KERNEL);
> +       if (!clk_table)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < info->nr_clk_ids; ++i)
> +               clk_table[i] = ERR_PTR(-ENOENT);
> +
> +       ctx->clk_data.clks = clk_table;
> +       ctx->clk_data.clk_num = info->nr_clk_ids;
> +       ctx->reg_base = reg_base;
> +       ctx->dev = dev;
> +       spin_lock_init(&ctx->lock);
> +
> +       data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
> +                                                   info->nr_clk_regs);
> +       data->nr_clk_save = info->nr_clk_regs;
> +       data->clk_suspend = info->suspend_regs;
> +       data->nr_clk_suspend = info->nr_suspend_regs;
> +       data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
> +                                                   "#clock-cells");
> +       if (data->nr_pclks > 0) {
> +               data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
> +                                          data->nr_pclks, GFP_KERNEL);
> +
> +               for (i = 0; i < data->nr_pclks; i++) {
> +                       struct clk *clk = of_clk_get(dev->of_node, i);
> +
> +                       if (IS_ERR(clk))
> +                               return PTR_ERR(clk);
> +                       data->pclks[i] = clk;
> +               }
> +       }
> +
> +       if (info->clk_name)
> +               data->clk = clk_get(dev, info->clk_name);
> +       clk_prepare_enable(data->clk);
> +
> +       platform_set_drvdata(pdev, data);
> +
> +       /*
> +        * Enable runtime pm here, so clock core with use runtime pm for all
> +        * registered clocks. Additionally we increase the runtime PM usage
> +        * count during the clock registration, to avoid the clock core from
> +        * runtime suspending the device.
> +        */
> +       pm_runtime_get_noresume(dev);
> +       pm_runtime_set_active(dev);
> +       pm_runtime_enable(dev);
> +
> +       if (info->pll_clks)
> +               samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
> +                                        reg_base);
> +       if (info->mux_clks)
> +               samsung_clk_register_mux(ctx, info->mux_clks,
> +                                        info->nr_mux_clks);
> +       if (info->div_clks)
> +               samsung_clk_register_div(ctx, info->div_clks,
> +                                        info->nr_div_clks);
> +       if (info->gate_clks)
> +               samsung_clk_register_gate(ctx, info->gate_clks,
> +                                         info->nr_gate_clks);
> +       if (info->fixed_clks)
> +               samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
> +                                               info->nr_fixed_clks);
> +       if (info->fixed_factor_clks)
> +               samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
> +                                                 info->nr_fixed_factor_clks);
> +
> +       samsung_clk_of_add_provider(dev->of_node, ctx);
> +       pm_runtime_put_sync(dev);
> +
> +       return 0;
> +}
> +
> +static const struct of_device_id exynos5433_cmu_of_match[] = {
> +       {
> +               .compatible = "samsung,exynos5433-cmu-aud",
> +               .data = &aud_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-cam0",
> +               .data = &cam0_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-cam1",
> +               .data = &cam1_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-disp",
> +               .data = &disp_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-g2d",
> +               .data = &g2d_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-g3d",
> +               .data = &g3d_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-fsys",
> +               .data = &fsys_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-gscl",
> +               .data = &gscl_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-mfc",
> +               .data = &mfc_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-hevc",
> +               .data = &hevc_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-isp",
> +               .data = &isp_cmu_info,
> +       }, {
> +               .compatible = "samsung,exynos5433-cmu-mscl",
> +               .data = &mscl_cmu_info,
> +       }, {
> +       },
> +};
> +
> +static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
> +       SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
> +                          NULL)
> +       SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
> +                                    pm_runtime_force_resume)
> +};
> +
> +static struct platform_driver exynos5433_cmu_driver __refdata = {
> +       .driver = {
> +               .name = "exynos5433-cmu",
> +               .of_match_table = exynos5433_cmu_of_match,
> +               .suppress_bind_attrs = true,
> +               .pm = &exynos5433_cmu_pm_ops,
> +       },
> +       .probe = exynos5433_cmu_probe,
>  };
>
> -static void __init exynos5433_cmu_cam1_init(struct device_node *np)
> +static int __init exynos5433_cmu_init(void)
>  {
> -       samsung_cmu_register_one(np, &cam1_cmu_info);
> +       return platform_driver_register(&exynos5433_cmu_driver);
>  }
> -CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
> -               exynos5433_cmu_cam1_init);
> +core_initcall(exynos5433_cmu_init);
> diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
> index 9263d8a27c6b..664020cb4794 100644
> --- a/drivers/clk/samsung/clk.h
> +++ b/drivers/clk/samsung/clk.h
> @@ -354,6 +354,12 @@ struct samsung_cmu_info {
>         /* list and number of clocks registers */
>         const unsigned long *clk_regs;
>         unsigned int nr_clk_regs;
> +
> +       /* list and number of clocks registers to set before suspend */
> +       const struct samsung_clk_reg_dump *suspend_regs;
> +       unsigned int nr_suspend_regs;
> +       /* name of the parent clock needed for CMU register access */
> +       const char *clk_name;
>  };
>
>  extern struct samsung_clk_provider *__init samsung_clk_init(
> --
> 1.9.1
>

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

* Re: [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-22 12:00         ` Ulf Hansson
  -1 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-22 12:00 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel,
	Stephen Boyd, Michael Turquette, Sylwester Nawrocki,
	Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> This patch adds support for runtime PM to Exynos Audio SubSystem driver to
> enable full support for audio power domain on Exynos5 SoCs. The main change
> is moving register saving and restoring code from system sleep PM ops to
> runtime PM ops and implementing system sleep PM ops with generic
> pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
> device is managed from clock core depending on the preparation status
> of the provided clocks.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Minor nitpick below.

[...]

>
> -#ifdef CONFIG_PM_SLEEP
>  static unsigned long reg_save[][2] = {
>         { ASS_CLK_SRC,  0 },
>         { ASS_CLK_DIV,  0 },
>         { ASS_CLK_GATE, 0 },
>  };
>
> -static int exynos_audss_clk_suspend(struct device *dev)
> +static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)

Instead of using "__maybe_unused", I would rather change from
"CONFIG_PM_SLEEP" to "CONFIG_PM". However, no strong opinion.

Perhaps you need something like this for patch 3/4 as well?

>  {
>         int i;
>
> @@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
>         return 0;
>  }

[...]

Kind regards
Uffe

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

* [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
@ 2017-03-22 12:00         ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-22 12:00 UTC (permalink / raw)
  To: linux-arm-kernel

On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
> This patch adds support for runtime PM to Exynos Audio SubSystem driver to
> enable full support for audio power domain on Exynos5 SoCs. The main change
> is moving register saving and restoring code from system sleep PM ops to
> runtime PM ops and implementing system sleep PM ops with generic
> pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
> device is managed from clock core depending on the preparation status
> of the provided clocks.
>
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>

Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>

Minor nitpick below.

[...]

>
> -#ifdef CONFIG_PM_SLEEP
>  static unsigned long reg_save[][2] = {
>         { ASS_CLK_SRC,  0 },
>         { ASS_CLK_DIV,  0 },
>         { ASS_CLK_GATE, 0 },
>  };
>
> -static int exynos_audss_clk_suspend(struct device *dev)
> +static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)

Instead of using "__maybe_unused", I would rather change from
"CONFIG_PM_SLEEP" to "CONFIG_PM". However, no strong opinion.

Perhaps you need something like this for patch 3/4 as well?

>  {
>         int i;
>
> @@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
>         return 0;
>  }

[...]

Kind regards
Uffe

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

* Re: [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
  2017-03-22 12:00         ` Ulf Hansson
  (?)
@ 2017-03-22 13:32           ` Marek Szyprowski
  -1 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 13:32 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel,
	Stephen Boyd, Michael Turquette, Sylwester Nawrocki,
	Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

Hi Ulf,

On 2017-03-22 13:00, Ulf Hansson wrote:
> On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> This patch adds support for runtime PM to Exynos Audio SubSystem driver to
>> enable full support for audio power domain on Exynos5 SoCs. The main change
>> is moving register saving and restoring code from system sleep PM ops to
>> runtime PM ops and implementing system sleep PM ops with generic
>> pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
>> device is managed from clock core depending on the preparation status
>> of the provided clocks.
>>
>> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
>
> Minor nitpick below.
>
> [...]
>
>> -#ifdef CONFIG_PM_SLEEP
>>   static unsigned long reg_save[][2] = {
>>          { ASS_CLK_SRC,  0 },
>>          { ASS_CLK_DIV,  0 },
>>          { ASS_CLK_GATE, 0 },
>>   };
>>
>> -static int exynos_audss_clk_suspend(struct device *dev)
>> +static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
> Instead of using "__maybe_unused", I would rather change from
> "CONFIG_PM_SLEEP" to "CONFIG_PM". However, no strong opinion.
>
> Perhaps you need something like this for patch 3/4 as well?

Frankly, this matters only for compile-time tests. All Exynos platforms
require CONFIG_PM, so that code will be always used in runtime.

For compile-time tests probably it is better to use __maybe_unused approach
to have higher coverage.

>>   {
>>          int i;
>>
>> @@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
>>          return 0;
>>   }
> [...]
>
> Kind regards
> Uffe

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
@ 2017-03-22 13:32           ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 13:32 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: linux-samsung-soc, linux-pm, Michael Turquette,
	Bartlomiej Zolnierkiewicz, Stephen Boyd, Krzysztof Kozlowski,
	Inki Dae, Chanwoo Choi, Sylwester Nawrocki, linux-clk,
	linux-arm-kernel

Hi Ulf,

On 2017-03-22 13:00, Ulf Hansson wrote:
> On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> This patch adds support for runtime PM to Exynos Audio SubSystem driver to
>> enable full support for audio power domain on Exynos5 SoCs. The main change
>> is moving register saving and restoring code from system sleep PM ops to
>> runtime PM ops and implementing system sleep PM ops with generic
>> pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
>> device is managed from clock core depending on the preparation status
>> of the provided clocks.
>>
>> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
>
> Minor nitpick below.
>
> [...]
>
>> -#ifdef CONFIG_PM_SLEEP
>>   static unsigned long reg_save[][2] = {
>>          { ASS_CLK_SRC,  0 },
>>          { ASS_CLK_DIV,  0 },
>>          { ASS_CLK_GATE, 0 },
>>   };
>>
>> -static int exynos_audss_clk_suspend(struct device *dev)
>> +static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
> Instead of using "__maybe_unused", I would rather change from
> "CONFIG_PM_SLEEP" to "CONFIG_PM". However, no strong opinion.
>
> Perhaps you need something like this for patch 3/4 as well?

Frankly, this matters only for compile-time tests. All Exynos platforms
require CONFIG_PM, so that code will be always used in runtime.

For compile-time tests probably it is better to use __maybe_unused approach
to have higher coverage.

>>   {
>>          int i;
>>
>> @@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
>>          return 0;
>>   }
> [...]
>
> Kind regards
> Uffe

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
@ 2017-03-22 13:32           ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-22 13:32 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Ulf,

On 2017-03-22 13:00, Ulf Hansson wrote:
> On 22 March 2017 at 12:35, Marek Szyprowski <m.szyprowski@samsung.com> wrote:
>> This patch adds support for runtime PM to Exynos Audio SubSystem driver to
>> enable full support for audio power domain on Exynos5 SoCs. The main change
>> is moving register saving and restoring code from system sleep PM ops to
>> runtime PM ops and implementing system sleep PM ops with generic
>> pm_runtime_force_suspend/resume helpers. Runtime PM of the Exynos AudSS
>> device is managed from clock core depending on the preparation status
>> of the provided clocks.
>>
>> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
>
> Minor nitpick below.
>
> [...]
>
>> -#ifdef CONFIG_PM_SLEEP
>>   static unsigned long reg_save[][2] = {
>>          { ASS_CLK_SRC,  0 },
>>          { ASS_CLK_DIV,  0 },
>>          { ASS_CLK_GATE, 0 },
>>   };
>>
>> -static int exynos_audss_clk_suspend(struct device *dev)
>> +static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
> Instead of using "__maybe_unused", I would rather change from
> "CONFIG_PM_SLEEP" to "CONFIG_PM". However, no strong opinion.
>
> Perhaps you need something like this for patch 3/4 as well?

Frankly, this matters only for compile-time tests. All Exynos platforms
require CONFIG_PM, so that code will be always used in runtime.

For compile-time tests probably it is better to use __maybe_unused approach
to have higher coverage.

>>   {
>>          int i;
>>
>> @@ -54,7 +54,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
>>          return 0;
>>   }
> [...]
>
> Kind regards
> Uffe

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-27 15:24         ` Sylwester Nawrocki
  -1 siblings, 0 replies; 41+ messages in thread
From: Sylwester Nawrocki @ 2017-03-27 15:24 UTC (permalink / raw)
  To: Marek Szyprowski, linux-clk
  Cc: linux-pm, linux-samsung-soc, linux-arm-kernel, Stephen Boyd,
	Michael Turquette, Ulf Hansson, Chanwoo Choi, Inki Dae,
	Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

On 03/22/2017 12:35 PM, Marek Szyprowski wrote:
> +static int __init exynos5433_cmu_probe(struct platform_device *pdev)
> +{

> +	/*
> +	 * Enable runtime pm here, so clock core with use runtime pm for all
> +	 * registered clocks. Additionally we increase the runtime PM usage
> +	 * count during the clock registration, to avoid the clock core from
> +	 * runtime suspending the device.
> +	 */

While applying I rephrased the above comment to:

/*
  * Enable runtime PM here to allow the clock core using runtime PM
  * for the registered clocks. Additionally, we increase the runtime
  * PM usage count before registering the clocks, to prevent the
  * clock core from runtime suspending the device.
  */

> +	pm_runtime_get_noresume(dev);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);


-- 
Thanks,
Sylwester

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

* [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support
@ 2017-03-27 15:24         ` Sylwester Nawrocki
  0 siblings, 0 replies; 41+ messages in thread
From: Sylwester Nawrocki @ 2017-03-27 15:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/22/2017 12:35 PM, Marek Szyprowski wrote:
> +static int __init exynos5433_cmu_probe(struct platform_device *pdev)
> +{

> +	/*
> +	 * Enable runtime pm here, so clock core with use runtime pm for all
> +	 * registered clocks. Additionally we increase the runtime PM usage
> +	 * count during the clock registration, to avoid the clock core from
> +	 * runtime suspending the device.
> +	 */

While applying I rephrased the above comment to:

/*
  * Enable runtime PM here to allow the clock core using runtime PM
  * for the registered clocks. Additionally, we increase the runtime
  * PM usage count before registering the clocks, to prevent the
  * clock core from runtime suspending the device.
  */

> +	pm_runtime_get_noresume(dev);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);


-- 
Thanks,
Sylwester

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

* Re: [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
  2017-03-22 11:35       ` Marek Szyprowski
@ 2017-03-27 15:24         ` Sylwester Nawrocki
  -1 siblings, 0 replies; 41+ messages in thread
From: Sylwester Nawrocki @ 2017-03-27 15:24 UTC (permalink / raw)
  To: Marek Szyprowski, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel
  Cc: Stephen Boyd, Michael Turquette, Ulf Hansson, Chanwoo Choi,
	Inki Dae, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

On 03/22/2017 12:35 PM, Marek Szyprowski wrote:
> @@ -141,15 +141,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)

> +	/*
> +	 * Enable runtime PM here, so clock core with use runtime PM for all
> +	 * registered clocks.
> +	 */

While applying I rephrased the above comment to:

/*
  * Enable runtime PM here to allow the clock core using runtime PM
  * for the registered clocks.
  */

> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);

--
Thanks,
Sylwester

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

* [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM
@ 2017-03-27 15:24         ` Sylwester Nawrocki
  0 siblings, 0 replies; 41+ messages in thread
From: Sylwester Nawrocki @ 2017-03-27 15:24 UTC (permalink / raw)
  To: linux-arm-kernel

On 03/22/2017 12:35 PM, Marek Szyprowski wrote:
> @@ -141,15 +141,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)

> +	/*
> +	 * Enable runtime PM here, so clock core with use runtime PM for all
> +	 * registered clocks.
> +	 */

While applying I rephrased the above comment to:

/*
  * Enable runtime PM here to allow the clock core using runtime PM
  * for the registered clocks.
  */

> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);

--
Thanks,
Sylwester

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
  2017-03-22 11:35       ` Marek Szyprowski
  (?)
@ 2017-03-29 20:22         ` Michael Turquette
  -1 siblings, 0 replies; 41+ messages in thread
From: Michael Turquette @ 2017-03-29 20:22 UTC (permalink / raw)
  To: Marek Szyprowski, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel
  Cc: Marek Szyprowski, Stephen Boyd, Ulf Hansson, Sylwester Nawrocki,
	Chanwoo Choi, Inki Dae, Krzysztof Kozlowski,
	Bartlomiej Zolnierkiewicz

Hi Marek,

Quoting Marek Szyprowski (2017-03-22 04:35:40)
> Registers for some clocks might be located in the SOC area, which are und=
er the
> power domain. To enable access to those registers respective domain has t=
o be
> turned on. Additionally, registers for such clocks will usually loose its
> contents when power domain is turned off, so additional saving and restor=
ing of
> them might be needed in the clock controller driver.
> =

> This patch adds basic infrastructure in the clocks core to allow implemen=
ting
> driver for such clocks under power domains. Clock provider can supply a
> struct device pointer, which is the used by clock core for tracking and m=
anaging
> clock's controller runtime pm state. Each clk_prepare() operation
> will first call pm_runtime_get_sync() on the supplied device, while
> clk_unprepare() will do pm_runtime_put_sync() at the end.
> =

> Additional calls to pm_runtime_get/put functions are required to ensure t=
hat any
> register access (like calculating/changing clock rates and unpreparing/di=
sabling
> unused clocks on boot) will be done with clock controller in runtime resu=
mend
> state.

The above is a bit confusing. Is clk_prepare really special? It seems to
me that every single clk_ops callback will need this?

That leads to my second question: why put this in the clk core? Can the
clk provider driver simply add pm_runtime_{get,put} calls into its
clk_ops callbacks instead? The clk core does not directly touch hardware
(e.g. reading registers) so putting the pm runtime calls into the
provider callbacks should be sufficient.

> =

> When one wants to register clock controller, which make use of this featu=
re, he
> has to:
> 1. Provide a struct device to the core when registering the provider.
> 2. Ensure to enable runtime PM for that device before registering clocks.
> 3. Make sure that the runtime PM status of the controller device reflects
>    the HW state.

Third question: is there a case where more than one device is required?
Is is possible that a single pm_runtime_get call against a single device
will not be enough for some clk provider drivers? If so then this
solution does not scale very well and the clk provider driver will have
to implement this in the clk_ops callbacks (as I mentioned above in my
second question).

Fourth & final question: I'm under the impression that pm runtime calls
be be nested and re-enter, but I want to make sure (Ulf?). For instance
it is highly likely that this new feature would cause something like:

pm_runtime_get()              - called by random driver
-> clk_prepare_enable()       - genpd enables functioal clocks
   -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
      -> clk_prepare_enable() - genpd enables interface or bus clocks

I guess this is safe from the pm_runtime_get/genpd perspective, but want
to make sure first.

Thanks,
Mike

> =

> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
> ---
>  drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-=
------
>  1 file changed, 114 insertions(+), 15 deletions(-)
> =

> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 0fb39fe217d1..a6001b6e49a7 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -21,6 +21,7 @@
>  #include <linux/of.h>
>  #include <linux/device.h>
>  #include <linux/init.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/sched.h>
>  #include <linux/clkdev.h>
>  =

> @@ -46,6 +47,7 @@ struct clk_core {
>         const struct clk_ops    *ops;
>         struct clk_hw           *hw;
>         struct module           *owner;
> +       struct device           *dev;
>         struct clk_core         *parent;
>         const char              **parent_names;
>         struct clk_core         **parents;
> @@ -87,6 +89,26 @@ struct clk {
>         struct hlist_node clks_node;
>  };
>  =

> +/***           runtime pm          ***/
> +static int clk_pm_runtime_get(struct clk_core *core)
> +{
> +       int ret =3D 0;
> +
> +       if (!core->dev)
> +               return 0;
> +
> +       ret =3D pm_runtime_get_sync(core->dev);
> +       return ret < 0 ? ret : 0;
> +}
> +
> +static void clk_pm_runtime_put(struct clk_core *core)
> +{
> +       if (!core->dev)
> +               return;
> +
> +       pm_runtime_put_sync(core->dev);
> +}
> +
>  /***           locking             ***/
>  static void clk_prepare_lock(void)
>  {
> @@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
>  =

>  static bool clk_core_is_prepared(struct clk_core *core)
>  {
> +       bool ret =3D false;
> +
>         /*
>          * .is_prepared is optional for clocks that can prepare
>          * fall back to software usage counter if it is missing
> @@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *c=
ore)
>         if (!core->ops->is_prepared)
>                 return core->prepare_count;
>  =

> -       return core->ops->is_prepared(core->hw);
> +       if (!clk_pm_runtime_get(core)) {
> +               ret =3D core->ops->is_prepared(core->hw);
> +               clk_pm_runtime_put(core);
> +       }
> +
> +       return ret;
>  }
>  =

>  static bool clk_core_is_enabled(struct clk_core *core)
>  {
> +       bool ret =3D false;
> +
>         /*
>          * .is_enabled is only mandatory for clocks that gate
>          * fall back to software usage counter if .is_enabled is missing
> @@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *cor=
e)
>         if (!core->ops->is_enabled)
>                 return core->enable_count;
>  =

> -       return core->ops->is_enabled(core->hw);
> +       /*
> +        * Check if clock controller's device is runtime active before
> +        * calling .is_enabled callback. If not, assume that clock is
> +        * disabled, because we might be called from atomic context, from
> +        * which pm_runtime_get() is not allowed.
> +        * This function is called mainly from clk_disable_unused_subtree,
> +        * which ensures proper runtime pm activation of controller before
> +        * taking enable spinlock, but the below check is needed if one t=
ries
> +        * to call it from other places.
> +        */
> +       if (core->dev) {
> +               pm_runtime_get_noresume(core->dev);
> +               if (!pm_runtime_active(core->dev)) {
> +                       ret =3D false;
> +                       goto done;
> +               }
> +       }
> +
> +       ret =3D core->ops->is_enabled(core->hw);
> +done:
> +       clk_pm_runtime_put(core);
> +
> +       return ret;
>  }
>  =

>  /***    helper functions   ***/
> @@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
>         if (core->ops->unprepare)
>                 core->ops->unprepare(core->hw);
>  =

> +       clk_pm_runtime_put(core);
> +
>         trace_clk_unprepare_complete(core);
>         clk_core_unprepare(core->parent);
>  }
> @@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
>                 return 0;
>  =

>         if (core->prepare_count =3D=3D 0) {
> -               ret =3D clk_core_prepare(core->parent);
> +               ret =3D clk_pm_runtime_get(core);
>                 if (ret)
>                         return ret;
>  =

> +               ret =3D clk_core_prepare(core->parent);
> +               if (ret)
> +                       goto runtime_put;
> +
>                 trace_clk_prepare(core);
>  =

>                 if (core->ops->prepare)
> @@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
>  =

>                 trace_clk_prepare_complete(core);
>  =

> -               if (ret) {
> -                       clk_core_unprepare(core->parent);
> -                       return ret;
> -               }
> +               if (ret)
> +                       goto unprepare;
>         }
>  =

>         core->prepare_count++;
>  =

>         return 0;
> +unprepare:
> +       clk_core_unprepare(core->parent);
> +runtime_put:
> +       clk_pm_runtime_put(core);
> +       return ret;
>  }
>  =

>  static int clk_core_prepare_lock(struct clk_core *core)
> @@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_c=
ore *core)
>         if (core->flags & CLK_IGNORE_UNUSED)
>                 return;
>  =

> +       if (clk_pm_runtime_get(core))
> +               return;
> +
>         if (clk_core_is_prepared(core)) {
>                 trace_clk_unprepare(core);
>                 if (core->ops->unprepare_unused)
> @@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_c=
ore *core)
>                         core->ops->unprepare(core->hw);
>                 trace_clk_unprepare_complete(core);
>         }
> +
> +       clk_pm_runtime_put(core);
>  }
>  =

>  static void clk_disable_unused_subtree(struct clk_core *core)
> @@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_cor=
e *core)
>         if (core->flags & CLK_OPS_PARENT_ENABLE)
>                 clk_core_prepare_enable(core->parent);
>  =

> +       if (clk_pm_runtime_get(core))
> +               goto unprepare_out;
> +
>         flags =3D clk_enable_lock();
>  =

>         if (core->enable_count)
> @@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_cor=
e *core)
>  =

>  unlock_out:
>         clk_enable_unlock(flags);
> +       clk_pm_runtime_put(core);
> +unprepare_out:
>         if (core->flags & CLK_OPS_PARENT_ENABLE)
>                 clk_core_disable_unprepare(core->parent);
>  }
> @@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk)
>  static unsigned long clk_recalc(struct clk_core *core,
>                                 unsigned long parent_rate)
>  {
> -       if (core->ops->recalc_rate)
> -               return core->ops->recalc_rate(core->hw, parent_rate);
> -       return parent_rate;
> +       unsigned long rate =3D parent_rate;
> +
> +       if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
> +               rate =3D core->ops->recalc_rate(core->hw, parent_rate);
> +               clk_pm_runtime_put(core);
> +       }
> +       return rate;
>  }
>  =

>  /**
> @@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core=
 *core,
>  {
>         struct clk_core *top, *fail_clk;
>         unsigned long rate =3D req_rate;
> +       int ret =3D 0;
>  =

>         if (!core)
>                 return 0;
> @@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_co=
re *core,
>         if (!top)
>                 return -EINVAL;
>  =

> +       ret =3D clk_pm_runtime_get(core);
> +       if (ret)
> +               return ret;
> +
>         /* notify that we are about to change rates */
>         fail_clk =3D clk_propagate_rate_change(top, PRE_RATE_CHANGE);
>         if (fail_clk) {
>                 pr_debug("%s: failed to set %s rate\n", __func__,
>                                 fail_clk->name);
>                 clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
> -               return -EBUSY;
> +               ret =3D -EBUSY;
> +               goto err;
>         }
>  =

>         /* change the rates */
>         clk_change_rate(top);
>  =

>         core->req_rate =3D req_rate;
> +err:
> +       clk_pm_runtime_put(core);
>  =

> -       return 0;
> +       return ret;
>  }
>  =

>  /**
> @@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *c=
ore, struct clk_core *parent)
>                 p_rate =3D parent->rate;
>         }
>  =

> +       ret =3D clk_pm_runtime_get(core);
> +       if (ret)
> +               goto out;
> +
>         /* propagate PRE_RATE_CHANGE notifications */
>         ret =3D __clk_speculate_rates(core, p_rate);
>  =

>         /* abort if a driver objects */
>         if (ret & NOTIFY_STOP_MASK)
> -               goto out;
> +               goto runtime_put;
>  =

>         /* do the re-parent */
>         ret =3D __clk_set_parent(core, parent, p_index);
> @@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *cor=
e, struct clk_core *parent)
>                 __clk_recalc_accuracies(core);
>         }
>  =

> +runtime_put:
> +       clk_pm_runtime_put(core);
>  out:
>         clk_prepare_unlock();
>  =

> @@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struc=
t clk_hw *hw)
>                 goto fail_name;
>         }
>         core->ops =3D hw->init->ops;
> +       if (dev && pm_runtime_enabled(dev)) {
> +               core->dev =3D dev;
> +               ret =3D clk_pm_runtime_get(core);
> +               if (ret)
> +                       goto fail_pm;
> +       }
>         if (dev && dev->driver)
>                 core->owner =3D dev->driver->owner;
>         core->hw =3D hw;
> @@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, stru=
ct clk_hw *hw)
>         }
>  =

>         ret =3D __clk_core_init(core);
> -       if (!ret)
> +       if (!ret) {
> +               clk_pm_runtime_put(core);
>                 return hw->clk;
> +       }
>  =

>         __clk_free_clk(hw->clk);
>         hw->clk =3D NULL;
> -
>  fail_parents:
>         kfree(core->parents);
>  fail_parent_names_copy:
> @@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct=
 clk_hw *hw)
>                 kfree_const(core->parent_names[i]);
>         kfree(core->parent_names);
>  fail_parent_names:
> +       clk_pm_runtime_put(core);
> +fail_pm:
>         kfree_const(core->name);
>  fail_name:
>         kfree(core);
> -- =

> 1.9.1
>=20

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-29 20:22         ` Michael Turquette
  0 siblings, 0 replies; 41+ messages in thread
From: Michael Turquette @ 2017-03-29 20:22 UTC (permalink / raw)
  To: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel
  Cc: Ulf Hansson, Bartlomiej Zolnierkiewicz, Stephen Boyd,
	Krzysztof Kozlowski, Inki Dae, Chanwoo Choi, Sylwester Nawrocki,
	Marek Szyprowski

Hi Marek,

Quoting Marek Szyprowski (2017-03-22 04:35:40)
> Registers for some clocks might be located in the SOC area, which are under the
> power domain. To enable access to those registers respective domain has to be
> turned on. Additionally, registers for such clocks will usually loose its
> contents when power domain is turned off, so additional saving and restoring of
> them might be needed in the clock controller driver.
> 
> This patch adds basic infrastructure in the clocks core to allow implementing
> driver for such clocks under power domains. Clock provider can supply a
> struct device pointer, which is the used by clock core for tracking and managing
> clock's controller runtime pm state. Each clk_prepare() operation
> will first call pm_runtime_get_sync() on the supplied device, while
> clk_unprepare() will do pm_runtime_put_sync() at the end.
> 
> Additional calls to pm_runtime_get/put functions are required to ensure that any
> register access (like calculating/changing clock rates and unpreparing/disabling
> unused clocks on boot) will be done with clock controller in runtime resumend
> state.

The above is a bit confusing. Is clk_prepare really special? It seems to
me that every single clk_ops callback will need this?

That leads to my second question: why put this in the clk core? Can the
clk provider driver simply add pm_runtime_{get,put} calls into its
clk_ops callbacks instead? The clk core does not directly touch hardware
(e.g. reading registers) so putting the pm runtime calls into the
provider callbacks should be sufficient.

> 
> When one wants to register clock controller, which make use of this feature, he
> has to:
> 1. Provide a struct device to the core when registering the provider.
> 2. Ensure to enable runtime PM for that device before registering clocks.
> 3. Make sure that the runtime PM status of the controller device reflects
>    the HW state.

Third question: is there a case where more than one device is required?
Is is possible that a single pm_runtime_get call against a single device
will not be enough for some clk provider drivers? If so then this
solution does not scale very well and the clk provider driver will have
to implement this in the clk_ops callbacks (as I mentioned above in my
second question).

Fourth & final question: I'm under the impression that pm runtime calls
be be nested and re-enter, but I want to make sure (Ulf?). For instance
it is highly likely that this new feature would cause something like:

pm_runtime_get()              - called by random driver
-> clk_prepare_enable()       - genpd enables functioal clocks
   -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
      -> clk_prepare_enable() - genpd enables interface or bus clocks

I guess this is safe from the pm_runtime_get/genpd perspective, but want
to make sure first.

Thanks,
Mike

> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
> ---
>  drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 114 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 0fb39fe217d1..a6001b6e49a7 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -21,6 +21,7 @@
>  #include <linux/of.h>
>  #include <linux/device.h>
>  #include <linux/init.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/sched.h>
>  #include <linux/clkdev.h>
>  
> @@ -46,6 +47,7 @@ struct clk_core {
>         const struct clk_ops    *ops;
>         struct clk_hw           *hw;
>         struct module           *owner;
> +       struct device           *dev;
>         struct clk_core         *parent;
>         const char              **parent_names;
>         struct clk_core         **parents;
> @@ -87,6 +89,26 @@ struct clk {
>         struct hlist_node clks_node;
>  };
>  
> +/***           runtime pm          ***/
> +static int clk_pm_runtime_get(struct clk_core *core)
> +{
> +       int ret = 0;
> +
> +       if (!core->dev)
> +               return 0;
> +
> +       ret = pm_runtime_get_sync(core->dev);
> +       return ret < 0 ? ret : 0;
> +}
> +
> +static void clk_pm_runtime_put(struct clk_core *core)
> +{
> +       if (!core->dev)
> +               return;
> +
> +       pm_runtime_put_sync(core->dev);
> +}
> +
>  /***           locking             ***/
>  static void clk_prepare_lock(void)
>  {
> @@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
>  
>  static bool clk_core_is_prepared(struct clk_core *core)
>  {
> +       bool ret = false;
> +
>         /*
>          * .is_prepared is optional for clocks that can prepare
>          * fall back to software usage counter if it is missing
> @@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core)
>         if (!core->ops->is_prepared)
>                 return core->prepare_count;
>  
> -       return core->ops->is_prepared(core->hw);
> +       if (!clk_pm_runtime_get(core)) {
> +               ret = core->ops->is_prepared(core->hw);
> +               clk_pm_runtime_put(core);
> +       }
> +
> +       return ret;
>  }
>  
>  static bool clk_core_is_enabled(struct clk_core *core)
>  {
> +       bool ret = false;
> +
>         /*
>          * .is_enabled is only mandatory for clocks that gate
>          * fall back to software usage counter if .is_enabled is missing
> @@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core)
>         if (!core->ops->is_enabled)
>                 return core->enable_count;
>  
> -       return core->ops->is_enabled(core->hw);
> +       /*
> +        * Check if clock controller's device is runtime active before
> +        * calling .is_enabled callback. If not, assume that clock is
> +        * disabled, because we might be called from atomic context, from
> +        * which pm_runtime_get() is not allowed.
> +        * This function is called mainly from clk_disable_unused_subtree,
> +        * which ensures proper runtime pm activation of controller before
> +        * taking enable spinlock, but the below check is needed if one tries
> +        * to call it from other places.
> +        */
> +       if (core->dev) {
> +               pm_runtime_get_noresume(core->dev);
> +               if (!pm_runtime_active(core->dev)) {
> +                       ret = false;
> +                       goto done;
> +               }
> +       }
> +
> +       ret = core->ops->is_enabled(core->hw);
> +done:
> +       clk_pm_runtime_put(core);
> +
> +       return ret;
>  }
>  
>  /***    helper functions   ***/
> @@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
>         if (core->ops->unprepare)
>                 core->ops->unprepare(core->hw);
>  
> +       clk_pm_runtime_put(core);
> +
>         trace_clk_unprepare_complete(core);
>         clk_core_unprepare(core->parent);
>  }
> @@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
>                 return 0;
>  
>         if (core->prepare_count == 0) {
> -               ret = clk_core_prepare(core->parent);
> +               ret = clk_pm_runtime_get(core);
>                 if (ret)
>                         return ret;
>  
> +               ret = clk_core_prepare(core->parent);
> +               if (ret)
> +                       goto runtime_put;
> +
>                 trace_clk_prepare(core);
>  
>                 if (core->ops->prepare)
> @@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
>  
>                 trace_clk_prepare_complete(core);
>  
> -               if (ret) {
> -                       clk_core_unprepare(core->parent);
> -                       return ret;
> -               }
> +               if (ret)
> +                       goto unprepare;
>         }
>  
>         core->prepare_count++;
>  
>         return 0;
> +unprepare:
> +       clk_core_unprepare(core->parent);
> +runtime_put:
> +       clk_pm_runtime_put(core);
> +       return ret;
>  }
>  
>  static int clk_core_prepare_lock(struct clk_core *core)
> @@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
>         if (core->flags & CLK_IGNORE_UNUSED)
>                 return;
>  
> +       if (clk_pm_runtime_get(core))
> +               return;
> +
>         if (clk_core_is_prepared(core)) {
>                 trace_clk_unprepare(core);
>                 if (core->ops->unprepare_unused)
> @@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
>                         core->ops->unprepare(core->hw);
>                 trace_clk_unprepare_complete(core);
>         }
> +
> +       clk_pm_runtime_put(core);
>  }
>  
>  static void clk_disable_unused_subtree(struct clk_core *core)
> @@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
>         if (core->flags & CLK_OPS_PARENT_ENABLE)
>                 clk_core_prepare_enable(core->parent);
>  
> +       if (clk_pm_runtime_get(core))
> +               goto unprepare_out;
> +
>         flags = clk_enable_lock();
>  
>         if (core->enable_count)
> @@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
>  
>  unlock_out:
>         clk_enable_unlock(flags);
> +       clk_pm_runtime_put(core);
> +unprepare_out:
>         if (core->flags & CLK_OPS_PARENT_ENABLE)
>                 clk_core_disable_unprepare(core->parent);
>  }
> @@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk)
>  static unsigned long clk_recalc(struct clk_core *core,
>                                 unsigned long parent_rate)
>  {
> -       if (core->ops->recalc_rate)
> -               return core->ops->recalc_rate(core->hw, parent_rate);
> -       return parent_rate;
> +       unsigned long rate = parent_rate;
> +
> +       if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
> +               rate = core->ops->recalc_rate(core->hw, parent_rate);
> +               clk_pm_runtime_put(core);
> +       }
> +       return rate;
>  }
>  
>  /**
> @@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
>  {
>         struct clk_core *top, *fail_clk;
>         unsigned long rate = req_rate;
> +       int ret = 0;
>  
>         if (!core)
>                 return 0;
> @@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
>         if (!top)
>                 return -EINVAL;
>  
> +       ret = clk_pm_runtime_get(core);
> +       if (ret)
> +               return ret;
> +
>         /* notify that we are about to change rates */
>         fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
>         if (fail_clk) {
>                 pr_debug("%s: failed to set %s rate\n", __func__,
>                                 fail_clk->name);
>                 clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
> -               return -EBUSY;
> +               ret = -EBUSY;
> +               goto err;
>         }
>  
>         /* change the rates */
>         clk_change_rate(top);
>  
>         core->req_rate = req_rate;
> +err:
> +       clk_pm_runtime_put(core);
>  
> -       return 0;
> +       return ret;
>  }
>  
>  /**
> @@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
>                 p_rate = parent->rate;
>         }
>  
> +       ret = clk_pm_runtime_get(core);
> +       if (ret)
> +               goto out;
> +
>         /* propagate PRE_RATE_CHANGE notifications */
>         ret = __clk_speculate_rates(core, p_rate);
>  
>         /* abort if a driver objects */
>         if (ret & NOTIFY_STOP_MASK)
> -               goto out;
> +               goto runtime_put;
>  
>         /* do the re-parent */
>         ret = __clk_set_parent(core, parent, p_index);
> @@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
>                 __clk_recalc_accuracies(core);
>         }
>  
> +runtime_put:
> +       clk_pm_runtime_put(core);
>  out:
>         clk_prepare_unlock();
>  
> @@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
>                 goto fail_name;
>         }
>         core->ops = hw->init->ops;
> +       if (dev && pm_runtime_enabled(dev)) {
> +               core->dev = dev;
> +               ret = clk_pm_runtime_get(core);
> +               if (ret)
> +                       goto fail_pm;
> +       }
>         if (dev && dev->driver)
>                 core->owner = dev->driver->owner;
>         core->hw = hw;
> @@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
>         }
>  
>         ret = __clk_core_init(core);
> -       if (!ret)
> +       if (!ret) {
> +               clk_pm_runtime_put(core);
>                 return hw->clk;
> +       }
>  
>         __clk_free_clk(hw->clk);
>         hw->clk = NULL;
> -
>  fail_parents:
>         kfree(core->parents);
>  fail_parent_names_copy:
> @@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
>                 kfree_const(core->parent_names[i]);
>         kfree(core->parent_names);
>  fail_parent_names:
> +       clk_pm_runtime_put(core);
> +fail_pm:
>         kfree_const(core->name);
>  fail_name:
>         kfree(core);
> -- 
> 1.9.1
> 

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-29 20:22         ` Michael Turquette
  0 siblings, 0 replies; 41+ messages in thread
From: Michael Turquette @ 2017-03-29 20:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marek,

Quoting Marek Szyprowski (2017-03-22 04:35:40)
> Registers for some clocks might be located in the SOC area, which are under the
> power domain. To enable access to those registers respective domain has to be
> turned on. Additionally, registers for such clocks will usually loose its
> contents when power domain is turned off, so additional saving and restoring of
> them might be needed in the clock controller driver.
> 
> This patch adds basic infrastructure in the clocks core to allow implementing
> driver for such clocks under power domains. Clock provider can supply a
> struct device pointer, which is the used by clock core for tracking and managing
> clock's controller runtime pm state. Each clk_prepare() operation
> will first call pm_runtime_get_sync() on the supplied device, while
> clk_unprepare() will do pm_runtime_put_sync() at the end.
> 
> Additional calls to pm_runtime_get/put functions are required to ensure that any
> register access (like calculating/changing clock rates and unpreparing/disabling
> unused clocks on boot) will be done with clock controller in runtime resumend
> state.

The above is a bit confusing. Is clk_prepare really special? It seems to
me that every single clk_ops callback will need this?

That leads to my second question: why put this in the clk core? Can the
clk provider driver simply add pm_runtime_{get,put} calls into its
clk_ops callbacks instead? The clk core does not directly touch hardware
(e.g. reading registers) so putting the pm runtime calls into the
provider callbacks should be sufficient.

> 
> When one wants to register clock controller, which make use of this feature, he
> has to:
> 1. Provide a struct device to the core when registering the provider.
> 2. Ensure to enable runtime PM for that device before registering clocks.
> 3. Make sure that the runtime PM status of the controller device reflects
>    the HW state.

Third question: is there a case where more than one device is required?
Is is possible that a single pm_runtime_get call against a single device
will not be enough for some clk provider drivers? If so then this
solution does not scale very well and the clk provider driver will have
to implement this in the clk_ops callbacks (as I mentioned above in my
second question).

Fourth & final question: I'm under the impression that pm runtime calls
be be nested and re-enter, but I want to make sure (Ulf?). For instance
it is highly likely that this new feature would cause something like:

pm_runtime_get()              - called by random driver
-> clk_prepare_enable()       - genpd enables functioal clocks
   -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
      -> clk_prepare_enable() - genpd enables interface or bus clocks

I guess this is safe from the pm_runtime_get/genpd perspective, but want
to make sure first.

Thanks,
Mike

> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
> ---
>  drivers/clk/clk.c | 129 +++++++++++++++++++++++++++++++++++++++++++++++-------
>  1 file changed, 114 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index 0fb39fe217d1..a6001b6e49a7 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -21,6 +21,7 @@
>  #include <linux/of.h>
>  #include <linux/device.h>
>  #include <linux/init.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/sched.h>
>  #include <linux/clkdev.h>
>  
> @@ -46,6 +47,7 @@ struct clk_core {
>         const struct clk_ops    *ops;
>         struct clk_hw           *hw;
>         struct module           *owner;
> +       struct device           *dev;
>         struct clk_core         *parent;
>         const char              **parent_names;
>         struct clk_core         **parents;
> @@ -87,6 +89,26 @@ struct clk {
>         struct hlist_node clks_node;
>  };
>  
> +/***           runtime pm          ***/
> +static int clk_pm_runtime_get(struct clk_core *core)
> +{
> +       int ret = 0;
> +
> +       if (!core->dev)
> +               return 0;
> +
> +       ret = pm_runtime_get_sync(core->dev);
> +       return ret < 0 ? ret : 0;
> +}
> +
> +static void clk_pm_runtime_put(struct clk_core *core)
> +{
> +       if (!core->dev)
> +               return;
> +
> +       pm_runtime_put_sync(core->dev);
> +}
> +
>  /***           locking             ***/
>  static void clk_prepare_lock(void)
>  {
> @@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
>  
>  static bool clk_core_is_prepared(struct clk_core *core)
>  {
> +       bool ret = false;
> +
>         /*
>          * .is_prepared is optional for clocks that can prepare
>          * fall back to software usage counter if it is missing
> @@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core)
>         if (!core->ops->is_prepared)
>                 return core->prepare_count;
>  
> -       return core->ops->is_prepared(core->hw);
> +       if (!clk_pm_runtime_get(core)) {
> +               ret = core->ops->is_prepared(core->hw);
> +               clk_pm_runtime_put(core);
> +       }
> +
> +       return ret;
>  }
>  
>  static bool clk_core_is_enabled(struct clk_core *core)
>  {
> +       bool ret = false;
> +
>         /*
>          * .is_enabled is only mandatory for clocks that gate
>          * fall back to software usage counter if .is_enabled is missing
> @@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core)
>         if (!core->ops->is_enabled)
>                 return core->enable_count;
>  
> -       return core->ops->is_enabled(core->hw);
> +       /*
> +        * Check if clock controller's device is runtime active before
> +        * calling .is_enabled callback. If not, assume that clock is
> +        * disabled, because we might be called from atomic context, from
> +        * which pm_runtime_get() is not allowed.
> +        * This function is called mainly from clk_disable_unused_subtree,
> +        * which ensures proper runtime pm activation of controller before
> +        * taking enable spinlock, but the below check is needed if one tries
> +        * to call it from other places.
> +        */
> +       if (core->dev) {
> +               pm_runtime_get_noresume(core->dev);
> +               if (!pm_runtime_active(core->dev)) {
> +                       ret = false;
> +                       goto done;
> +               }
> +       }
> +
> +       ret = core->ops->is_enabled(core->hw);
> +done:
> +       clk_pm_runtime_put(core);
> +
> +       return ret;
>  }
>  
>  /***    helper functions   ***/
> @@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
>         if (core->ops->unprepare)
>                 core->ops->unprepare(core->hw);
>  
> +       clk_pm_runtime_put(core);
> +
>         trace_clk_unprepare_complete(core);
>         clk_core_unprepare(core->parent);
>  }
> @@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
>                 return 0;
>  
>         if (core->prepare_count == 0) {
> -               ret = clk_core_prepare(core->parent);
> +               ret = clk_pm_runtime_get(core);
>                 if (ret)
>                         return ret;
>  
> +               ret = clk_core_prepare(core->parent);
> +               if (ret)
> +                       goto runtime_put;
> +
>                 trace_clk_prepare(core);
>  
>                 if (core->ops->prepare)
> @@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
>  
>                 trace_clk_prepare_complete(core);
>  
> -               if (ret) {
> -                       clk_core_unprepare(core->parent);
> -                       return ret;
> -               }
> +               if (ret)
> +                       goto unprepare;
>         }
>  
>         core->prepare_count++;
>  
>         return 0;
> +unprepare:
> +       clk_core_unprepare(core->parent);
> +runtime_put:
> +       clk_pm_runtime_put(core);
> +       return ret;
>  }
>  
>  static int clk_core_prepare_lock(struct clk_core *core)
> @@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
>         if (core->flags & CLK_IGNORE_UNUSED)
>                 return;
>  
> +       if (clk_pm_runtime_get(core))
> +               return;
> +
>         if (clk_core_is_prepared(core)) {
>                 trace_clk_unprepare(core);
>                 if (core->ops->unprepare_unused)
> @@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
>                         core->ops->unprepare(core->hw);
>                 trace_clk_unprepare_complete(core);
>         }
> +
> +       clk_pm_runtime_put(core);
>  }
>  
>  static void clk_disable_unused_subtree(struct clk_core *core)
> @@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
>         if (core->flags & CLK_OPS_PARENT_ENABLE)
>                 clk_core_prepare_enable(core->parent);
>  
> +       if (clk_pm_runtime_get(core))
> +               goto unprepare_out;
> +
>         flags = clk_enable_lock();
>  
>         if (core->enable_count)
> @@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
>  
>  unlock_out:
>         clk_enable_unlock(flags);
> +       clk_pm_runtime_put(core);
> +unprepare_out:
>         if (core->flags & CLK_OPS_PARENT_ENABLE)
>                 clk_core_disable_unprepare(core->parent);
>  }
> @@ -1036,9 +1108,13 @@ long clk_get_accuracy(struct clk *clk)
>  static unsigned long clk_recalc(struct clk_core *core,
>                                 unsigned long parent_rate)
>  {
> -       if (core->ops->recalc_rate)
> -               return core->ops->recalc_rate(core->hw, parent_rate);
> -       return parent_rate;
> +       unsigned long rate = parent_rate;
> +
> +       if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
> +               rate = core->ops->recalc_rate(core->hw, parent_rate);
> +               clk_pm_runtime_put(core);
> +       }
> +       return rate;
>  }
>  
>  /**
> @@ -1563,6 +1639,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
>  {
>         struct clk_core *top, *fail_clk;
>         unsigned long rate = req_rate;
> +       int ret = 0;
>  
>         if (!core)
>                 return 0;
> @@ -1579,21 +1656,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
>         if (!top)
>                 return -EINVAL;
>  
> +       ret = clk_pm_runtime_get(core);
> +       if (ret)
> +               return ret;
> +
>         /* notify that we are about to change rates */
>         fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
>         if (fail_clk) {
>                 pr_debug("%s: failed to set %s rate\n", __func__,
>                                 fail_clk->name);
>                 clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
> -               return -EBUSY;
> +               ret = -EBUSY;
> +               goto err;
>         }
>  
>         /* change the rates */
>         clk_change_rate(top);
>  
>         core->req_rate = req_rate;
> +err:
> +       clk_pm_runtime_put(core);
>  
> -       return 0;
> +       return ret;
>  }
>  
>  /**
> @@ -1824,12 +1908,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
>                 p_rate = parent->rate;
>         }
>  
> +       ret = clk_pm_runtime_get(core);
> +       if (ret)
> +               goto out;
> +
>         /* propagate PRE_RATE_CHANGE notifications */
>         ret = __clk_speculate_rates(core, p_rate);
>  
>         /* abort if a driver objects */
>         if (ret & NOTIFY_STOP_MASK)
> -               goto out;
> +               goto runtime_put;
>  
>         /* do the re-parent */
>         ret = __clk_set_parent(core, parent, p_index);
> @@ -1842,6 +1930,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
>                 __clk_recalc_accuracies(core);
>         }
>  
> +runtime_put:
> +       clk_pm_runtime_put(core);
>  out:
>         clk_prepare_unlock();
>  
> @@ -2549,6 +2639,12 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
>                 goto fail_name;
>         }
>         core->ops = hw->init->ops;
> +       if (dev && pm_runtime_enabled(dev)) {
> +               core->dev = dev;
> +               ret = clk_pm_runtime_get(core);
> +               if (ret)
> +                       goto fail_pm;
> +       }
>         if (dev && dev->driver)
>                 core->owner = dev->driver->owner;
>         core->hw = hw;
> @@ -2595,12 +2691,13 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
>         }
>  
>         ret = __clk_core_init(core);
> -       if (!ret)
> +       if (!ret) {
> +               clk_pm_runtime_put(core);
>                 return hw->clk;
> +       }
>  
>         __clk_free_clk(hw->clk);
>         hw->clk = NULL;
> -
>  fail_parents:
>         kfree(core->parents);
>  fail_parent_names_copy:
> @@ -2608,6 +2705,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
>                 kfree_const(core->parent_names[i]);
>         kfree(core->parent_names);
>  fail_parent_names:
> +       clk_pm_runtime_put(core);
> +fail_pm:
>         kfree_const(core->name);
>  fail_name:
>         kfree(core);
> -- 
> 1.9.1
> 

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
  2017-03-29 20:22         ` Michael Turquette
@ 2017-03-30  7:24           ` Marek Szyprowski
  -1 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-30  7:24 UTC (permalink / raw)
  To: Michael Turquette, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel
  Cc: Stephen Boyd, Ulf Hansson, Sylwester Nawrocki, Chanwoo Choi,
	Inki Dae, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

Hi Michael,

On 2017-03-29 22:22, Michael Turquette wrote:
> Quoting Marek Szyprowski (2017-03-22 04:35:40)
>> Registers for some clocks might be located in the SOC area, which are under the
>> power domain. To enable access to those registers respective domain has to be
>> turned on. Additionally, registers for such clocks will usually loose its
>> contents when power domain is turned off, so additional saving and restoring of
>> them might be needed in the clock controller driver.
>>
>> This patch adds basic infrastructure in the clocks core to allow implementing
>> driver for such clocks under power domains. Clock provider can supply a
>> struct device pointer, which is the used by clock core for tracking and managing
>> clock's controller runtime pm state. Each clk_prepare() operation
>> will first call pm_runtime_get_sync() on the supplied device, while
>> clk_unprepare() will do pm_runtime_put_sync() at the end.
>>
>> Additional calls to pm_runtime_get/put functions are required to ensure that any
>> register access (like calculating/changing clock rates and unpreparing/disabling
>> unused clocks on boot) will be done with clock controller in runtime resumend
>> state.
> The above is a bit confusing. Is clk_prepare really special? It seems to
> me that every single clk_ops callback will need this?

clk_prepare/unprepare are special, because they allow sleeping, so they 
are natural
candidates for the place for calling runtime PM operations. 
clk_enable()/disable()
is called under a spinlock, so runtime pm cannot be called efficiently 
there, but
core guarantees that they are called after clk_prepare(), so accessing 
hw registers
is safe. The only remaining calls are not guaranteed to be called always 
after
clk_prepare(), so those additional calls and checks in runtime pm are 
needed there.

> That leads to my second question: why put this in the clk core? Can the
> clk provider driver simply add pm_runtime_{get,put} calls into its
> clk_ops callbacks instead? The clk core does not directly touch hardware
> (e.g. reading registers) so putting the pm runtime calls into the
> provider callbacks should be sufficient.

In theory is should be possible to duplicate all kind of clock build blocks
(gates, muxes, dividers, ...) with additional runtime pm calls. This 
would however
end in much more code and a bit more complicated locking. Implementing 
it in clk
core made the code simpler. It also turned out that runtime pm 
integration is
needed for more that a single clock provider: besides Samsung SoCs 
(Exynos 5433
and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x 
can be
also replaced by runtime PM calls), Ulf mentioned that exactly similar 
pattern
is used for some UX500 SoCs (STE). More will probably come once the 
feature is
in, because for now such drivers simply forces runtime active state as a
workaround or don't instantiate related power domains at all.

It is not that uncommon to have runtime PM integrated in the framework 
(examples:
mmc, scsi). Please not that this is optional for clock providers - if 
they don't
enable runtime PM for the provided clock controller device during clock
registration, the clock core will behave exactly the same way as now.

>> When one wants to register clock controller, which make use of this feature, he
>> has to:
>> 1. Provide a struct device to the core when registering the provider.
>> 2. Ensure to enable runtime PM for that device before registering clocks.
>> 3. Make sure that the runtime PM status of the controller device reflects
>>     the HW state.
> Third question: is there a case where more than one device is required?
> Is is possible that a single pm_runtime_get call against a single device
> will not be enough for some clk provider drivers? If so then this
> solution does not scale very well and the clk provider driver will have
> to implement this in the clk_ops callbacks (as I mentioned above in my
> second question).

This is a generic question about runtime PM. There are various methods to
model hardware relations to control pm/runtime pm state of a set of devices:
child-parent-bus relations (setting child to active state also activates a
parent), gen_pd power domains and sub-domains and recently merged device pm
links, which allows to model relations across the typical child-parent tree
hierarchy. IMHO device core pm related framework provides enough features to
solve the case when one needs to control more than one device - what is
worth to mention - in all cases the client only need to call pm_runtime
funtions on the ONE leaf device, everything else will be handled by pm core.

> Fourth & final question: I'm under the impression that pm runtime calls
> be be nested and re-enter, but I want to make sure (Ulf?). For instance
> it is highly likely that this new feature would cause something like:
>
> pm_runtime_get()              - called by random driver
> -> clk_prepare_enable()       - genpd enables functioal clocks
>     -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
>        -> clk_prepare_enable() - genpd enables interface or bus clocks
>
> I guess this is safe from the pm_runtime_get/genpd perspective, but want
> to make sure first.

Yes, this will work fine after recent fixes. Tested with Exynos IIS ASoC
driver (which is also a clock provider), which in turn is a client for
Exynos Audio Subsystem clock provider.

> Thanks,

 >> [...]

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-30  7:24           ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-03-30  7:24 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Michael,

On 2017-03-29 22:22, Michael Turquette wrote:
> Quoting Marek Szyprowski (2017-03-22 04:35:40)
>> Registers for some clocks might be located in the SOC area, which are under the
>> power domain. To enable access to those registers respective domain has to be
>> turned on. Additionally, registers for such clocks will usually loose its
>> contents when power domain is turned off, so additional saving and restoring of
>> them might be needed in the clock controller driver.
>>
>> This patch adds basic infrastructure in the clocks core to allow implementing
>> driver for such clocks under power domains. Clock provider can supply a
>> struct device pointer, which is the used by clock core for tracking and managing
>> clock's controller runtime pm state. Each clk_prepare() operation
>> will first call pm_runtime_get_sync() on the supplied device, while
>> clk_unprepare() will do pm_runtime_put_sync() at the end.
>>
>> Additional calls to pm_runtime_get/put functions are required to ensure that any
>> register access (like calculating/changing clock rates and unpreparing/disabling
>> unused clocks on boot) will be done with clock controller in runtime resumend
>> state.
> The above is a bit confusing. Is clk_prepare really special? It seems to
> me that every single clk_ops callback will need this?

clk_prepare/unprepare are special, because they allow sleeping, so they 
are natural
candidates for the place for calling runtime PM operations. 
clk_enable()/disable()
is called under a spinlock, so runtime pm cannot be called efficiently 
there, but
core guarantees that they are called after clk_prepare(), so accessing 
hw registers
is safe. The only remaining calls are not guaranteed to be called always 
after
clk_prepare(), so those additional calls and checks in runtime pm are 
needed there.

> That leads to my second question: why put this in the clk core? Can the
> clk provider driver simply add pm_runtime_{get,put} calls into its
> clk_ops callbacks instead? The clk core does not directly touch hardware
> (e.g. reading registers) so putting the pm runtime calls into the
> provider callbacks should be sufficient.

In theory is should be possible to duplicate all kind of clock build blocks
(gates, muxes, dividers, ...) with additional runtime pm calls. This 
would however
end in much more code and a bit more complicated locking. Implementing 
it in clk
core made the code simpler. It also turned out that runtime pm 
integration is
needed for more that a single clock provider: besides Samsung SoCs 
(Exynos 5433
and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x 
can be
also replaced by runtime PM calls), Ulf mentioned that exactly similar 
pattern
is used for some UX500 SoCs (STE). More will probably come once the 
feature is
in, because for now such drivers simply forces runtime active state as a
workaround or don't instantiate related power domains at all.

It is not that uncommon to have runtime PM integrated in the framework 
(examples:
mmc, scsi). Please not that this is optional for clock providers - if 
they don't
enable runtime PM for the provided clock controller device during clock
registration, the clock core will behave exactly the same way as now.

>> When one wants to register clock controller, which make use of this feature, he
>> has to:
>> 1. Provide a struct device to the core when registering the provider.
>> 2. Ensure to enable runtime PM for that device before registering clocks.
>> 3. Make sure that the runtime PM status of the controller device reflects
>>     the HW state.
> Third question: is there a case where more than one device is required?
> Is is possible that a single pm_runtime_get call against a single device
> will not be enough for some clk provider drivers? If so then this
> solution does not scale very well and the clk provider driver will have
> to implement this in the clk_ops callbacks (as I mentioned above in my
> second question).

This is a generic question about runtime PM. There are various methods to
model hardware relations to control pm/runtime pm state of a set of devices:
child-parent-bus relations (setting child to active state also activates a
parent), gen_pd power domains and sub-domains and recently merged device pm
links, which allows to model relations across the typical child-parent tree
hierarchy. IMHO device core pm related framework provides enough features to
solve the case when one needs to control more than one device - what is
worth to mention - in all cases the client only need to call pm_runtime
funtions on the ONE leaf device, everything else will be handled by pm core.

> Fourth & final question: I'm under the impression that pm runtime calls
> be be nested and re-enter, but I want to make sure (Ulf?). For instance
> it is highly likely that this new feature would cause something like:
>
> pm_runtime_get()              - called by random driver
> -> clk_prepare_enable()       - genpd enables functioal clocks
>     -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
>        -> clk_prepare_enable() - genpd enables interface or bus clocks
>
> I guess this is safe from the pm_runtime_get/genpd perspective, but want
> to make sure first.

Yes, this will work fine after recent fixes. Tested with Exynos IIS ASoC
driver (which is also a clock provider), which in turn is a client for
Exynos Audio Subsystem clock provider.

> Thanks,

 >> [...]

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
  2017-03-29 20:22         ` Michael Turquette
@ 2017-03-30 16:29           ` Ulf Hansson
  -1 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-30 16:29 UTC (permalink / raw)
  To: Michael Turquette
  Cc: Marek Szyprowski, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel, Stephen Boyd, Sylwester Nawrocki, Chanwoo Choi,
	Inki Dae, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

[...]

>
> Fourth & final question: I'm under the impression that pm runtime calls
> be be nested and re-enter, but I want to make sure (Ulf?). For instance
> it is highly likely that this new feature would cause something like:
>
> pm_runtime_get()              - called by random driver
> -> clk_prepare_enable()       - genpd enables functioal clocks
>    -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
>       -> clk_prepare_enable() - genpd enables interface or bus clocks
>
> I guess this is safe from the pm_runtime_get/genpd perspective, but want
> to make sure first.

I noticed Marek's reply, which perhaps is sufficient. However, to
clarify also from genpd and runtime PM point of view the re-entrant
issue is safe and it is already quite commonly used.

For example, from a random driver's ->runtime_resume() callback
clk_prepare_enable() is called. When such clock is managed behind a
i2c interface the i2c controller also needs to be runtime resumed,
meaning its driver calls pm_runtime_get_sync(), before it can serve
the request and ungate the clock.

[...]

Kind regards
Uffe

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-30 16:29           ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2017-03-30 16:29 UTC (permalink / raw)
  To: linux-arm-kernel

[...]

>
> Fourth & final question: I'm under the impression that pm runtime calls
> be be nested and re-enter, but I want to make sure (Ulf?). For instance
> it is highly likely that this new feature would cause something like:
>
> pm_runtime_get()              - called by random driver
> -> clk_prepare_enable()       - genpd enables functioal clocks
>    -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
>       -> clk_prepare_enable() - genpd enables interface or bus clocks
>
> I guess this is safe from the pm_runtime_get/genpd perspective, but want
> to make sure first.

I noticed Marek's reply, which perhaps is sufficient. However, to
clarify also from genpd and runtime PM point of view the re-entrant
issue is safe and it is already quite commonly used.

For example, from a random driver's ->runtime_resume() callback
clk_prepare_enable() is called. When such clock is managed behind a
i2c interface the i2c controller also needs to be runtime resumed,
meaning its driver calls pm_runtime_get_sync(), before it can serve
the request and ungate the clock.

[...]

Kind regards
Uffe

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
  2017-03-30  7:24           ` Marek Szyprowski
  (?)
@ 2017-03-30 17:11             ` Michael Turquette
  -1 siblings, 0 replies; 41+ messages in thread
From: Michael Turquette @ 2017-03-30 17:11 UTC (permalink / raw)
  To: Marek Szyprowski, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel
  Cc: Stephen Boyd, Ulf Hansson, Sylwester Nawrocki, Chanwoo Choi,
	Inki Dae, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

Quoting Marek Szyprowski (2017-03-30 00:24:15)
> Hi Michael,
> =

> On 2017-03-29 22:22, Michael Turquette wrote:
> > Quoting Marek Szyprowski (2017-03-22 04:35:40)
> >> Registers for some clocks might be located in the SOC area, which are =
under the
> >> power domain. To enable access to those registers respective domain ha=
s to be
> >> turned on. Additionally, registers for such clocks will usually loose =
its
> >> contents when power domain is turned off, so additional saving and res=
toring of
> >> them might be needed in the clock controller driver.
> >>
> >> This patch adds basic infrastructure in the clocks core to allow imple=
menting
> >> driver for such clocks under power domains. Clock provider can supply a
> >> struct device pointer, which is the used by clock core for tracking an=
d managing
> >> clock's controller runtime pm state. Each clk_prepare() operation
> >> will first call pm_runtime_get_sync() on the supplied device, while
> >> clk_unprepare() will do pm_runtime_put_sync() at the end.
> >>
> >> Additional calls to pm_runtime_get/put functions are required to ensur=
e that any
> >> register access (like calculating/changing clock rates and unpreparing=
/disabling
> >> unused clocks on boot) will be done with clock controller in runtime r=
esumend
> >> state.
> > The above is a bit confusing. Is clk_prepare really special? It seems to
> > me that every single clk_ops callback will need this?
> =

> clk_prepare/unprepare are special, because they allow sleeping, so they =

> are natural
> candidates for the place for calling runtime PM operations. =

> clk_enable()/disable()
> is called under a spinlock, so runtime pm cannot be called efficiently =

> there, but
> core guarantees that they are called after clk_prepare(), so accessing =

> hw registers
> is safe. The only remaining calls are not guaranteed to be called always =

> after
> clk_prepare(), so those additional calls and checks in runtime pm are =

> needed there.

Right, so any call that holds prepare_lock and might touch the hardware
needs to first call pm_runtime_get_sync.

> =

> > That leads to my second question: why put this in the clk core? Can the
> > clk provider driver simply add pm_runtime_{get,put} calls into its
> > clk_ops callbacks instead? The clk core does not directly touch hardware
> > (e.g. reading registers) so putting the pm runtime calls into the
> > provider callbacks should be sufficient.
> =

> In theory is should be possible to duplicate all kind of clock build bloc=
ks
> (gates, muxes, dividers, ...) with additional runtime pm calls. This =

> would however
> end in much more code and a bit more complicated locking. Implementing =

> it in clk
> core made the code simpler. It also turned out that runtime pm =

> integration is
> needed for more that a single clock provider: besides Samsung SoCs =

> (Exynos 5433
> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x =

> can be
> also replaced by runtime PM calls), Ulf mentioned that exactly similar =

> pattern
> is used for some UX500 SoCs (STE). More will probably come once the =

> feature is
> in, because for now such drivers simply forces runtime active state as a
> workaround or don't instantiate related power domains at all.

I agree with the above, but I'm also wondering about the folks that use
regmap internally to their own clock provider drivers. I guess they will
have the option to either do their own thing or use this framework-level
solution.

I've started reviewing the code itself and will respond to those mails
separately.

Thanks,
Mike

> =

> It is not that uncommon to have runtime PM integrated in the framework =

> (examples:
> mmc, scsi). Please not that this is optional for clock providers - if =

> they don't
> enable runtime PM for the provided clock controller device during clock
> registration, the clock core will behave exactly the same way as now.
> =

> >> When one wants to register clock controller, which make use of this fe=
ature, he
> >> has to:
> >> 1. Provide a struct device to the core when registering the provider.
> >> 2. Ensure to enable runtime PM for that device before registering cloc=
ks.
> >> 3. Make sure that the runtime PM status of the controller device refle=
cts
> >>     the HW state.
> > Third question: is there a case where more than one device is required?
> > Is is possible that a single pm_runtime_get call against a single device
> > will not be enough for some clk provider drivers? If so then this
> > solution does not scale very well and the clk provider driver will have
> > to implement this in the clk_ops callbacks (as I mentioned above in my
> > second question).
> =

> This is a generic question about runtime PM. There are various methods to
> model hardware relations to control pm/runtime pm state of a set of devic=
es:
> child-parent-bus relations (setting child to active state also activates a
> parent), gen_pd power domains and sub-domains and recently merged device =
pm
> links, which allows to model relations across the typical child-parent tr=
ee
> hierarchy. IMHO device core pm related framework provides enough features=
 to
> solve the case when one needs to control more than one device - what is
> worth to mention - in all cases the client only need to call pm_runtime
> funtions on the ONE leaf device, everything else will be handled by pm co=
re.
> =

> > Fourth & final question: I'm under the impression that pm runtime calls
> > be be nested and re-enter, but I want to make sure (Ulf?). For instance
> > it is highly likely that this new feature would cause something like:
> >
> > pm_runtime_get()              - called by random driver
> > -> clk_prepare_enable()       - genpd enables functioal clocks
> >     -> pm_runtime_get()        - called by clk_pm_runtime_get in clk co=
re
> >        -> clk_prepare_enable() - genpd enables interface or bus clocks
> >
> > I guess this is safe from the pm_runtime_get/genpd perspective, but want
> > to make sure first.
> =

> Yes, this will work fine after recent fixes. Tested with Exynos IIS ASoC
> driver (which is also a clock provider), which in turn is a client for
> Exynos Audio Subsystem clock provider.
> =

> > Thanks,
> =

>  >> [...]
> =

> Best regards
> -- =

> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
>=20

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-30 17:11             ` Michael Turquette
  0 siblings, 0 replies; 41+ messages in thread
From: Michael Turquette @ 2017-03-30 17:11 UTC (permalink / raw)
  To: Marek Szyprowski, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel
  Cc: Stephen Boyd, Ulf Hansson, Sylwester Nawrocki, Chanwoo Choi,
	Inki Dae, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

Quoting Marek Szyprowski (2017-03-30 00:24:15)
> Hi Michael,
> 
> On 2017-03-29 22:22, Michael Turquette wrote:
> > Quoting Marek Szyprowski (2017-03-22 04:35:40)
> >> Registers for some clocks might be located in the SOC area, which are under the
> >> power domain. To enable access to those registers respective domain has to be
> >> turned on. Additionally, registers for such clocks will usually loose its
> >> contents when power domain is turned off, so additional saving and restoring of
> >> them might be needed in the clock controller driver.
> >>
> >> This patch adds basic infrastructure in the clocks core to allow implementing
> >> driver for such clocks under power domains. Clock provider can supply a
> >> struct device pointer, which is the used by clock core for tracking and managing
> >> clock's controller runtime pm state. Each clk_prepare() operation
> >> will first call pm_runtime_get_sync() on the supplied device, while
> >> clk_unprepare() will do pm_runtime_put_sync() at the end.
> >>
> >> Additional calls to pm_runtime_get/put functions are required to ensure that any
> >> register access (like calculating/changing clock rates and unpreparing/disabling
> >> unused clocks on boot) will be done with clock controller in runtime resumend
> >> state.
> > The above is a bit confusing. Is clk_prepare really special? It seems to
> > me that every single clk_ops callback will need this?
> 
> clk_prepare/unprepare are special, because they allow sleeping, so they 
> are natural
> candidates for the place for calling runtime PM operations. 
> clk_enable()/disable()
> is called under a spinlock, so runtime pm cannot be called efficiently 
> there, but
> core guarantees that they are called after clk_prepare(), so accessing 
> hw registers
> is safe. The only remaining calls are not guaranteed to be called always 
> after
> clk_prepare(), so those additional calls and checks in runtime pm are 
> needed there.

Right, so any call that holds prepare_lock and might touch the hardware
needs to first call pm_runtime_get_sync.

> 
> > That leads to my second question: why put this in the clk core? Can the
> > clk provider driver simply add pm_runtime_{get,put} calls into its
> > clk_ops callbacks instead? The clk core does not directly touch hardware
> > (e.g. reading registers) so putting the pm runtime calls into the
> > provider callbacks should be sufficient.
> 
> In theory is should be possible to duplicate all kind of clock build blocks
> (gates, muxes, dividers, ...) with additional runtime pm calls. This 
> would however
> end in much more code and a bit more complicated locking. Implementing 
> it in clk
> core made the code simpler. It also turned out that runtime pm 
> integration is
> needed for more that a single clock provider: besides Samsung SoCs 
> (Exynos 5433
> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x 
> can be
> also replaced by runtime PM calls), Ulf mentioned that exactly similar 
> pattern
> is used for some UX500 SoCs (STE). More will probably come once the 
> feature is
> in, because for now such drivers simply forces runtime active state as a
> workaround or don't instantiate related power domains at all.

I agree with the above, but I'm also wondering about the folks that use
regmap internally to their own clock provider drivers. I guess they will
have the option to either do their own thing or use this framework-level
solution.

I've started reviewing the code itself and will respond to those mails
separately.

Thanks,
Mike

> 
> It is not that uncommon to have runtime PM integrated in the framework 
> (examples:
> mmc, scsi). Please not that this is optional for clock providers - if 
> they don't
> enable runtime PM for the provided clock controller device during clock
> registration, the clock core will behave exactly the same way as now.
> 
> >> When one wants to register clock controller, which make use of this feature, he
> >> has to:
> >> 1. Provide a struct device to the core when registering the provider.
> >> 2. Ensure to enable runtime PM for that device before registering clocks.
> >> 3. Make sure that the runtime PM status of the controller device reflects
> >>     the HW state.
> > Third question: is there a case where more than one device is required?
> > Is is possible that a single pm_runtime_get call against a single device
> > will not be enough for some clk provider drivers? If so then this
> > solution does not scale very well and the clk provider driver will have
> > to implement this in the clk_ops callbacks (as I mentioned above in my
> > second question).
> 
> This is a generic question about runtime PM. There are various methods to
> model hardware relations to control pm/runtime pm state of a set of devices:
> child-parent-bus relations (setting child to active state also activates a
> parent), gen_pd power domains and sub-domains and recently merged device pm
> links, which allows to model relations across the typical child-parent tree
> hierarchy. IMHO device core pm related framework provides enough features to
> solve the case when one needs to control more than one device - what is
> worth to mention - in all cases the client only need to call pm_runtime
> funtions on the ONE leaf device, everything else will be handled by pm core.
> 
> > Fourth & final question: I'm under the impression that pm runtime calls
> > be be nested and re-enter, but I want to make sure (Ulf?). For instance
> > it is highly likely that this new feature would cause something like:
> >
> > pm_runtime_get()              - called by random driver
> > -> clk_prepare_enable()       - genpd enables functioal clocks
> >     -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
> >        -> clk_prepare_enable() - genpd enables interface or bus clocks
> >
> > I guess this is safe from the pm_runtime_get/genpd perspective, but want
> > to make sure first.
> 
> Yes, this will work fine after recent fixes. Tested with Exynos IIS ASoC
> driver (which is also a clock provider), which in turn is a client for
> Exynos Audio Subsystem clock provider.
> 
> > Thanks,
> 
>  >> [...]
> 
> Best regards
> -- 
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
> 

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-03-30 17:11             ` Michael Turquette
  0 siblings, 0 replies; 41+ messages in thread
From: Michael Turquette @ 2017-03-30 17:11 UTC (permalink / raw)
  To: linux-arm-kernel

Quoting Marek Szyprowski (2017-03-30 00:24:15)
> Hi Michael,
> 
> On 2017-03-29 22:22, Michael Turquette wrote:
> > Quoting Marek Szyprowski (2017-03-22 04:35:40)
> >> Registers for some clocks might be located in the SOC area, which are under the
> >> power domain. To enable access to those registers respective domain has to be
> >> turned on. Additionally, registers for such clocks will usually loose its
> >> contents when power domain is turned off, so additional saving and restoring of
> >> them might be needed in the clock controller driver.
> >>
> >> This patch adds basic infrastructure in the clocks core to allow implementing
> >> driver for such clocks under power domains. Clock provider can supply a
> >> struct device pointer, which is the used by clock core for tracking and managing
> >> clock's controller runtime pm state. Each clk_prepare() operation
> >> will first call pm_runtime_get_sync() on the supplied device, while
> >> clk_unprepare() will do pm_runtime_put_sync() at the end.
> >>
> >> Additional calls to pm_runtime_get/put functions are required to ensure that any
> >> register access (like calculating/changing clock rates and unpreparing/disabling
> >> unused clocks on boot) will be done with clock controller in runtime resumend
> >> state.
> > The above is a bit confusing. Is clk_prepare really special? It seems to
> > me that every single clk_ops callback will need this?
> 
> clk_prepare/unprepare are special, because they allow sleeping, so they 
> are natural
> candidates for the place for calling runtime PM operations. 
> clk_enable()/disable()
> is called under a spinlock, so runtime pm cannot be called efficiently 
> there, but
> core guarantees that they are called after clk_prepare(), so accessing 
> hw registers
> is safe. The only remaining calls are not guaranteed to be called always 
> after
> clk_prepare(), so those additional calls and checks in runtime pm are 
> needed there.

Right, so any call that holds prepare_lock and might touch the hardware
needs to first call pm_runtime_get_sync.

> 
> > That leads to my second question: why put this in the clk core? Can the
> > clk provider driver simply add pm_runtime_{get,put} calls into its
> > clk_ops callbacks instead? The clk core does not directly touch hardware
> > (e.g. reading registers) so putting the pm runtime calls into the
> > provider callbacks should be sufficient.
> 
> In theory is should be possible to duplicate all kind of clock build blocks
> (gates, muxes, dividers, ...) with additional runtime pm calls. This 
> would however
> end in much more code and a bit more complicated locking. Implementing 
> it in clk
> core made the code simpler. It also turned out that runtime pm 
> integration is
> needed for more that a single clock provider: besides Samsung SoCs 
> (Exynos 5433
> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x 
> can be
> also replaced by runtime PM calls), Ulf mentioned that exactly similar 
> pattern
> is used for some UX500 SoCs (STE). More will probably come once the 
> feature is
> in, because for now such drivers simply forces runtime active state as a
> workaround or don't instantiate related power domains at all.

I agree with the above, but I'm also wondering about the folks that use
regmap internally to their own clock provider drivers. I guess they will
have the option to either do their own thing or use this framework-level
solution.

I've started reviewing the code itself and will respond to those mails
separately.

Thanks,
Mike

> 
> It is not that uncommon to have runtime PM integrated in the framework 
> (examples:
> mmc, scsi). Please not that this is optional for clock providers - if 
> they don't
> enable runtime PM for the provided clock controller device during clock
> registration, the clock core will behave exactly the same way as now.
> 
> >> When one wants to register clock controller, which make use of this feature, he
> >> has to:
> >> 1. Provide a struct device to the core when registering the provider.
> >> 2. Ensure to enable runtime PM for that device before registering clocks.
> >> 3. Make sure that the runtime PM status of the controller device reflects
> >>     the HW state.
> > Third question: is there a case where more than one device is required?
> > Is is possible that a single pm_runtime_get call against a single device
> > will not be enough for some clk provider drivers? If so then this
> > solution does not scale very well and the clk provider driver will have
> > to implement this in the clk_ops callbacks (as I mentioned above in my
> > second question).
> 
> This is a generic question about runtime PM. There are various methods to
> model hardware relations to control pm/runtime pm state of a set of devices:
> child-parent-bus relations (setting child to active state also activates a
> parent), gen_pd power domains and sub-domains and recently merged device pm
> links, which allows to model relations across the typical child-parent tree
> hierarchy. IMHO device core pm related framework provides enough features to
> solve the case when one needs to control more than one device - what is
> worth to mention - in all cases the client only need to call pm_runtime
> funtions on the ONE leaf device, everything else will be handled by pm core.
> 
> > Fourth & final question: I'm under the impression that pm runtime calls
> > be be nested and re-enter, but I want to make sure (Ulf?). For instance
> > it is highly likely that this new feature would cause something like:
> >
> > pm_runtime_get()              - called by random driver
> > -> clk_prepare_enable()       - genpd enables functioal clocks
> >     -> pm_runtime_get()        - called by clk_pm_runtime_get in clk core
> >        -> clk_prepare_enable() - genpd enables interface or bus clocks
> >
> > I guess this is safe from the pm_runtime_get/genpd perspective, but want
> > to make sure first.
> 
> Yes, this will work fine after recent fixes. Tested with Exynos IIS ASoC
> driver (which is also a clock provider), which in turn is a client for
> Exynos Audio Subsystem clock provider.
> 
> > Thanks,
> 
>  >> [...]
> 
> Best regards
> -- 
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
> 

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
  2017-03-30 17:11             ` Michael Turquette
@ 2017-04-06 12:46               ` Marek Szyprowski
  -1 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-04-06 12:46 UTC (permalink / raw)
  To: Michael Turquette, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel, Stephen Boyd
  Cc: Ulf Hansson, Sylwester Nawrocki, Chanwoo Choi, Inki Dae,
	Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

Hi Michael,

On 2017-03-30 19:11, Michael Turquette wrote:
> Quoting Marek Szyprowski (2017-03-30 00:24:15)
>> On 2017-03-29 22:22, Michael Turquette wrote:
>>> Quoting Marek Szyprowski (2017-03-22 04:35:40)
>>>> Registers for some clocks might be located in the SOC area, which are under the
>>>> power domain. To enable access to those registers respective domain has to be
>>>> turned on. Additionally, registers for such clocks will usually loose its
>>>> contents when power domain is turned off, so additional saving and restoring of
>>>> them might be needed in the clock controller driver.
>>>>
>>>> This patch adds basic infrastructure in the clocks core to allow implementing
>>>> driver for such clocks under power domains. Clock provider can supply a
>>>> struct device pointer, which is the used by clock core for tracking and managing
>>>> clock's controller runtime pm state. Each clk_prepare() operation
>>>> will first call pm_runtime_get_sync() on the supplied device, while
>>>> clk_unprepare() will do pm_runtime_put_sync() at the end.
>>>>
>>>> Additional calls to pm_runtime_get/put functions are required to ensure that any
>>>> register access (like calculating/changing clock rates and unpreparing/disabling
>>>> unused clocks on boot) will be done with clock controller in runtime resumend
>>>> state.
>>> The above is a bit confusing. Is clk_prepare really special? It seems to
>>> me that every single clk_ops callback will need this?
>> clk_prepare/unprepare are special, because they allow sleeping, so they
>> are natural
>> candidates for the place for calling runtime PM operations.
>> clk_enable()/disable()
>> is called under a spinlock, so runtime pm cannot be called efficiently
>> there, but
>> core guarantees that they are called after clk_prepare(), so accessing
>> hw registers
>> is safe. The only remaining calls are not guaranteed to be called always
>> after
>> clk_prepare(), so those additional calls and checks in runtime pm are
>> needed there.
> Right, so any call that holds prepare_lock and might touch the hardware
> needs to first call pm_runtime_get_sync.
>
>>> That leads to my second question: why put this in the clk core? Can the
>>> clk provider driver simply add pm_runtime_{get,put} calls into its
>>> clk_ops callbacks instead? The clk core does not directly touch hardware
>>> (e.g. reading registers) so putting the pm runtime calls into the
>>> provider callbacks should be sufficient.
>> In theory is should be possible to duplicate all kind of clock build blocks
>> (gates, muxes, dividers, ...) with additional runtime pm calls. This
>> would however
>> end in much more code and a bit more complicated locking. Implementing
>> it in clk
>> core made the code simpler. It also turned out that runtime pm
>> integration is
>> needed for more that a single clock provider: besides Samsung SoCs
>> (Exynos 5433
>> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x
>> can be
>> also replaced by runtime PM calls), Ulf mentioned that exactly similar
>> pattern
>> is used for some UX500 SoCs (STE). More will probably come once the
>> feature is
>> in, because for now such drivers simply forces runtime active state as a
>> workaround or don't instantiate related power domains at all.
> I agree with the above, but I'm also wondering about the folks that use
> regmap internally to their own clock provider drivers. I guess they will
> have the option to either do their own thing or use this framework-level
> solution.
>
> I've started reviewing the code itself and will respond to those mails
> separately.

Is there a chance to get your comments soon? I would really like to give
this patchset a try in linux-next for a few days, but for now Sylwester
waits for you.

Stephen: what is your opinion on this patchset? Would you like to give it
a try in -next?

 > ...

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-04-06 12:46               ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-04-06 12:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Michael,

On 2017-03-30 19:11, Michael Turquette wrote:
> Quoting Marek Szyprowski (2017-03-30 00:24:15)
>> On 2017-03-29 22:22, Michael Turquette wrote:
>>> Quoting Marek Szyprowski (2017-03-22 04:35:40)
>>>> Registers for some clocks might be located in the SOC area, which are under the
>>>> power domain. To enable access to those registers respective domain has to be
>>>> turned on. Additionally, registers for such clocks will usually loose its
>>>> contents when power domain is turned off, so additional saving and restoring of
>>>> them might be needed in the clock controller driver.
>>>>
>>>> This patch adds basic infrastructure in the clocks core to allow implementing
>>>> driver for such clocks under power domains. Clock provider can supply a
>>>> struct device pointer, which is the used by clock core for tracking and managing
>>>> clock's controller runtime pm state. Each clk_prepare() operation
>>>> will first call pm_runtime_get_sync() on the supplied device, while
>>>> clk_unprepare() will do pm_runtime_put_sync() at the end.
>>>>
>>>> Additional calls to pm_runtime_get/put functions are required to ensure that any
>>>> register access (like calculating/changing clock rates and unpreparing/disabling
>>>> unused clocks on boot) will be done with clock controller in runtime resumend
>>>> state.
>>> The above is a bit confusing. Is clk_prepare really special? It seems to
>>> me that every single clk_ops callback will need this?
>> clk_prepare/unprepare are special, because they allow sleeping, so they
>> are natural
>> candidates for the place for calling runtime PM operations.
>> clk_enable()/disable()
>> is called under a spinlock, so runtime pm cannot be called efficiently
>> there, but
>> core guarantees that they are called after clk_prepare(), so accessing
>> hw registers
>> is safe. The only remaining calls are not guaranteed to be called always
>> after
>> clk_prepare(), so those additional calls and checks in runtime pm are
>> needed there.
> Right, so any call that holds prepare_lock and might touch the hardware
> needs to first call pm_runtime_get_sync.
>
>>> That leads to my second question: why put this in the clk core? Can the
>>> clk provider driver simply add pm_runtime_{get,put} calls into its
>>> clk_ops callbacks instead? The clk core does not directly touch hardware
>>> (e.g. reading registers) so putting the pm runtime calls into the
>>> provider callbacks should be sufficient.
>> In theory is should be possible to duplicate all kind of clock build blocks
>> (gates, muxes, dividers, ...) with additional runtime pm calls. This
>> would however
>> end in much more code and a bit more complicated locking. Implementing
>> it in clk
>> core made the code simpler. It also turned out that runtime pm
>> integration is
>> needed for more that a single clock provider: besides Samsung SoCs
>> (Exynos 5433
>> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 542x
>> can be
>> also replaced by runtime PM calls), Ulf mentioned that exactly similar
>> pattern
>> is used for some UX500 SoCs (STE). More will probably come once the
>> feature is
>> in, because for now such drivers simply forces runtime active state as a
>> workaround or don't instantiate related power domains at all.
> I agree with the above, but I'm also wondering about the folks that use
> regmap internally to their own clock provider drivers. I guess they will
> have the option to either do their own thing or use this framework-level
> solution.
>
> I've started reviewing the code itself and will respond to those mails
> separately.

Is there a chance to get your comments soon? I would really like to give
this patchset a try in linux-next for a few days, but for now Sylwester
waits for you.

Stephen: what is your opinion on this patchset? Would you like to give it
a try in -next?

 > ...

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* Re: [PATCH v6 1/4] clk: Add support for runtime PM
  2017-04-06 12:46               ` Marek Szyprowski
@ 2017-05-22 12:34                 ` Marek Szyprowski
  -1 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-05-22 12:34 UTC (permalink / raw)
  To: Michael Turquette, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel, Stephen Boyd
  Cc: Ulf Hansson, Sylwester Nawrocki, Chanwoo Choi, Inki Dae,
	Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

Hi All,

On 2017-04-06 14:46, Marek Szyprowski wrote:
> Hi Michael,
>
> On 2017-03-30 19:11, Michael Turquette wrote:
>> Quoting Marek Szyprowski (2017-03-30 00:24:15)
>>> On 2017-03-29 22:22, Michael Turquette wrote:
>>>> Quoting Marek Szyprowski (2017-03-22 04:35:40)
>>>>> Registers for some clocks might be located in the SOC area, which 
>>>>> are under the
>>>>> power domain. To enable access to those registers respective 
>>>>> domain has to be
>>>>> turned on. Additionally, registers for such clocks will usually 
>>>>> loose its
>>>>> contents when power domain is turned off, so additional saving and 
>>>>> restoring of
>>>>> them might be needed in the clock controller driver.
>>>>>
>>>>> This patch adds basic infrastructure in the clocks core to allow 
>>>>> implementing
>>>>> driver for such clocks under power domains. Clock provider can 
>>>>> supply a
>>>>> struct device pointer, which is the used by clock core for 
>>>>> tracking and managing
>>>>> clock's controller runtime pm state. Each clk_prepare() operation
>>>>> will first call pm_runtime_get_sync() on the supplied device, while
>>>>> clk_unprepare() will do pm_runtime_put_sync() at the end.
>>>>>
>>>>> Additional calls to pm_runtime_get/put functions are required to 
>>>>> ensure that any
>>>>> register access (like calculating/changing clock rates and 
>>>>> unpreparing/disabling
>>>>> unused clocks on boot) will be done with clock controller in 
>>>>> runtime resumend
>>>>> state.
>>>> The above is a bit confusing. Is clk_prepare really special? It 
>>>> seems to
>>>> me that every single clk_ops callback will need this?
>>> clk_prepare/unprepare are special, because they allow sleeping, so they
>>> are natural
>>> candidates for the place for calling runtime PM operations.
>>> clk_enable()/disable()
>>> is called under a spinlock, so runtime pm cannot be called efficiently
>>> there, but
>>> core guarantees that they are called after clk_prepare(), so accessing
>>> hw registers
>>> is safe. The only remaining calls are not guaranteed to be called 
>>> always
>>> after
>>> clk_prepare(), so those additional calls and checks in runtime pm are
>>> needed there.
>> Right, so any call that holds prepare_lock and might touch the hardware
>> needs to first call pm_runtime_get_sync.
>>
>>>> That leads to my second question: why put this in the clk core? Can 
>>>> the
>>>> clk provider driver simply add pm_runtime_{get,put} calls into its
>>>> clk_ops callbacks instead? The clk core does not directly touch 
>>>> hardware
>>>> (e.g. reading registers) so putting the pm runtime calls into the
>>>> provider callbacks should be sufficient.
>>> In theory is should be possible to duplicate all kind of clock build 
>>> blocks
>>> (gates, muxes, dividers, ...) with additional runtime pm calls. This
>>> would however
>>> end in much more code and a bit more complicated locking. Implementing
>>> it in clk
>>> core made the code simpler. It also turned out that runtime pm
>>> integration is
>>> needed for more that a single clock provider: besides Samsung SoCs
>>> (Exynos 5433
>>> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 
>>> 542x
>>> can be
>>> also replaced by runtime PM calls), Ulf mentioned that exactly similar
>>> pattern
>>> is used for some UX500 SoCs (STE). More will probably come once the
>>> feature is
>>> in, because for now such drivers simply forces runtime active state 
>>> as a
>>> workaround or don't instantiate related power domains at all.
>> I agree with the above, but I'm also wondering about the folks that use
>> regmap internally to their own clock provider drivers. I guess they will
>> have the option to either do their own thing or use this framework-level
>> solution.
>>
>> I've started reviewing the code itself and will respond to those mails
>> separately.
>
> Is there a chance to get your comments soon? I would really like to give
> this patchset a try in linux-next for a few days, but for now Sylwester
> waits for you.
>
> Stephen: what is your opinion on this patchset? Would you like to give it
> a try in -next?
>

Mike, Stephen: any comments? We already missed v4.12 merge window...

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

* [PATCH v6 1/4] clk: Add support for runtime PM
@ 2017-05-22 12:34                 ` Marek Szyprowski
  0 siblings, 0 replies; 41+ messages in thread
From: Marek Szyprowski @ 2017-05-22 12:34 UTC (permalink / raw)
  To: linux-arm-kernel

Hi All,

On 2017-04-06 14:46, Marek Szyprowski wrote:
> Hi Michael,
>
> On 2017-03-30 19:11, Michael Turquette wrote:
>> Quoting Marek Szyprowski (2017-03-30 00:24:15)
>>> On 2017-03-29 22:22, Michael Turquette wrote:
>>>> Quoting Marek Szyprowski (2017-03-22 04:35:40)
>>>>> Registers for some clocks might be located in the SOC area, which 
>>>>> are under the
>>>>> power domain. To enable access to those registers respective 
>>>>> domain has to be
>>>>> turned on. Additionally, registers for such clocks will usually 
>>>>> loose its
>>>>> contents when power domain is turned off, so additional saving and 
>>>>> restoring of
>>>>> them might be needed in the clock controller driver.
>>>>>
>>>>> This patch adds basic infrastructure in the clocks core to allow 
>>>>> implementing
>>>>> driver for such clocks under power domains. Clock provider can 
>>>>> supply a
>>>>> struct device pointer, which is the used by clock core for 
>>>>> tracking and managing
>>>>> clock's controller runtime pm state. Each clk_prepare() operation
>>>>> will first call pm_runtime_get_sync() on the supplied device, while
>>>>> clk_unprepare() will do pm_runtime_put_sync() at the end.
>>>>>
>>>>> Additional calls to pm_runtime_get/put functions are required to 
>>>>> ensure that any
>>>>> register access (like calculating/changing clock rates and 
>>>>> unpreparing/disabling
>>>>> unused clocks on boot) will be done with clock controller in 
>>>>> runtime resumend
>>>>> state.
>>>> The above is a bit confusing. Is clk_prepare really special? It 
>>>> seems to
>>>> me that every single clk_ops callback will need this?
>>> clk_prepare/unprepare are special, because they allow sleeping, so they
>>> are natural
>>> candidates for the place for calling runtime PM operations.
>>> clk_enable()/disable()
>>> is called under a spinlock, so runtime pm cannot be called efficiently
>>> there, but
>>> core guarantees that they are called after clk_prepare(), so accessing
>>> hw registers
>>> is safe. The only remaining calls are not guaranteed to be called 
>>> always
>>> after
>>> clk_prepare(), so those additional calls and checks in runtime pm are
>>> needed there.
>> Right, so any call that holds prepare_lock and might touch the hardware
>> needs to first call pm_runtime_get_sync.
>>
>>>> That leads to my second question: why put this in the clk core? Can 
>>>> the
>>>> clk provider driver simply add pm_runtime_{get,put} calls into its
>>>> clk_ops callbacks instead? The clk core does not directly touch 
>>>> hardware
>>>> (e.g. reading registers) so putting the pm runtime calls into the
>>>> provider callbacks should be sufficient.
>>> In theory is should be possible to duplicate all kind of clock build 
>>> blocks
>>> (gates, muxes, dividers, ...) with additional runtime pm calls. This
>>> would however
>>> end in much more code and a bit more complicated locking. Implementing
>>> it in clk
>>> core made the code simpler. It also turned out that runtime pm
>>> integration is
>>> needed for more that a single clock provider: besides Samsung SoCs
>>> (Exynos 5433
>>> and newer, Exynos 4412 ISP, Exynos Audio Subsystem, hacks in Exynos 
>>> 542x
>>> can be
>>> also replaced by runtime PM calls), Ulf mentioned that exactly similar
>>> pattern
>>> is used for some UX500 SoCs (STE). More will probably come once the
>>> feature is
>>> in, because for now such drivers simply forces runtime active state 
>>> as a
>>> workaround or don't instantiate related power domains at all.
>> I agree with the above, but I'm also wondering about the folks that use
>> regmap internally to their own clock provider drivers. I guess they will
>> have the option to either do their own thing or use this framework-level
>> solution.
>>
>> I've started reviewing the code itself and will respond to those mails
>> separately.
>
> Is there a chance to get your comments soon? I would really like to give
> this patchset a try in linux-next for a few days, but for now Sylwester
> waits for you.
>
> Stephen: what is your opinion on this patchset? Would you like to give it
> a try in -next?
>

Mike, Stephen: any comments? We already missed v4.12 merge window...

Best regards
-- 
Marek Szyprowski, PhD
Samsung R&D Institute Poland

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

end of thread, other threads:[~2017-05-22 12:34 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CGME20170322113553eucas1p20875eb2da524fd090c6a7cc725c9d944@eucas1p2.samsung.com>
2017-03-22 11:35 ` [PATCH v6 0/4] Add runtime PM support for clocks (on Exynos SoC example) Marek Szyprowski
2017-03-22 11:35   ` Marek Szyprowski
2017-03-22 11:35   ` Marek Szyprowski
     [not found]   ` <CGME20170322113554eucas1p27471f0de278c55d5dce7fdf120395e09@eucas1p2.samsung.com>
2017-03-22 11:35     ` [PATCH v6 1/4] clk: Add support for runtime PM Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-29 20:22       ` Michael Turquette
2017-03-29 20:22         ` Michael Turquette
2017-03-29 20:22         ` Michael Turquette
2017-03-30  7:24         ` Marek Szyprowski
2017-03-30  7:24           ` Marek Szyprowski
2017-03-30 17:11           ` Michael Turquette
2017-03-30 17:11             ` Michael Turquette
2017-03-30 17:11             ` Michael Turquette
2017-04-06 12:46             ` Marek Szyprowski
2017-04-06 12:46               ` Marek Szyprowski
2017-05-22 12:34               ` Marek Szyprowski
2017-05-22 12:34                 ` Marek Szyprowski
2017-03-30 16:29         ` Ulf Hansson
2017-03-30 16:29           ` Ulf Hansson
     [not found]   ` <CGME20170322113555eucas1p261e9641f1ea64d2b95501dde51792b58@eucas1p2.samsung.com>
2017-03-22 11:35     ` [PATCH v6 2/4] clk: samsung: " Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
     [not found]   ` <CGME20170322113555eucas1p2e6d19d66de5dc48b9f34e4b639759f78@eucas1p2.samsung.com>
2017-03-22 11:35     ` [PATCH v6 3/4] clk: samsung: exynos5433: Add runtime PM support Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-22 11:52       ` Ulf Hansson
2017-03-22 11:52         ` Ulf Hansson
2017-03-22 11:52         ` Ulf Hansson
2017-03-27 15:24       ` Sylwester Nawrocki
2017-03-27 15:24         ` Sylwester Nawrocki
     [not found]   ` <CGME20170322113556eucas1p1b3434935701aba1db7868415a9c3b4f9@eucas1p1.samsung.com>
2017-03-22 11:35     ` [PATCH v6 4/4] clk: samsung: exynos-audss: Use runtime PM Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-22 11:35       ` Marek Szyprowski
2017-03-22 12:00       ` Ulf Hansson
2017-03-22 12:00         ` Ulf Hansson
2017-03-22 13:32         ` Marek Szyprowski
2017-03-22 13:32           ` Marek Szyprowski
2017-03-22 13:32           ` Marek Szyprowski
2017-03-27 15:24       ` Sylwester Nawrocki
2017-03-27 15:24         ` Sylwester Nawrocki

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.