All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 0/5] Add runtime PM support for clocks (on Exynos SoC example)
       [not found] <CGME20170809103512eucas1p2e12493a126ee10419f84ae9890e3450b@eucas1p2.samsung.com>
@ 2017-08-09 10:35   ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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 was an issue for IOMMU drivers, but IOMMU deferred probe support has been
finally merged to v4.13-rc1.

Patches are based on v4.13-rc4.

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:

v8:
- fixed minor issues pointed by Krzysztof Kozlowski and added his 'Reviewed-by'
  tags
- added 'Reviewed-by' and 'Tested-by' tags from Chanwoo Choi
- moved trivial changes in Exynos AudioSS driver to separate patch
- added pm_runtime_get_noresume/pm_runtime_put_sync calls also to exynos-audss
  driver (like it is already done for exynos5433 clocks driver)
- still waiting for any comments from clk maintainers

v7: https://www.spinics.net/lists/arm-kernel/msg598408.html
- rebased onto v4.13-rc3
- still waiting for any comments from clk maintainers

v6: https://patches.linaro.org/cover/95713/
- 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: https://www.spinics.net/lists/linux-samsung-soc/msg57718.html
- 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 (5):
  clk: Add support for runtime PM
  clk: samsung: Add support for runtime PM
  clk: samsung: exynos5433: Add support for runtime PM
  clk: samsung: exynos-audss: Use local variable for controller's device
  clk: samsung: exynos-audss: Add support for 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             |  76 ++--
 drivers/clk/samsung/clk-exynos5433.c               | 409 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   2 +-
 drivers/clk/samsung/clk.c                          |  12 +-
 drivers/clk/samsung/clk.h                          |   7 +
 8 files changed, 521 insertions(+), 136 deletions(-)

-- 
1.9.1

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

* [PATCH v8 0/5] Add runtime PM support for clocks (on Exynos SoC example)
@ 2017-08-09 10:35   ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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 was an issue for IOMMU drivers, but IOMMU deferred probe support has been
finally merged to v4.13-rc1.

Patches are based on v4.13-rc4.

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:

v8:
- fixed minor issues pointed by Krzysztof Kozlowski and added his 'Reviewed-by'
  tags
- added 'Reviewed-by' and 'Tested-by' tags from Chanwoo Choi
- moved trivial changes in Exynos AudioSS driver to separate patch
- added pm_runtime_get_noresume/pm_runtime_put_sync calls also to exynos-audss
  driver (like it is already done for exynos5433 clocks driver)
- still waiting for any comments from clk maintainers

v7: https://www.spinics.net/lists/arm-kernel/msg598408.html
- rebased onto v4.13-rc3
- still waiting for any comments from clk maintainers

v6: https://patches.linaro.org/cover/95713/
- 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: https://www.spinics.net/lists/linux-samsung-soc/msg57718.html
- 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 (5):
  clk: Add support for runtime PM
  clk: samsung: Add support for runtime PM
  clk: samsung: exynos5433: Add support for runtime PM
  clk: samsung: exynos-audss: Use local variable for controller's device
  clk: samsung: exynos-audss: Add support for 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             |  76 ++--
 drivers/clk/samsung/clk-exynos5433.c               | 409 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   2 +-
 drivers/clk/samsung/clk.c                          |  12 +-
 drivers/clk/samsung/clk.h                          |   7 +
 8 files changed, 521 insertions(+), 136 deletions(-)

-- 
1.9.1

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

* [PATCH v8 1/5] clk: Add support for runtime PM
       [not found]   ` <CGME20170809103512eucas1p2eb66401b357d82bed91182a5a1b1fea9@eucas1p2.samsung.com>
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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>
Acked-by: Krzysztof Kozlowski <krzk@kernel.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 fc58c52a26b4..eb11a6a0e1d0 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);
 }
@@ -1038,9 +1110,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;
 }
 
 /**
@@ -1565,6 +1641,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;
@@ -1581,21 +1658,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;
 }
 
 /**
@@ -1826,12 +1910,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);
@@ -1844,6 +1932,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();
 
@@ -2583,6 +2673,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;
@@ -2629,12 +2725,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:
@@ -2642,6 +2739,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] 33+ messages in thread

* [PATCH v8 1/5] clk: Add support for runtime PM
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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>
Acked-by: Krzysztof Kozlowski <krzk@kernel.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 fc58c52a26b4..eb11a6a0e1d0 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);
 }
@@ -1038,9 +1110,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;
 }
 
 /**
@@ -1565,6 +1641,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;
@@ -1581,21 +1658,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;
 }
 
 /**
@@ -1826,12 +1910,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);
@@ -1844,6 +1932,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();
 
@@ -2583,6 +2673,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;
@@ -2629,12 +2725,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:
@@ -2642,6 +2739,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] 33+ messages in thread

* [PATCH v8 2/5] clk: samsung: Add support for runtime PM
       [not found]   ` <CGME20170809103513eucas1p25e142aac911838847e70a036c5d93131@eucas1p2.samsung.com>
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.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 037c61484098..41ebb94d2855 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1388,7 +1388,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;
 
