linux-samsung-soc.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 0/4] Add runtime PM support for clocks (on Exynos SoC example)
       [not found] <CGME20161230133344eucas1p21d107e1cf3096916aad1944f7e002bf9@eucas1p2.samsung.com>
@ 2016-12-30 13:33 ` Marek Szyprowski
       [not found]   ` <CGME20161230133345eucas1p2b88ad403789265915db45ce48ab40453@eucas1p2.samsung.com>
                     ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Marek Szyprowski @ 2016-12-30 13:33 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.

Patches are based on vanilla v4.10-rc1 kernel.

Best regards
Marek Szyprowski
Samsung R&D Institute Poland

Changelog:

v4:
- 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


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                                  | 111 +++++-
 drivers/clk/samsung/clk-exynos-audss.c             |  62 +--
 drivers/clk/samsung/clk-exynos5433.c               | 415 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   2 +-
 drivers/clk/samsung/clk.c                          |  12 +-
 drivers/clk/samsung/clk.h                          |   7 +
 8 files changed, 504 insertions(+), 127 deletions(-)

-- 
1.9.1


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

* [PATCH v4 1/4] clk: Add support for runtime PM
       [not found]   ` <CGME20161230133345eucas1p2b88ad403789265915db45ce48ab40453@eucas1p2.samsung.com>
@ 2016-12-30 13:33     ` Marek Szyprowski
  0 siblings, 0 replies; 6+ messages in thread
From: Marek Szyprowski @ 2016-12-30 13:33 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() 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>
---
 drivers/clk/clk.c | 111 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 101 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0fb39fe217d1..3cf202ccf5a9 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(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 status;
+
 	/*
 	 * .is_prepared is optional for clocks that can prepare
 	 * fall back to software usage counter if it is missing
@@ -157,11 +181,20 @@ 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) == 0) {
+		status = core->ops->is_prepared(core->hw);
+		clk_pm_runtime_put(core);
+	} else {
+		status = false;
+	}
+
+	return status;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+	bool status;
+
 	/*
 	 * .is_enabled is only mandatory for clocks that gate
 	 * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +202,30 @@ 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)) {
+			status = false;
+			goto done;
+		}
+	}
+
+	status = core->ops->is_enabled(core->hw);
+done:
+	if (core->dev)
+		pm_runtime_put(core->dev);
+
+	return status;
 }
 
 /***    helper functions   ***/
@@ -489,6 +545,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 +588,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 +603,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 +810,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 +821,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 +838,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 +865,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);
 }
@@ -1563,6 +1638,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 +1655,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 +1907,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 +1929,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 +2638,8 @@ 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;
 	if (dev && dev->driver)
 		core->owner = dev->driver->owner;
 	core->hw = hw;
-- 
1.9.1


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

* [PATCH v4 2/4] clk: samsung: Add support for runtime PM
       [not found]   ` <CGME20161230133345eucas1p22d8435055ed43b8c91cc3264d22467dd@eucas1p2.samsung.com>
@ 2016-12-30 13:33     ` Marek Szyprowski
  0 siblings, 0 replies; 6+ messages in thread
From: Marek Szyprowski @ 2016-12-30 13:33 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>
---
 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 9617825daabb..f5249a7e545d 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1345,7 +1345,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] 6+ messages in thread

* [PATCH v4 3/4] clk: samsung: exynos5433: Add runtime PM support
       [not found]   ` <CGME20161230133346eucas1p2995539e9fee2867739c5d5608c72802e@eucas1p2.samsung.com>
@ 2016-12-30 13:33     ` Marek Szyprowski
  0 siblings, 0 replies; 6+ messages in thread
From: Marek Szyprowski @ 2016-12-30 13:33 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               | 415 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 352 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 f096bd7df40c..9b12bc20c455 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,14 @@
  * Common Clock Framework support for Exynos5443 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>
 
@@ -1989,6 +1994,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,
@@ -2294,16 +2307,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
  */
@@ -2333,6 +2341,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", };
@@ -2418,16 +2430,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
  */
@@ -2492,6 +2499,16 @@ 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[] = {
+	/* 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", };
@@ -2837,16 +2854,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
  */
@@ -2881,6 +2893,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",};
@@ -3007,16 +3024,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}
  */
@@ -3218,6 +3230,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", };
@@ -3291,15 +3307,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
  */
@@ -3338,6 +3350,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", };
@@ -3432,15 +3450,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
  */
@@ -3966,6 +3980,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", };
@@ -4078,15 +4097,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
  */
@@ -4116,6 +4131,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 = {
@@ -4186,15 +4205,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
  */
@@ -4224,6 +4239,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 = {
@@ -4296,15 +4315,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
  */
@@ -4338,6 +4353,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", };
 
@@ -4549,15 +4568,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
  */
@@ -4621,6 +4636,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", };
@@ -5026,15 +5050,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
  */
@@ -5081,6 +5101,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", };
@@ -5399,11 +5425,230 @@ 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_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(data->pclks[i]);
+
+	clk_disable(data->clk);
+
+	return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	clk_enable(data->clk);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_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(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 (!reg_base) {
+		dev_err(dev, "failed to map registers\n");
+		return -ENOMEM;
+	}
+
+	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;
+		}
+	}
+
+	/*
+	 * Prepare all parent clocks here to avoid potential deadlock caused
+	 * by global clock "prepare lock" grabbed by runtime pm callbacks
+	 * from pm workers.
+	 */
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare(data->pclks[i]);
+
+	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.
+	 */
+	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);
+
+	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] 6+ messages in thread