-	ret = clk_hw_register(NULL, &pll->hw);
+	ret = clk_hw_register(ctx->dev, &pll->hw);
 	if (ret) {
 		pr_err("%s: failed to register pll clock %s : %d\n",
 			__func__, pll_clk->name, ret);
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index 7ce0fa86c5ff..aef97b091b50 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -134,7 +134,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_hw = clk_hw_register_fixed_rate(NULL, list->name,
+		clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name,
 			list->parent_name, list->flags, list->fixed_rate);
 		if (IS_ERR(clk_hw)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -163,7 +163,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_hw = clk_hw_register_fixed_factor(NULL, list->name,
+		clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name,
 			list->parent_name, list->flags, list->mult, list->div);
 		if (IS_ERR(clk_hw)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -184,7 +184,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_hw = clk_hw_register_mux(NULL, list->name,
+		clk_hw = clk_hw_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);
@@ -217,13 +217,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
 		if (list->table)
-			clk_hw = clk_hw_register_divider_table(NULL,
+			clk_hw = clk_hw_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_hw = clk_hw_register_divider(NULL, list->name,
+			clk_hw = clk_hw_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);
@@ -255,7 +255,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_hw = clk_hw_register_gate(NULL, list->name, list->parent_name,
+		clk_hw = clk_hw_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_hw)) {
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index b8ca0dd3a38b..f0acae4f5d1b 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -24,6 +24,7 @@
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	spinlock_t lock;
 	/* clk_data must be the last entry due to variable lenght 'hws' array */
 	struct clk_hw_onecell_data clk_data;
-- 
1.9.1

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

* [PATCH v8 2/5] clk: samsung: Add support for runtime PM
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.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 037c61484098..41ebb94d2855 100644
--- a/drivers/clk/samsung/clk-pll.c
+++ b/drivers/clk/samsung/clk-pll.c
@@ -1388,7 +1388,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;
 
-	ret = clk_hw_register(NULL, &pll->hw);
+	ret = clk_hw_register(ctx->dev, &pll->hw);
 	if (ret) {
 		pr_err("%s: failed to register pll clock %s : %d\n",
 			__func__, pll_clk->name, ret);
diff --git a/drivers/clk/samsung/clk.c b/drivers/clk/samsung/clk.c
index 7ce0fa86c5ff..aef97b091b50 100644
--- a/drivers/clk/samsung/clk.c
+++ b/drivers/clk/samsung/clk.c
@@ -134,7 +134,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_hw = clk_hw_register_fixed_rate(NULL, list->name,
+		clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name,
 			list->parent_name, list->flags, list->fixed_rate);
 		if (IS_ERR(clk_hw)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -163,7 +163,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_hw = clk_hw_register_fixed_factor(NULL, list->name,
+		clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name,
 			list->parent_name, list->flags, list->mult, list->div);
 		if (IS_ERR(clk_hw)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
@@ -184,7 +184,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_hw = clk_hw_register_mux(NULL, list->name,
+		clk_hw = clk_hw_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);
@@ -217,13 +217,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
 		if (list->table)
-			clk_hw = clk_hw_register_divider_table(NULL,
+			clk_hw = clk_hw_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_hw = clk_hw_register_divider(NULL, list->name,
+			clk_hw = clk_hw_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);
@@ -255,7 +255,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_hw = clk_hw_register_gate(NULL, list->name, list->parent_name,
+		clk_hw = clk_hw_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_hw)) {
diff --git a/drivers/clk/samsung/clk.h b/drivers/clk/samsung/clk.h
index b8ca0dd3a38b..f0acae4f5d1b 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -24,6 +24,7 @@
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	spinlock_t lock;
 	/* clk_data must be the last entry due to variable lenght 'hws' array */
 	struct clk_hw_onecell_data clk_data;
-- 
1.9.1

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

* [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
       [not found]   ` <CGME20170809103514eucas1p1d09fbd4d6b9954b24fb6280c7d9eaa02@eucas1p1.samsung.com>
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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 belong
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 enter 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 being 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>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
---
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/samsung/clk-exynos5433.c               | 409 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 346 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..cd9337613dd8 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,13 @@
  * 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 <dt-bindings/clock/exynos5433.h>
 
@@ -1991,6 +1995,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 +2308,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 +2342,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 +2431,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 +2500,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 +2859,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 +2898,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 +3029,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 +3235,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 +3312,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 +3355,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 +3455,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 +3985,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 +4102,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 +4136,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 +4210,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 +4244,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 +4320,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 +4358,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 +4573,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 +4641,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 +5055,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 +5106,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 +5430,223 @@ 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_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;
+
+	/* must be the last entry */
+	struct samsung_clk_provider ctx;
+};
+
+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]);
+
+	/* for suspend some registers have to be set to certain values */
+	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;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data) +
+			    sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
+			    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);
+	}
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.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 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);
+
+	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 f0acae4f5d1b..d93031e94387 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -353,6 +353,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] 33+ messages in thread

* [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10:35 UTC (permalink / raw)
  To: linux-arm-kernel

Add runtime pm support for all clock controller units (CMU), which belong
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 enter 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 being 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>
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>
Tested-by: Chanwoo Choi <cw00.choi@samsung.com>
Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
---
 .../devicetree/bindings/clock/exynos5433-clock.txt |  16 +
 drivers/clk/samsung/clk-exynos5433.c               | 409 ++++++++++++++++-----
 drivers/clk/samsung/clk.h                          |   6 +
 3 files changed, 346 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..cd9337613dd8 100644
--- a/drivers/clk/samsung/clk-exynos5433.c
+++ b/drivers/clk/samsung/clk-exynos5433.c
@@ -9,9 +9,13 @@
  * 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 <dt-bindings/clock/exynos5433.h>
 
@@ -1991,6 +1995,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 +2308,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 +2342,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 +2431,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 +2500,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 +2859,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 +2898,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 +3029,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 +3235,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 +3312,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 +3355,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 +3455,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 +3985,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 +4102,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 +4136,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 +4210,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 +4244,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 +4320,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 +4358,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 +4573,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 +4641,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 +5055,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 +5106,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 +5430,223 @@ 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_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;
+
+	/* must be the last entry */
+	struct samsung_clk_provider ctx;
+};
+
+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]);
+
+	/* for suspend some registers have to be set to certain values */
+	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;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data) +
+			    sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
+			    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);
+	}
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.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 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);
+
+	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 f0acae4f5d1b..d93031e94387 100644
--- a/drivers/clk/samsung/clk.h
+++ b/drivers/clk/samsung/clk.h
@@ -353,6 +353,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] 33+ messages in thread

* [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device
       [not found]   ` <CGME20170809103514eucas1p11fb5d1af243e882a5d51a25956d24a19@eucas1p1.samsung.com>
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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

Store pointer to the controller's device in local variable to avoid
extracting it from platform device in each call. This will also simplify
code in the future, when runtime PM support is added.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/clk/samsung/clk-exynos-audss.c | 25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index 1fab56f396d4..6be52fb46ff3 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -135,6 +135,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	const struct exynos_audss_clk_drvdata *variant;
 	struct clk_hw **clk_table;
 	struct resource *res;
+	struct device *dev = &pdev->dev;
 	int i, ret = 0;
 
 	variant = of_device_get_match_data(&pdev->dev);
@@ -142,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_data = devm_kzalloc(&pdev->dev,
+	clk_data = devm_kzalloc(dev,
 				sizeof(*clk_data) +
 				sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
 				GFP_KERNEL);
@@ -160,8 +161,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	clk_data->num = variant->num_clks;
 	clk_table = clk_data->hws;
 
-	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)) {
@@ -172,7 +173,7 @@ 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;
 			}
@@ -183,8 +184,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 				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))
@@ -222,7 +223,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 				 "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_hw_register_gate(NULL, "sclk_pcm",
@@ -237,16 +238,16 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 
 	for (i = 0; i < clk_data->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_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_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;
 	}
 
-- 
1.9.1

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

* [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10:35 UTC (permalink / raw)
  To: linux-arm-kernel

Store pointer to the controller's device in local variable to avoid
extracting it from platform device in each call. This will also simplify
code in the future, when runtime PM support is added.

Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
---
 drivers/clk/samsung/clk-exynos-audss.c | 25 +++++++++++++------------
 1 file changed, 13 insertions(+), 12 deletions(-)

diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
index 1fab56f396d4..6be52fb46ff3 100644
--- a/drivers/clk/samsung/clk-exynos-audss.c
+++ b/drivers/clk/samsung/clk-exynos-audss.c
@@ -135,6 +135,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	const struct exynos_audss_clk_drvdata *variant;
 	struct clk_hw **clk_table;
 	struct resource *res;
+	struct device *dev = &pdev->dev;
 	int i, ret = 0;
 
 	variant = of_device_get_match_data(&pdev->dev);
@@ -142,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_data = devm_kzalloc(&pdev->dev,
+	clk_data = devm_kzalloc(dev,
 				sizeof(*clk_data) +
 				sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
 				GFP_KERNEL);
@@ -160,8 +161,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 	clk_data->num = variant->num_clks;
 	clk_table = clk_data->hws;
 
-	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)) {
@@ -172,7 +173,7 @@ 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;
 			}
@@ -183,8 +184,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 				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))
@@ -222,7 +223,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 				 "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_hw_register_gate(NULL, "sclk_pcm",
@@ -237,16 +238,16 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 
 	for (i = 0; i < clk_data->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_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
+	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_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;
 	}
 
-- 
1.9.1

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

* [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM
       [not found]   ` <CGME20170809103515eucas1p19fcbdd113e191cc02f9e5aaa469376d3@eucas1p1.samsung.com>
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
 drivers/clk/samsung/clk-exynos-audss.c             | 51 ++++++++++++++--------
 2 files changed, 40 insertions(+), 17 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 6be52fb46ff3..ab494c104ce6 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>
 
@@ -36,14 +37,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;
 
@@ -53,7 +53,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;
 
@@ -62,7 +62,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;
@@ -179,7 +178,18 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 			}
 		}
 	}
-	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
+
+	/*
+	 * 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);
+
+	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_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);
@@ -190,48 +200,48 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		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_hw_register_mux(NULL, "mout_i2s",
+	clk_table[EXYNOS_MOUT_I2S] = clk_hw_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_hw_register_divider(NULL, "dout_srp",
+	clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 				0, &lock);
 
-	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL,
+	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
 				"dout_aud_bus", "dout_srp", 0,
 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s",
+	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
 				&lock);
 
-	clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(NULL, "srp_clk",
+	clk_table[EXYNOS_SRP_CLK] = clk_hw_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_hw_register_gate(NULL, "i2s_bus",
+	clk_table[EXYNOS_I2S_BUS] = clk_hw_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_hw_register_gate(NULL, "sclk_i2s",
+	clk_table[EXYNOS_SCLK_I2S] = clk_hw_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_hw_register_gate(NULL, "pcm_bus",
+	clk_table[EXYNOS_PCM_BUS] = clk_hw_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(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_hw_register_gate(NULL, "sclk_pcm",
+	clk_table[EXYNOS_SCLK_PCM] = clk_hw_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_hw_register_gate(NULL, "adma",
+		clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 9, 0, &lock);
 	}
@@ -251,10 +261,14 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		goto unregister;
 	}
 
+	pm_runtime_put_sync(dev);
+
 	return 0;
 
 unregister:
 	exynos_audss_clk_teardown();
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
 
 	if (!IS_ERR(epll))
 		clk_disable_unprepare(epll);
@@ -267,6 +281,7 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
 	of_clk_del_provider(pdev->dev.of_node);
 
 	exynos_audss_clk_teardown();
+	pm_runtime_disable(&pdev->dev);
 
 	if (!IS_ERR(epll))
 		clk_disable_unprepare(epll);
@@ -275,8 +290,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] 33+ messages in thread

* [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM
@ 2017-08-09 10:35       ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-09 10: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>
Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
---
 .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
 drivers/clk/samsung/clk-exynos-audss.c             | 51 ++++++++++++++--------
 2 files changed, 40 insertions(+), 17 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 6be52fb46ff3..ab494c104ce6 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>
 
@@ -36,14 +37,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;
 
@@ -53,7 +53,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;
 
@@ -62,7 +62,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;
@@ -179,7 +178,18 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 			}
 		}
 	}
-	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
+
+	/*
+	 * 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);
+
+	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_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);
@@ -190,48 +200,48 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		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_hw_register_mux(NULL, "mout_i2s",
+	clk_table[EXYNOS_MOUT_I2S] = clk_hw_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_hw_register_divider(NULL, "dout_srp",
+	clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
 				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
 				0, &lock);
 
-	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL,
+	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
 				"dout_aud_bus", "dout_srp", 0,
 				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
 
-	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s",
+	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
 				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
 				&lock);
 
-	clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(NULL, "srp_clk",
+	clk_table[EXYNOS_SRP_CLK] = clk_hw_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_hw_register_gate(NULL, "i2s_bus",
+	clk_table[EXYNOS_I2S_BUS] = clk_hw_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_hw_register_gate(NULL, "sclk_i2s",
+	clk_table[EXYNOS_SCLK_I2S] = clk_hw_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_hw_register_gate(NULL, "pcm_bus",
+	clk_table[EXYNOS_PCM_BUS] = clk_hw_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(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_hw_register_gate(NULL, "sclk_pcm",
+	clk_table[EXYNOS_SCLK_PCM] = clk_hw_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_hw_register_gate(NULL, "adma",
+		clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
 				"dout_srp", CLK_SET_RATE_PARENT,
 				reg_base + ASS_CLK_GATE, 9, 0, &lock);
 	}
@@ -251,10 +261,14 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
 		goto unregister;
 	}
 
+	pm_runtime_put_sync(dev);
+
 	return 0;
 
 unregister:
 	exynos_audss_clk_teardown();
+	pm_runtime_put_sync(dev);
+	pm_runtime_disable(dev);
 
 	if (!IS_ERR(epll))
 		clk_disable_unprepare(epll);
@@ -267,6 +281,7 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
 	of_clk_del_provider(pdev->dev.of_node);
 
 	exynos_audss_clk_teardown();
+	pm_runtime_disable(&pdev->dev);
 
 	if (!IS_ERR(epll))
 		clk_disable_unprepare(epll);
@@ -275,8 +290,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] 33+ messages in thread

* Re: [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM
  2017-08-09 10:35       ` Marek Szyprowski
@ 2017-08-10  5:51         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2017-08-10  5:51 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel,
	Stephen Boyd, Michael Turquette, Ulf Hansson, Sylwester Nawrocki,
	Chanwoo Choi, Inki Dae, Bartlomiej Zolnierkiewicz

On Wed, Aug 09, 2017 at 12:35:07PM +0200, Marek Szyprowski 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>
> ---
>  .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
>  drivers/clk/samsung/clk-exynos-audss.c             | 51 ++++++++++++++--------
>  2 files changed, 40 insertions(+), 17 deletions(-)
> 

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

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

* [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM
@ 2017-08-10  5:51         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2017-08-10  5:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 09, 2017 at 12:35:07PM +0200, Marek Szyprowski 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>
> ---
>  .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
>  drivers/clk/samsung/clk-exynos-audss.c             | 51 ++++++++++++++--------
>  2 files changed, 40 insertions(+), 17 deletions(-)
> 

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

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

* Re: [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device
  2017-08-09 10:35       ` Marek Szyprowski
@ 2017-08-10  5:53         ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2017-08-10  5:53 UTC (permalink / raw)
  To: Marek Szyprowski
  Cc: linux-clk, linux-pm, linux-samsung-soc, linux-arm-kernel,
	Stephen Boyd, Michael Turquette, Ulf Hansson, Sylwester Nawrocki,
	Chanwoo Choi, Inki Dae, Bartlomiej Zolnierkiewicz

On Wed, Aug 09, 2017 at 12:35:06PM +0200, Marek Szyprowski wrote:
> Store pointer to the controller's device in local variable to avoid
> extracting it from platform device in each call. This will also simplify
> code in the future, when runtime PM support is added.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/clk/samsung/clk-exynos-audss.c | 25 +++++++++++++------------
>  1 file changed, 13 insertions(+), 12 deletions(-)
> 

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

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

* [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device
@ 2017-08-10  5:53         ` Krzysztof Kozlowski
  0 siblings, 0 replies; 33+ messages in thread
From: Krzysztof Kozlowski @ 2017-08-10  5:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Aug 09, 2017 at 12:35:06PM +0200, Marek Szyprowski wrote:
> Store pointer to the controller's device in local variable to avoid
> extracting it from platform device in each call. This will also simplify
> code in the future, when runtime PM support is added.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/clk/samsung/clk-exynos-audss.c | 25 +++++++++++++------------
>  1 file changed, 13 insertions(+), 12 deletions(-)
> 

Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>

Best regards,
Krzysztof

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

* Re: [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device
  2017-08-09 10:35       ` Marek Szyprowski
@ 2017-08-10  6:27         ` Chanwoo Choi
  -1 siblings, 0 replies; 33+ messages in thread
From: Chanwoo Choi @ 2017-08-10  6:27 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

Hi,

On 2017년 08월 09일 19:35, Marek Szyprowski wrote:
> Store pointer to the controller's device in local variable to avoid
> extracting it from platform device in each call. This will also simplify
> code in the future, when runtime PM support is added.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/clk/samsung/clk-exynos-audss.c | 25 +++++++++++++------------
>  1 file changed, 13 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
> index 1fab56f396d4..6be52fb46ff3 100644
> --- a/drivers/clk/samsung/clk-exynos-audss.c
> +++ b/drivers/clk/samsung/clk-exynos-audss.c
> @@ -135,6 +135,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  	const struct exynos_audss_clk_drvdata *variant;
>  	struct clk_hw **clk_table;
>  	struct resource *res;
> +	struct device *dev = &pdev->dev;
>  	int i, ret = 0;
>  
>  	variant = of_device_get_match_data(&pdev->dev);
> @@ -142,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_data = devm_kzalloc(&pdev->dev,
> +	clk_data = devm_kzalloc(dev,
>  				sizeof(*clk_data) +
>  				sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
>  				GFP_KERNEL);
> @@ -160,8 +161,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  	clk_data->num = variant->num_clks;
>  	clk_table = clk_data->hws;
>  
> -	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)) {
> @@ -172,7 +173,7 @@ 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;
>  			}
> @@ -183,8 +184,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  				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))
> @@ -222,7 +223,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  				 "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_hw_register_gate(NULL, "sclk_pcm",
> @@ -237,16 +238,16 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  
>  	for (i = 0; i < clk_data->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_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
> +	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_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;
>  	}
>  
> 

Looks good to me.
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

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

* [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device
@ 2017-08-10  6:27         ` Chanwoo Choi
  0 siblings, 0 replies; 33+ messages in thread
From: Chanwoo Choi @ 2017-08-10  6:27 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 2017? 08? 09? 19:35, Marek Szyprowski wrote:
> Store pointer to the controller's device in local variable to avoid
> extracting it from platform device in each call. This will also simplify
> code in the future, when runtime PM support is added.
> 
> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> ---
>  drivers/clk/samsung/clk-exynos-audss.c | 25 +++++++++++++------------
>  1 file changed, 13 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/clk/samsung/clk-exynos-audss.c b/drivers/clk/samsung/clk-exynos-audss.c
> index 1fab56f396d4..6be52fb46ff3 100644
> --- a/drivers/clk/samsung/clk-exynos-audss.c
> +++ b/drivers/clk/samsung/clk-exynos-audss.c
> @@ -135,6 +135,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  	const struct exynos_audss_clk_drvdata *variant;
>  	struct clk_hw **clk_table;
>  	struct resource *res;
> +	struct device *dev = &pdev->dev;
>  	int i, ret = 0;
>  
>  	variant = of_device_get_match_data(&pdev->dev);
> @@ -142,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_data = devm_kzalloc(&pdev->dev,
> +	clk_data = devm_kzalloc(dev,
>  				sizeof(*clk_data) +
>  				sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
>  				GFP_KERNEL);
> @@ -160,8 +161,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  	clk_data->num = variant->num_clks;
>  	clk_table = clk_data->hws;
>  
> -	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)) {
> @@ -172,7 +173,7 @@ 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;
>  			}
> @@ -183,8 +184,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  				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))
> @@ -222,7 +223,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  				 "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_hw_register_gate(NULL, "sclk_pcm",
> @@ -237,16 +238,16 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  
>  	for (i = 0; i < clk_data->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_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
> +	ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_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;
>  	}
>  
> 

Looks good to me.
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

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

* Re: [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM
  2017-08-09 10:35       ` Marek Szyprowski
@ 2017-08-10  6:46         ` Chanwoo Choi
  -1 siblings, 0 replies; 33+ messages in thread
From: Chanwoo Choi @ 2017-08-10  6:46 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

Hi,

On 2017년 08월 09일 19:35, Marek Szyprowski 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>
> ---
>  .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
>  drivers/clk/samsung/clk-exynos-audss.c             | 51 ++++++++++++++--------
>  2 files changed, 40 insertions(+), 17 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 6be52fb46ff3..ab494c104ce6 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>
>  
> @@ -36,14 +37,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;
>  
> @@ -53,7 +53,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;
>  
> @@ -62,7 +62,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;
> @@ -179,7 +178,18 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  			}
>  		}
>  	}
> -	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
> +
> +	/*
> +	 * 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);
> +
> +	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_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);
> @@ -190,48 +200,48 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  		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_hw_register_mux(NULL, "mout_i2s",
> +	clk_table[EXYNOS_MOUT_I2S] = clk_hw_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_hw_register_divider(NULL, "dout_srp",
> +	clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
>  				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
>  				0, &lock);
>  
> -	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL,
> +	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
>  				"dout_aud_bus", "dout_srp", 0,
>  				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
>  
> -	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s",
> +	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
>  				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
>  				&lock);
>  
> -	clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(NULL, "srp_clk",
> +	clk_table[EXYNOS_SRP_CLK] = clk_hw_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_hw_register_gate(NULL, "i2s_bus",
> +	clk_table[EXYNOS_I2S_BUS] = clk_hw_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_hw_register_gate(NULL, "sclk_i2s",
> +	clk_table[EXYNOS_SCLK_I2S] = clk_hw_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_hw_register_gate(NULL, "pcm_bus",
> +	clk_table[EXYNOS_PCM_BUS] = clk_hw_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(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_hw_register_gate(NULL, "sclk_pcm",
> +	clk_table[EXYNOS_SCLK_PCM] = clk_hw_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_hw_register_gate(NULL, "adma",
> +		clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
>  				"dout_srp", CLK_SET_RATE_PARENT,
>  				reg_base + ASS_CLK_GATE, 9, 0, &lock);
>  	}
> @@ -251,10 +261,14 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  		goto unregister;
>  	}
>  
> +	pm_runtime_put_sync(dev);
> +
>  	return 0;
>  
>  unregister:
>  	exynos_audss_clk_teardown();
> +	pm_runtime_put_sync(dev);
> +	pm_runtime_disable(dev);
>  
>  	if (!IS_ERR(epll))
>  		clk_disable_unprepare(epll);
> @@ -267,6 +281,7 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
>  	of_clk_del_provider(pdev->dev.of_node);
>  
>  	exynos_audss_clk_teardown();
> +	pm_runtime_disable(&pdev->dev);
>  
>  	if (!IS_ERR(epll))
>  		clk_disable_unprepare(epll);
> @@ -275,8 +290,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 = {
> 

Looks good to me.
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

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

* [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM
@ 2017-08-10  6:46         ` Chanwoo Choi
  0 siblings, 0 replies; 33+ messages in thread
From: Chanwoo Choi @ 2017-08-10  6:46 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On 2017? 08? 09? 19:35, Marek Szyprowski 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>
> ---
>  .../devicetree/bindings/clock/clk-exynos-audss.txt |  6 +++
>  drivers/clk/samsung/clk-exynos-audss.c             | 51 ++++++++++++++--------
>  2 files changed, 40 insertions(+), 17 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 6be52fb46ff3..ab494c104ce6 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>
>  
> @@ -36,14 +37,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;
>  
> @@ -53,7 +53,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;
>  
> @@ -62,7 +62,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;
> @@ -179,7 +178,18 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  			}
>  		}
>  	}
> -	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
> +
> +	/*
> +	 * 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);
> +
> +	clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_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);
> @@ -190,48 +200,48 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  		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_hw_register_mux(NULL, "mout_i2s",
> +	clk_table[EXYNOS_MOUT_I2S] = clk_hw_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_hw_register_divider(NULL, "dout_srp",
> +	clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
>  				"mout_audss", 0, reg_base + ASS_CLK_DIV, 0, 4,
>  				0, &lock);
>  
> -	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(NULL,
> +	clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
>  				"dout_aud_bus", "dout_srp", 0,
>  				reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
>  
> -	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(NULL, "dout_i2s",
> +	clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
>  				"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
>  				&lock);
>  
> -	clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(NULL, "srp_clk",
> +	clk_table[EXYNOS_SRP_CLK] = clk_hw_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_hw_register_gate(NULL, "i2s_bus",
> +	clk_table[EXYNOS_I2S_BUS] = clk_hw_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_hw_register_gate(NULL, "sclk_i2s",
> +	clk_table[EXYNOS_SCLK_I2S] = clk_hw_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_hw_register_gate(NULL, "pcm_bus",
> +	clk_table[EXYNOS_PCM_BUS] = clk_hw_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(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_hw_register_gate(NULL, "sclk_pcm",
> +	clk_table[EXYNOS_SCLK_PCM] = clk_hw_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_hw_register_gate(NULL, "adma",
> +		clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
>  				"dout_srp", CLK_SET_RATE_PARENT,
>  				reg_base + ASS_CLK_GATE, 9, 0, &lock);
>  	}
> @@ -251,10 +261,14 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
>  		goto unregister;
>  	}
>  
> +	pm_runtime_put_sync(dev);
> +
>  	return 0;
>  
>  unregister:
>  	exynos_audss_clk_teardown();
> +	pm_runtime_put_sync(dev);
> +	pm_runtime_disable(dev);
>  
>  	if (!IS_ERR(epll))
>  		clk_disable_unprepare(epll);
> @@ -267,6 +281,7 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
>  	of_clk_del_provider(pdev->dev.of_node);
>  
>  	exynos_audss_clk_teardown();
> +	pm_runtime_disable(&pdev->dev);
>  
>  	if (!IS_ERR(epll))
>  		clk_disable_unprepare(epll);
> @@ -275,8 +290,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 = {
> 

Looks good to me.
Reviewed-by: Chanwoo Choi <cw00.choi@samsung.com>

-- 
Best Regards,
Chanwoo Choi
Samsung Electronics

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

* Re: [PATCH v8 1/5] clk: Add support for runtime PM
  2017-08-09 10:35       ` Marek Szyprowski
  (?)
@ 2017-08-10 18:28         ` Michael Turquette
  -1 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-10 18:28 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-08-09 03:35:03)
> 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.
> =

> 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.
> =

> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>

Overall the idea and the patch look good to me.

Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
makes perfect sense to me. It means that the power domain that powers
that clock hardware must be on for that clock to function properly.

It=E2=80=99s worth pointing out that this patch leaves that power domain en=
abled
for for as long as prepare_count is greater than zero.

However the rate-change operations only use the pm_runtime functions to
program the hardware and modify the registers. At the end of each
rate-change call there is a pm_runtime_put. This makes sense to me, but
I=E2=80=99m trying to wrap my head around the fact that this patch introduc=
es
two behaviors:

1) protect access to the clk hardware by wrapping writes across the bus
with pm_runtime calls
2) power the clock logic when the clock is in use (e.g. clk_prepare())

Everything about this patch *seems* correct to me, but I=E2=80=99m trying to
figure out what it means to for a clock consumer driver to do something
like this:

clk_prepare_enable()
clk_disable_unprepare()
clk_set_rate()

Maybe patches #2-5 will help me understand, but I wanted to jot this
down in my review before I forget.

> @@ -2583,6 +2673,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;
> @@ -2629,12 +2725,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;
> +       }

I wonder if the above can be moved inside of __clk_core_init?
clk_register shouldn't manage any clk_ops directly, only __clk_core_init
does this.

Regards,
Mike

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

* Re: [PATCH v8 1/5] clk: Add support for runtime PM
@ 2017-08-10 18:28         ` Michael Turquette
  0 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-10 18:28 UTC (permalink / raw)
  To: 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-08-09 03:35:03)
> 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>
> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>

Overall the idea and the patch look good to me.

Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
makes perfect sense to me. It means that the power domain that powers
that clock hardware must be on for that clock to function properly.

It’s worth pointing out that this patch leaves that power domain enabled
for for as long as prepare_count is greater than zero.

However the rate-change operations only use the pm_runtime functions to
program the hardware and modify the registers. At the end of each
rate-change call there is a pm_runtime_put. This makes sense to me, but
I’m trying to wrap my head around the fact that this patch introduces
two behaviors:

1) protect access to the clk hardware by wrapping writes across the bus
with pm_runtime calls
2) power the clock logic when the clock is in use (e.g. clk_prepare())

Everything about this patch *seems* correct to me, but I’m trying to
figure out what it means to for a clock consumer driver to do something
like this:

clk_prepare_enable()
clk_disable_unprepare()
clk_set_rate()

Maybe patches #2-5 will help me understand, but I wanted to jot this
down in my review before I forget.

> @@ -2583,6 +2673,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;
> @@ -2629,12 +2725,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;
> +       }

I wonder if the above can be moved inside of __clk_core_init?
clk_register shouldn't manage any clk_ops directly, only __clk_core_init
does this.

Regards,
Mike

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

* [PATCH v8 1/5] clk: Add support for runtime PM
@ 2017-08-10 18:28         ` Michael Turquette
  0 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-10 18:28 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Marek,

Quoting Marek Szyprowski (2017-08-09 03:35:03)
> 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>
> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>

Overall the idea and the patch look good to me.

Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
makes perfect sense to me. It means that the power domain that powers
that clock hardware must be on for that clock to function properly.

It?s worth pointing out that this patch leaves that power domain enabled
for for as long as prepare_count is greater than zero.

However the rate-change operations only use the pm_runtime functions to
program the hardware and modify the registers. At the end of each
rate-change call there is a pm_runtime_put. This makes sense to me, but
I?m trying to wrap my head around the fact that this patch introduces
two behaviors:

1) protect access to the clk hardware by wrapping writes across the bus
with pm_runtime calls
2) power the clock logic when the clock is in use (e.g. clk_prepare())

Everything about this patch *seems* correct to me, but I?m trying to
figure out what it means to for a clock consumer driver to do something
like this:

clk_prepare_enable()
clk_disable_unprepare()
clk_set_rate()

Maybe patches #2-5 will help me understand, but I wanted to jot this
down in my review before I forget.

> @@ -2583,6 +2673,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;
> @@ -2629,12 +2725,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;
> +       }

I wonder if the above can be moved inside of __clk_core_init?
clk_register shouldn't manage any clk_ops directly, only __clk_core_init
does this.

Regards,
Mike

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

* Re: [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
  2017-08-09 10:35       ` Marek Szyprowski
  (?)
@ 2017-08-10 18:46         ` Michael Turquette
  -1 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-10 18:46 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

Quoting Marek Szyprowski (2017-08-09 03:35:05)
> Add runtime pm support for all clock controller units (CMU), which belong
> 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 enter 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 being 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.

I would have preferred two patches here. First to platform_driver-ify
the samsung clk driver and the second to add the pm_runtime bits.

> +static int exynos5433_cmu_suspend(struct device *dev)
> +{
> +       struct exynos5433_cmu_data *data =3D dev_get_drvdata(dev);
> +       int i;
> +
> +       samsung_clk_save(data->ctx.reg_base, data->clk_save,
> +                        data->nr_clk_save);
> +
> +       for (i =3D 0; i < data->nr_pclks; i++)
> +               clk_prepare_enable(data->pclks[i]);
> +
> +       /* for suspend some registers have to be set to certain values */
> +       samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
> +                           data->nr_clk_suspend);
> +
> +       for (i =3D 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 =3D dev_get_drvdata(dev);
> +       int i;
> +
> +       clk_prepare_enable(data->clk);
> +
> +       for (i =3D 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);

Will the restored clk hardware always match the software bookkeeping in
the clk framework?

Is it necessary to do a tree walk and recalc rates after restoring these
registers?

> +
> +       for (i =3D 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 =3D &pdev->dev;
> +       struct resource *res;
> +       void __iomem *reg_base;
> +       int i;
> +
> +       info =3D of_device_get_match_data(dev);
> +
> +       data =3D devm_kzalloc(dev, sizeof(*data) +
> +                           sizeof(*data->ctx.clk_data.hws) * info->nr_cl=
k_ids,
> +                           GFP_KERNEL);
> +       if (!data)
> +               return -ENOMEM;
> +       ctx =3D &data->ctx;
> +
> +       res =3D platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       reg_base =3D devm_ioremap_resource(dev, res);
> +       if (IS_ERR(reg_base)) {
> +               dev_err(dev, "failed to map registers\n");
> +               return PTR_ERR(reg_base);
> +       }
> +
> +       for (i =3D 0; i < info->nr_clk_ids; ++i)
> +               ctx->clk_data.hws[i] =3D ERR_PTR(-ENOENT);
> +
> +       ctx->clk_data.num =3D info->nr_clk_ids;
> +       ctx->reg_base =3D reg_base;
> +       ctx->dev =3D dev;
> +       spin_lock_init(&ctx->lock);
> +
> +       data->clk_save =3D samsung_clk_alloc_reg_dump(info->clk_regs,
> +                                                   info->nr_clk_regs);
> +       data->nr_clk_save =3D info->nr_clk_regs;
> +       data->clk_suspend =3D info->suspend_regs;
> +       data->nr_clk_suspend =3D info->nr_suspend_regs;
> +       data->nr_pclks =3D of_count_phandle_with_args(dev->of_node, "cloc=
ks",
> +                                                   "#clock-cells");
> +       if (data->nr_pclks > 0) {
> +               data->pclks =3D devm_kcalloc(dev, sizeof(struct clk *),
> +                                          data->nr_pclks, GFP_KERNEL);
> +
> +               for (i =3D 0; i < data->nr_pclks; i++) {
> +                       struct clk *clk =3D of_clk_get(dev->of_node, i);
> +
> +                       if (IS_ERR(clk))
> +                               return PTR_ERR(clk);
> +                       data->pclks[i] =3D clk;
> +               }
> +       }
> +
> +       if (info->clk_name)
> +               data->clk =3D clk_get(dev, info->clk_name);
> +       clk_prepare_enable(data->clk);

What's going on here with this weird clk_prepare_enable? It looks like
it is never balanced with a disable_unprepare?

> +
> +       platform_set_drvdata(pdev, data);
> +
> +       /*
> +        * Enable runtime PM here to allow the clock core using runtime PM
> +        * for the registered clocks. Additionally, we increase the runti=
me
> +        * 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);

Hmm, I do wonder if this sort of initialization can be made generic for
all other clk drivers that will use this. Thanksfully it is very few
functions to call.

Regards,
Mike

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

* Re: [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
@ 2017-08-10 18:46         ` Michael Turquette
  0 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-10 18:46 UTC (permalink / raw)
  To: 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

Quoting Marek Szyprowski (2017-08-09 03:35:05)
> Add runtime pm support for all clock controller units (CMU), which belong
> 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 enter 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 being 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.

I would have preferred two patches here. First to platform_driver-ify
the samsung clk driver and the second to add the pm_runtime bits.

> +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]);
> +
> +       /* for suspend some registers have to be set to certain values */
> +       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);

Will the restored clk hardware always match the software bookkeeping in
the clk framework?

Is it necessary to do a tree walk and recalc rates after restoring these
registers?

> +
> +       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;
> +       int i;
> +
> +       info = of_device_get_match_data(dev);
> +
> +       data = devm_kzalloc(dev, sizeof(*data) +
> +                           sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
> +                           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);
> +       }
> +
> +       for (i = 0; i < info->nr_clk_ids; ++i)
> +               ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
> +
> +       ctx->clk_data.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);

What's going on here with this weird clk_prepare_enable? It looks like
it is never balanced with a disable_unprepare?

> +
> +       platform_set_drvdata(pdev, data);
> +
> +       /*
> +        * 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);

Hmm, I do wonder if this sort of initialization can be made generic for
all other clk drivers that will use this. Thanksfully it is very few
functions to call.

Regards,
Mike

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

* [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
@ 2017-08-10 18:46         ` Michael Turquette
  0 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-10 18:46 UTC (permalink / raw)
  To: linux-arm-kernel

Quoting Marek Szyprowski (2017-08-09 03:35:05)
> Add runtime pm support for all clock controller units (CMU), which belong
> 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 enter 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 being 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.

I would have preferred two patches here. First to platform_driver-ify
the samsung clk driver and the second to add the pm_runtime bits.

> +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]);
> +
> +       /* for suspend some registers have to be set to certain values */
> +       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);

Will the restored clk hardware always match the software bookkeeping in
the clk framework?

Is it necessary to do a tree walk and recalc rates after restoring these
registers?

> +
> +       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;
> +       int i;
> +
> +       info = of_device_get_match_data(dev);
> +
> +       data = devm_kzalloc(dev, sizeof(*data) +
> +                           sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
> +                           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);
> +       }
> +
> +       for (i = 0; i < info->nr_clk_ids; ++i)
> +               ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
> +
> +       ctx->clk_data.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);

What's going on here with this weird clk_prepare_enable? It looks like
it is never balanced with a disable_unprepare?

> +
> +       platform_set_drvdata(pdev, data);
> +
> +       /*
> +        * 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);

Hmm, I do wonder if this sort of initialization can be made generic for
all other clk drivers that will use this. Thanksfully it is very few
functions to call.

Regards,
Mike

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

* Re: [PATCH v8 1/5] clk: Add support for runtime PM
  2017-08-10 18:28         ` Michael Turquette
@ 2017-08-11  6:18           ` Marek Szyprowski
  -1 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-11  6:18 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,

Thanks for the comments!

On 2017-08-10 20:28, Michael Turquette wrote:
> Hi Marek,
>
> Quoting Marek Szyprowski (2017-08-09 03:35:03)
>> 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>
>> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> Overall the idea and the patch look good to me.
>
> Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
> makes perfect sense to me. It means that the power domain that powers
> that clock hardware must be on for that clock to function properly.
>
> It’s worth pointing out that this patch leaves that power domain enabled
> for for as long as prepare_count is greater than zero.
>
> However the rate-change operations only use the pm_runtime functions to
> program the hardware and modify the registers. At the end of each
> rate-change call there is a pm_runtime_put. This makes sense to me, but
> I’m trying to wrap my head around the fact that this patch introduces
> two behaviors:
>
> 1) protect access to the clk hardware by wrapping writes across the bus
> with pm_runtime calls
> 2) power the clock logic when the clock is in use (e.g. clk_prepare())
>
> Everything about this patch *seems* correct to me, but I’m trying to
> figure out what it means to for a clock consumer driver to do something
> like this:
>
> clk_prepare_enable()
> clk_disable_unprepare()
> clk_set_rate()
>
> Maybe patches #2-5 will help me understand, but I wanted to jot this
> down in my review before I forget.

Well, the question is what will the above sequence do without my patches?

IMO the only result of above calls is (optionally) changed rate of parent
clocks and appropriate values written to given clock's registers, so next
time one calls clk_(prepare)_enable(), the given clock will be enabled
with the configured rate.

With runtime pm patches the overall result will be the same. However, if
the given clock is the only/last enabled clock from the provider, there
might be a runtime pm resume/suspend transition (2 times: one from
prepare/unprepare calls, the second from set_rate calls). This means that
the rate of parent clocks (especially if they comes from the other
providers) might or might not change between those transitions. This
depends how the runtime pm is implemented and what operations has to be
done for putting provider into suspend mode. The final result will be
however the same as without runtime pm patches.

Clock provider's runtime pm suspend callback should save internal
configuration and resume callback must restore it completely, so the
provider's internal state is exactly the same as before suspend. If we
assume that clock provider might be in a power domain, the internal
clock provider's hardware context might be lost after suspend callback.

>> @@ -2583,6 +2673,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;
>> @@ -2629,12 +2725,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;
>> +       }
> I wonder if the above can be moved inside of __clk_core_init?
> clk_register shouldn't manage any clk_ops directly, only __clk_core_init
> does this.

If you prefer that, I will move runtime pm related bits to 
__clk_core_init then.

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


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

* [PATCH v8 1/5] clk: Add support for runtime PM
@ 2017-08-11  6:18           ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-11  6:18 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Michael,

Thanks for the comments!

On 2017-08-10 20:28, Michael Turquette wrote:
> Hi Marek,
>
> Quoting Marek Szyprowski (2017-08-09 03:35:03)
>> 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>
>> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> Overall the idea and the patch look good to me.
>
> Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
> makes perfect sense to me. It means that the power domain that powers
> that clock hardware must be on for that clock to function properly.
>
> It?s worth pointing out that this patch leaves that power domain enabled
> for for as long as prepare_count is greater than zero.
>
> However the rate-change operations only use the pm_runtime functions to
> program the hardware and modify the registers. At the end of each
> rate-change call there is a pm_runtime_put. This makes sense to me, but
> I?m trying to wrap my head around the fact that this patch introduces
> two behaviors:
>
> 1) protect access to the clk hardware by wrapping writes across the bus
> with pm_runtime calls
> 2) power the clock logic when the clock is in use (e.g. clk_prepare())
>
> Everything about this patch *seems* correct to me, but I?m trying to
> figure out what it means to for a clock consumer driver to do something
> like this:
>
> clk_prepare_enable()
> clk_disable_unprepare()
> clk_set_rate()
>
> Maybe patches #2-5 will help me understand, but I wanted to jot this
> down in my review before I forget.

Well, the question is what will the above sequence do without my patches?

IMO the only result of above calls is (optionally) changed rate of parent
clocks and appropriate values written to given clock's registers, so next
time one calls clk_(prepare)_enable(), the given clock will be enabled
with the configured rate.

With runtime pm patches the overall result will be the same. However, if
the given clock is the only/last enabled clock from the provider, there
might be a runtime pm resume/suspend transition (2 times: one from
prepare/unprepare calls, the second from set_rate calls). This means that
the rate of parent clocks (especially if they comes from the other
providers) might or might not change between those transitions. This
depends how the runtime pm is implemented and what operations has to be
done for putting provider into suspend mode. The final result will be
however the same as without runtime pm patches.

Clock provider's runtime pm suspend callback should save internal
configuration and resume callback must restore it completely, so the
provider's internal state is exactly the same as before suspend. If we
assume that clock provider might be in a power domain, the internal
clock provider's hardware context might be lost after suspend callback.

>> @@ -2583,6 +2673,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;
>> @@ -2629,12 +2725,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;
>> +       }
> I wonder if the above can be moved inside of __clk_core_init?
> clk_register shouldn't manage any clk_ops directly, only __clk_core_init
> does this.

If you prefer that, I will move runtime pm related bits to 
__clk_core_init then.

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

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

* Re: [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
  2017-08-10 18:46         ` Michael Turquette
@ 2017-08-11  6:34           ` Marek Szyprowski
  -1 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-11  6:34 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-08-10 20:46, Michael Turquette wrote:
> Quoting Marek Szyprowski (2017-08-09 03:35:05)
>> Add runtime pm support for all clock controller units (CMU), which belong
>> 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 enter 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 being 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.
> I would have preferred two patches here. First to platform_driver-ify
> the samsung clk driver and the second to add the pm_runtime bits.
>
>> +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]);
>> +
>> +       /* for suspend some registers have to be set to certain values */
>> +       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);
> Will the restored clk hardware always match the software bookkeeping in
> the clk framework?