* [PATCH v4 4/4] clk: samsung: exynos-audss: Use runtime PM
       [not found]   ` <CGME20161230133346eucas1p1d5ce770573dcd5dcf7ad5ed0bf338f71@eucas1p1.samsung.com>
@ 2016-12-30 13:33     ` Marek Szyprowski
  0 siblings, 0 replies; 6+ messages in thread
From: Marek Szyprowski @ 2016-12-30 13:33 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>
---
This patch depends on "clk/samsung: exynos-audss: Replace syscore PM with
platform device PM" patch, which has been already queued for merging:
https://patchwork.kernel.org/patch/9447115/
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
 drivers/clk/samsung/clk-exynos-audss.c             | 62 +++++++++++++---------
 2 files changed, 43 insertions(+), 25 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..25bb569bdd30 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>
 
@@ -134,6 +135,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 +143,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 +160,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 +172,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 +282,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] 6+ messages in thread

* Re: [PATCH v4 0/4] Add runtime PM support for clocks (on Exynos SoC example)
  2016-12-30 13:33 ` [PATCH v4 0/4] Add runtime PM support for clocks (on Exynos SoC example) Marek Szyprowski
                     ` (3 preceding siblings ...)
       [not found]   ` <CGME20161230133346eucas1p1d5ce770573dcd5dcf7ad5ed0bf338f71@eucas1p1.samsung.com>
@ 2017-01-04  5:25   ` Chanwoo Choi
  4 siblings, 0 replies; 6+ messages in thread
From: Chanwoo Choi @ 2017-01-04  5:25 UTC (permalink / raw)
  To: Marek Szyprowski, linux-clk, linux-pm, linux-samsung-soc,
	linux-arm-kernel
  Cc: Stephen Boyd, Michael Turquette, Ulf Hansson, Sylwester Nawrocki,
	Inki Dae, Krzysztof Kozlowski, Bartlomiej Zolnierkiewicz

Dear Marek,

I like these patches because as you already said, the some clocks are included in the specific power domain such as exynos5433. I want to test these patches on Exynos5433-TM2 board. But, this series don't include the Device-Tree binding patches. Could you give the additional patches for test?

Best Regards,
Chanwoo Choi

On 2016년 12월 30일 22:33, Marek Szyprowski wrote:
> 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.
> 
> Patches are based on vanilla v4.10-rc1 kernel.
> 
> Best regards
> Marek Szyprowski
> Samsung R&D Institute Poland
> 
> Changelog:
> 
> v4:
> - 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
> 
> 
> 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                                  | 111 +++++-
>  drivers/clk/samsung/clk-exynos-audss.c             |  62 +--
>  drivers/clk/samsung/clk-exynos5433.c               | 415 ++++++++++++++++-----
>  drivers/clk/samsung/clk-pll.c                      |   2 +-
>  drivers/clk/samsung/clk.c                          |  12 +-
>  drivers/clk/samsung/clk.h                          |   7 +
>  8 files changed, 504 insertions(+), 127 deletions(-)
> 

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

end of thread, other threads:[~2017-01-04  5:26 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CGME20161230133344eucas1p21d107e1cf3096916aad1944f7e002bf9@eucas1p2.samsung.com>
2016-12-30 13:33 ` [PATCH v4 0/4] Add runtime PM support for clocks (on Exynos SoC example) Marek Szyprowski
     [not found]   ` <CGME20161230133345eucas1p2b88ad403789265915db45ce48ab40453@eucas1p2.samsung.com>
2016-12-30 13:33     ` [PATCH v4 1/4] clk: Add support for runtime PM Marek Szyprowski
     [not found]   ` <CGME20161230133345eucas1p22d8435055ed43b8c91cc3264d22467dd@eucas1p2.samsung.com>
2016-12-30 13:33     ` [PATCH v4 2/4] clk: samsung: " Marek Szyprowski
     [not found]   ` <CGME20161230133346eucas1p2995539e9fee2867739c5d5608c72802e@eucas1p2.samsung.com>
2016-12-30 13:33     ` [PATCH v4 3/4] clk: samsung: exynos5433: Add runtime PM support Marek Szyprowski
     [not found]   ` <CGME20161230133346eucas1p1d5ce770573dcd5dcf7ad5ed0bf338f71@eucas1p1.samsung.com>
2016-12-30 13:33     ` [PATCH v4 4/4] clk: samsung: exynos-audss: Use runtime PM Marek Szyprowski
2017-01-04  5:25   ` [PATCH v4 0/4] Add runtime PM support for clocks (on Exynos SoC example) Chanwoo Choi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).