Yes, that's why we store all registers in suspend.

> Is it necessary to do a tree walk and recalc rates after restoring these
> registers?

Nope, no recalc is needed. During suspend, the software logical state in clk
framework might not match the hardware in a few places: some provider's
internal muxes are reconfigured to lowest possible source (external
oscillator), because otherwise the hardware will lock the turning power
domain off. It is also a bit hard to say about provider's state when the
power domain is off, because the registers cannot be accessed at all (any
access will result external abort exception). However after resume, all
the mentioned muxes are restored to the values they had before the suspend,
so the hw state matches again the state in clock framework.

>> +
>> +       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;
>> +       int i;
>> +
>> +       info = of_device_get_match_data(dev);
>> +
>> +       data = devm_kzalloc(dev, sizeof(*data) +
>> +                           sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
>> +                           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);
>> +       }
>> +
>> +       for (i = 0; i < info->nr_clk_ids; ++i)
>> +               ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
>> +
>> +       ctx->clk_data.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);
> What's going on here with this weird clk_prepare_enable? It looks like
> it is never balanced with a disable_unprepare?

Well, in theory that disable_unprepare() has to be done in provider's remove
callback. But there is no exynos5433_cmu_remove function, because this 
driver
is so critical in the system, that it is marked with 
.suppress_bind_attrs and
hotplug/unplug is forbidden.

CLK_OF_DECLARE() initialized clocks also doesn't have any remove method.

Keeping this special parent clock enabled all the time is needed to make any
access to the given clock provider as well as even putting it into suspend.
Currently those clocks are marked with IS_CRITICAL flag in the parent
provider, but those flags might be removed once this patch is applied.

>> +
>> +       platform_set_drvdata(pdev, data);
>> +
>> +       /*
>> +        * 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);
> Hmm, I do wonder if this sort of initialization can be made generic for
> all other clk drivers that will use this. Thanksfully it is very few
> functions to call.

IMHO this can be only judged after some time, when more drivers will use 
this
feature.

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

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

* [PATCH v8 3/5] clk: samsung: exynos5433: Add support for runtime PM
@ 2017-08-11  6:34           ` Marek Szyprowski
  0 siblings, 0 replies; 33+ messages in thread
From: Marek Szyprowski @ 2017-08-11  6:34 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Michael,

On 2017-08-10 20:46, Michael Turquette wrote:
> Quoting Marek Szyprowski (2017-08-09 03:35:05)
>> Add runtime pm support for all clock controller units (CMU), which belong
>> 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 enter 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 being 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.
> I would have preferred two patches here. First to platform_driver-ify
> the samsung clk driver and the second to add the pm_runtime bits.
>
>> +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]);
>> +
>> +       /* for suspend some registers have to be set to certain values */
>> +       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);
> Will the restored clk hardware always match the software bookkeeping in
> the clk framework?

Yes, that's why we store all registers in suspend.

> Is it necessary to do a tree walk and recalc rates after restoring these
> registers?

Nope, no recalc is needed. During suspend, the software logical state in clk
framework might not match the hardware in a few places: some provider's
internal muxes are reconfigured to lowest possible source (external
oscillator), because otherwise the hardware will lock the turning power
domain off. It is also a bit hard to say about provider's state when the
power domain is off, because the registers cannot be accessed at all (any
access will result external abort exception). However after resume, all
the mentioned muxes are restored to the values they had before the suspend,
so the hw state matches again the state in clock framework.

>> +
>> +       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;
>> +       int i;
>> +
>> +       info = of_device_get_match_data(dev);
>> +
>> +       data = devm_kzalloc(dev, sizeof(*data) +
>> +                           sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
>> +                           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);
>> +       }
>> +
>> +       for (i = 0; i < info->nr_clk_ids; ++i)
>> +               ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
>> +
>> +       ctx->clk_data.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);
> What's going on here with this weird clk_prepare_enable? It looks like
> it is never balanced with a disable_unprepare?

Well, in theory that disable_unprepare() has to be done in provider's remove
callback. But there is no exynos5433_cmu_remove function, because this 
driver
is so critical in the system, that it is marked with 
.suppress_bind_attrs and
hotplug/unplug is forbidden.

CLK_OF_DECLARE() initialized clocks also doesn't have any remove method.

Keeping this special parent clock enabled all the time is needed to make any
access to the given clock provider as well as even putting it into suspend.
Currently those clocks are marked with IS_CRITICAL flag in the parent
provider, but those flags might be removed once this patch is applied.

>> +
>> +       platform_set_drvdata(pdev, data);
>> +
>> +       /*
>> +        * 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);
> Hmm, I do wonder if this sort of initialization can be made generic for
> all other clk drivers that will use this. Thanksfully it is very few
> functions to call.

IMHO this can be only judged after some time, when more drivers will use 
this
feature.

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

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

* Re: [PATCH v8 1/5] clk: Add support for runtime PM
  2017-08-11  6:18           ` Marek Szyprowski
  (?)
@ 2017-08-11 16:28             ` Michael Turquette
  -1 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-11 16:28 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-08-10 23:18:42)
> Hi Michael,
> =

> Thanks for the comments!
> =

> On 2017-08-10 20:28, Michael Turquette wrote:
> > Hi Marek,
> >
> > Quoting Marek Szyprowski (2017-08-09 03:35:03)
> >> 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.
> >>
> >> 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.
> >>
> >> Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
> >> Reviewed-by: Ulf Hansson <ulf.hansson@linaro.org>
> >> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> > Overall the idea and the patch look good to me.
> >
> > Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
> > makes perfect sense to me. It means that the power domain that powers
> > that clock hardware must be on for that clock to function properly.
> >
> > It=E2=80=99s worth pointing out that this patch leaves that power domai=
n enabled
> > for for as long as prepare_count is greater than zero.
> >
> > However the rate-change operations only use the pm_runtime functions to
> > program the hardware and modify the registers. At the end of each
> > rate-change call there is a pm_runtime_put. This makes sense to me, but
> > I=E2=80=99m trying to wrap my head around the fact that this patch intr=
oduces
> > two behaviors:
> >
> > 1) protect access to the clk hardware by wrapping writes across the bus
> > with pm_runtime calls
> > 2) power the clock logic when the clock is in use (e.g. clk_prepare())
> >
> > Everything about this patch *seems* correct to me, but I=E2=80=99m tryi=
ng to
> > figure out what it means to for a clock consumer driver to do something
> > like this:
> >
> > clk_prepare_enable()
> > clk_disable_unprepare()
> > clk_set_rate()
> >
> > Maybe patches #2-5 will help me understand, but I wanted to jot this
> > down in my review before I forget.
> =

> Well, the question is what will the above sequence do without my patches?
> =

> IMO the only result of above calls is (optionally) changed rate of parent
> clocks and appropriate values written to given clock's registers, so next
> time one calls clk_(prepare)_enable(), the given clock will be enabled
> with the configured rate.
> =

> With runtime pm patches the overall result will be the same. However, if
> the given clock is the only/last enabled clock from the provider, there
> might be a runtime pm resume/suspend transition (2 times: one from
> prepare/unprepare calls, the second from set_rate calls). This means that
> the rate of parent clocks (especially if they comes from the other
> providers) might or might not change between those transitions. This
> depends how the runtime pm is implemented and what operations has to be
> done for putting provider into suspend mode. The final result will be
> however the same as without runtime pm patches.
> =

> Clock provider's runtime pm suspend callback should save internal
> configuration and resume callback must restore it completely, so the
> provider's internal state is exactly the same as before suspend. If we
> assume that clock provider might be in a power domain, the internal
> clock provider's hardware context might be lost after suspend callback.
> =

> >> @@ -2583,6 +2673,12 @@ struct clk *clk_register(struct device *dev, st=
ruct 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;
> >> @@ -2629,12 +2725,13 @@ struct clk *clk_register(struct device *dev, s=
truct clk_hw *hw)
> >>          }
> >>   =

> >>          ret =3D __clk_core_init(core);
> >> -       if (!ret)
> >> +       if (!ret) {
> >> +               clk_pm_runtime_put(core);
> >>                  return hw->clk;
> >> +       }
> > I wonder if the above can be moved inside of __clk_core_init?
> > clk_register shouldn't manage any clk_ops directly, only __clk_core_init
> > does this.
> =

> If you prefer that, I will move runtime pm related bits to =

> __clk_core_init then.

Please do. After that I can pull v9 into clk-next.

Thanks,
Mike

> =

> Best regards
> -- =

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

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

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

* Re: [PATCH v8 1/5] clk: Add support for runtime PM
@ 2017-08-11 16:28             ` Michael Turquette
  0 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-11 16:28 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-08-10 23:18:42)
> Hi Michael,
> 
> Thanks for the comments!
> 
> On 2017-08-10 20:28, Michael Turquette wrote:
> > Hi Marek,
> >
> > Quoting Marek Szyprowski (2017-08-09 03:35:03)
> >> 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>
> >> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> > Overall the idea and the patch look good to me.
> >
> > Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
> > makes perfect sense to me. It means that the power domain that powers
> > that clock hardware must be on for that clock to function properly.
> >
> > It’s worth pointing out that this patch leaves that power domain enabled
> > for for as long as prepare_count is greater than zero.
> >
> > However the rate-change operations only use the pm_runtime functions to
> > program the hardware and modify the registers. At the end of each
> > rate-change call there is a pm_runtime_put. This makes sense to me, but
> > I’m trying to wrap my head around the fact that this patch introduces
> > two behaviors:
> >
> > 1) protect access to the clk hardware by wrapping writes across the bus
> > with pm_runtime calls
> > 2) power the clock logic when the clock is in use (e.g. clk_prepare())
> >
> > Everything about this patch *seems* correct to me, but I’m trying to
> > figure out what it means to for a clock consumer driver to do something
> > like this:
> >
> > clk_prepare_enable()
> > clk_disable_unprepare()
> > clk_set_rate()
> >
> > Maybe patches #2-5 will help me understand, but I wanted to jot this
> > down in my review before I forget.
> 
> Well, the question is what will the above sequence do without my patches?
> 
> IMO the only result of above calls is (optionally) changed rate of parent
> clocks and appropriate values written to given clock's registers, so next
> time one calls clk_(prepare)_enable(), the given clock will be enabled
> with the configured rate.
> 
> With runtime pm patches the overall result will be the same. However, if
> the given clock is the only/last enabled clock from the provider, there
> might be a runtime pm resume/suspend transition (2 times: one from
> prepare/unprepare calls, the second from set_rate calls). This means that
> the rate of parent clocks (especially if they comes from the other
> providers) might or might not change between those transitions. This
> depends how the runtime pm is implemented and what operations has to be
> done for putting provider into suspend mode. The final result will be
> however the same as without runtime pm patches.
> 
> Clock provider's runtime pm suspend callback should save internal
> configuration and resume callback must restore it completely, so the
> provider's internal state is exactly the same as before suspend. If we
> assume that clock provider might be in a power domain, the internal
> clock provider's hardware context might be lost after suspend callback.
> 
> >> @@ -2583,6 +2673,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;
> >> @@ -2629,12 +2725,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;
> >> +       }
> > I wonder if the above can be moved inside of __clk_core_init?
> > clk_register shouldn't manage any clk_ops directly, only __clk_core_init
> > does this.
> 
> If you prefer that, I will move runtime pm related bits to 
> __clk_core_init then.

Please do. After that I can pull v9 into clk-next.

Thanks,
Mike

> 
> Best regards
> -- 
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH v8 1/5] clk: Add support for runtime PM
@ 2017-08-11 16:28             ` Michael Turquette
  0 siblings, 0 replies; 33+ messages in thread
From: Michael Turquette @ 2017-08-11 16:28 UTC (permalink / raw)
  To: linux-arm-kernel

Quoting Marek Szyprowski (2017-08-10 23:18:42)
> Hi Michael,
> 
> Thanks for the comments!
> 
> On 2017-08-10 20:28, Michael Turquette wrote:
> > Hi Marek,
> >
> > Quoting Marek Szyprowski (2017-08-09 03:35:03)
> >> 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>
> >> Acked-by: Krzysztof Kozlowski <krzk@kernel.org>
> > Overall the idea and the patch look good to me.
> >
> > Calling pm_runtime_get for prepare and pm_runtime_put for unprepared
> > makes perfect sense to me. It means that the power domain that powers
> > that clock hardware must be on for that clock to function properly.
> >
> > It?s worth pointing out that this patch leaves that power domain enabled
> > for for as long as prepare_count is greater than zero.
> >
> > However the rate-change operations only use the pm_runtime functions to
> > program the hardware and modify the registers. At the end of each
> > rate-change call there is a pm_runtime_put. This makes sense to me, but
> > I?m trying to wrap my head around the fact that this patch introduces
> > two behaviors:
> >
> > 1) protect access to the clk hardware by wrapping writes across the bus
> > with pm_runtime calls
> > 2) power the clock logic when the clock is in use (e.g. clk_prepare())
> >
> > Everything about this patch *seems* correct to me, but I?m trying to
> > figure out what it means to for a clock consumer driver to do something
> > like this:
> >
> > clk_prepare_enable()
> > clk_disable_unprepare()
> > clk_set_rate()
> >
> > Maybe patches #2-5 will help me understand, but I wanted to jot this
> > down in my review before I forget.
> 
> Well, the question is what will the above sequence do without my patches?
> 
> IMO the only result of above calls is (optionally) changed rate of parent
> clocks and appropriate values written to given clock's registers, so next
> time one calls clk_(prepare)_enable(), the given clock will be enabled
> with the configured rate.
> 
> With runtime pm patches the overall result will be the same. However, if
> the given clock is the only/last enabled clock from the provider, there
> might be a runtime pm resume/suspend transition (2 times: one from
> prepare/unprepare calls, the second from set_rate calls). This means that
> the rate of parent clocks (especially if they comes from the other
> providers) might or might not change between those transitions. This
> depends how the runtime pm is implemented and what operations has to be
> done for putting provider into suspend mode. The final result will be
> however the same as without runtime pm patches.
> 
> Clock provider's runtime pm suspend callback should save internal
> configuration and resume callback must restore it completely, so the
> provider's internal state is exactly the same as before suspend. If we
> assume that clock provider might be in a power domain, the internal
> clock provider's hardware context might be lost after suspend callback.
> 
> >> @@ -2583,6 +2673,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;
> >> @@ -2629,12 +2725,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;
> >> +       }
> > I wonder if the above can be moved inside of __clk_core_init?
> > clk_register shouldn't manage any clk_ops directly, only __clk_core_init
> > does this.
> 
> If you prefer that, I will move runtime pm related bits to 
> __clk_core_init then.

Please do. After that I can pull v9 into clk-next.

Thanks,
Mike

> 
> Best regards
> -- 
> Marek Szyprowski, PhD
> Samsung R&D Institute Poland
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-clk" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2017-08-11 16:28 UTC | newest]

Thread overview: 33+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CGME20170809103512eucas1p2e12493a126ee10419f84ae9890e3450b@eucas1p2.samsung.com>
2017-08-09 10:35 ` [PATCH v8 0/5] Add runtime PM support for clocks (on Exynos SoC example) Marek Szyprowski
2017-08-09 10:35   ` Marek Szyprowski
     [not found]   ` <CGME20170809103512eucas1p2eb66401b357d82bed91182a5a1b1fea9@eucas1p2.samsung.com>
2017-08-09 10:35     ` [PATCH v8 1/5] clk: Add support for runtime PM Marek Szyprowski
2017-08-09 10:35       ` Marek Szyprowski
2017-08-10 18:28       ` Michael Turquette
2017-08-10 18:28         ` Michael Turquette
2017-08-10 18:28         ` Michael Turquette
2017-08-11  6:18         ` Marek Szyprowski
2017-08-11  6:18           ` Marek Szyprowski
2017-08-11 16:28           ` Michael Turquette
2017-08-11 16:28             ` Michael Turquette
2017-08-11 16:28             ` Michael Turquette
     [not found]   ` <CGME20170809103513eucas1p25e142aac911838847e70a036c5d93131@eucas1p2.samsung.com>
2017-08-09 10:35     ` [PATCH v8 2/5] clk: samsung: " Marek Szyprowski
2017-08-09 10:35       ` Marek Szyprowski
     [not found]   ` <CGME20170809103514eucas1p1d09fbd4d6b9954b24fb6280c7d9eaa02@eucas1p1.samsung.com>
2017-08-09 10:35     ` [PATCH v8 3/5] clk: samsung: exynos5433: " Marek Szyprowski
2017-08-09 10:35       ` Marek Szyprowski
2017-08-10 18:46       ` Michael Turquette
2017-08-10 18:46         ` Michael Turquette
2017-08-10 18:46         ` Michael Turquette
2017-08-11  6:34         ` Marek Szyprowski
2017-08-11  6:34           ` Marek Szyprowski
     [not found]   ` <CGME20170809103514eucas1p11fb5d1af243e882a5d51a25956d24a19@eucas1p1.samsung.com>
2017-08-09 10:35     ` [PATCH v8 4/5] clk: samsung: exynos-audss: Use local variable for controller's device Marek Szyprowski
2017-08-09 10:35       ` Marek Szyprowski
2017-08-10  5:53       ` Krzysztof Kozlowski
2017-08-10  5:53         ` Krzysztof Kozlowski
2017-08-10  6:27       ` Chanwoo Choi
2017-08-10  6:27         ` Chanwoo Choi
     [not found]   ` <CGME20170809103515eucas1p19fcbdd113e191cc02f9e5aaa469376d3@eucas1p1.samsung.com>
2017-08-09 10:35     ` [PATCH v8 5/5] clk: samsung: exynos-audss: Add support for runtime PM Marek Szyprowski
2017-08-09 10:35       ` Marek Szyprowski
2017-08-10  5:51       ` Krzysztof Kozlowski
2017-08-10  5:51         ` Krzysztof Kozlowski
2017-08-10  6:46       ` Chanwoo Choi
2017-08-10  6:46         ` Chanwoo Choi

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.