Skip to contentSkip to conversation

Add runtime PM support for clocks (on Exynos SoC example)

PATCH Superseded Marek Szyprowski 3 participants 13 messages Sep 19, 2016 – Oct 10, 2016 1 message not in the archive

Subsystems (4)

v2 of 9 Superseded Latest is v9 →

lore
Share & tools
lore

A message that is not in the archive (sent off-list or not yet received): CGME20160919105545eucas1p1026e435bbaeb1f5c866a9abcdd051464@eucas1p1.samsung.com

#1 lore raw

v2 cover Add runtime PM support for clocks (on Exynos SoC example)

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 4412 and 5433 SoCs, 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 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 exynos 4412 and 5433 clocks drivers change the way the clock provider is initialized. Instead of CLK_OF_DECLARE based initialization, a complete platform device driver infrastructure is being used. This is needed to let driver to use runtime pm feature and integrate with generic power domains. The side-effect of this change is a delay in clock provider registeration during system boot, so early initialized drivers might get EPROBEDEFER error when requesting their clocks. This is an issue for IOMMU drivers, so this patchset will be fully functional once the deferred probe for IOMMU will be merged.

The side-effect of this patchset is the one can finally read /sys/kernel/debug/clk/clk_summary on all Exynos4412 boards without any freeze.

If one wants to test this patchset (on Exynos4412 Trats2 device with FIMC-IS driver), I've provided a branch with all needed patches (fixes for Exynos, FIMC-IS driver and IOMMU deferred probe):
https://git.linaro.org/people/marek.szyprowski/linux-srpol.git v4.8-clocks-pm-v2

Patches are based on vanilla v4.8-rc7 kernel.

Best regards
Marek Szyprowski
Samsung R&D Institute Poland

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

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

Marek Szyprowski (5):
clk: add support for runtime pm
clock: samsung: add support for runtime pm
clocks: exynos4x12: add runtime pm support for ISP clocks
ARM: dts: exynos: add support for ISP power domain to exynos4x12
clocks device
clocks: exynos5433: add runtime pm support

 .../devicetree/bindings/clock/exynos4-clock.txt    |  22 ++
 arch/arm/boot/dts/exynos4x12.dtsi                  |   5 +
 drivers/clk/clk.c                                  | 107 +++++-
 drivers/clk/samsung/clk-exynos4.c                  | 227 ++++++++----
 drivers/clk/samsung/clk-exynos5433.c               | 385 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   4 +-
 drivers/clk/samsung/clk.c                          |  36 +-
 drivers/clk/samsung/clk.h                          |   7 +
 include/linux/clk-provider.h                       |   1 +
 9 files changed, 632 insertions(+), 162 deletions(-)
Signature · 1 line
1.9.1
#2 lore raw

PATCH v2 1/5 clk: add support for runtime pm

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

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

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

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

---
 drivers/clk/clk.c            | 107 +++++++++++++++++++++++++++++++++++++++----
 include/linux/clk-provider.h |   1 +
 2 files changed, 98 insertions(+), 10 deletions(-)
drivers/clk/clk.c +97 -10
index 820a939fb6bb..096a199b8e46 100644
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
 	const struct clk_ops	*ops;
 	struct clk_hw		*hw;
 	struct module		*owner;
+	struct device		*dev;
 	struct clk_core		*parent;
 	const char		**parent_names;
 	struct clk_core		**parents;
@@ -87,6 +89,26 @@ struct clk {
 	struct hlist_node clks_node;
 };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+	int ret = 0;
+
+	if (!core->dev)
+		return 0;
+
+	ret = pm_runtime_get_sync(core->dev);
+	return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+	if (!core->dev)
+		return;
+
+	pm_runtime_put(core->dev);
+}
+
 /***           locking             ***/
 static void clk_prepare_lock(void)
 {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static bool clk_core_is_prepared(struct clk_core *core)
 {
+	bool status;
+
 	/*
 	 * .is_prepared is optional for clocks that can prepare
 	 * fall back to software usage counter if it is missing
@@ -157,11 +181,17 @@ 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);
+	clk_pm_runtime_get(core);
+	status = core->ops->is_prepared(core->hw);
+	clk_pm_runtime_put(core);
+
+	return status;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+	bool status;
+
 	/*
 	 * .is_enabled is only mandatory for clocks that gate
 	 * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +199,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 runtime pm is enabled 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 place.
+	 */
+	if (core->dev) {
+		pm_runtime_get_noresume(core->dev);
+		if (pm_runtime_suspended(core->dev)) {
+			status = false;
+			goto done;
+		}
+	}
+
+	status = core->ops->is_enabled(core->hw);
+done:
+	if (core->dev)
+		pm_runtime_put(core->dev);
+
+	return status;
 }
 
 /***    helper functions   ***/
@@ -489,6 +541,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 +584,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 +599,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 +806,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
 	if (core->flags & CLK_IGNORE_UNUSED)
 		return;
 
+	if (clk_pm_runtime_get(core) != 0)
+		return;
+
 	if (clk_core_is_prepared(core)) {
 		trace_clk_unprepare(core);
 		if (core->ops->unprepare_unused)
@@ -753,6 +817,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 +834,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) != 0)
+		return;
+
 	flags = clk_enable_lock();
 
 	if (core->enable_count)
@@ -794,6 +863,8 @@ unlock_out:
 	clk_enable_unlock(flags);
 	if (core->flags & CLK_OPS_PARENT_ENABLE)
 		clk_core_disable_unprepare(core->parent);
+
+	clk_pm_runtime_put(core);
 }
 
 static bool clk_ignore_unused;
@@ -1563,6 +1634,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 {
 	struct clk_core *top, *fail_clk;
 	unsigned long rate = req_rate;
+	int ret = 0;
 
 	if (!core)
 		return 0;
@@ -1579,21 +1651,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (!top)
 		return -EINVAL;
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		return ret;
+
 	/* notify that we are about to change rates */
 	fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
 	if (fail_clk) {
 		pr_debug("%s: failed to set %s rate\n", __func__,
 				fail_clk->name);
 		clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-		return -EBUSY;
+		ret = -EBUSY;
+		goto err;
 	}
 
 	/* change the rates */
 	clk_change_rate(top);
 
 	core->req_rate = req_rate;
+err:
+	clk_pm_runtime_put(core);
 
-	return 0;
+	return ret;
 }
 
 /**
@@ -1824,12 +1903,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		p_rate = parent->rate;
 	}
 
+	ret = clk_pm_runtime_get(core);
+	if (ret)
+		goto out;
+
 	/* propagate PRE_RATE_CHANGE notifications */
 	ret = __clk_speculate_rates(core, p_rate);
 
 	/* abort if a driver objects */
 	if (ret & NOTIFY_STOP_MASK)
-		goto out;
+		goto runtime_put;
 
 	/* do the re-parent */
 	ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1925,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();
 
@@ -2546,6 +2631,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
 		goto fail_name;
 	}
 	core->ops = hw->init->ops;
+	if (dev && (hw->init->flags & CLK_RUNTIME_PM))
+		core->dev = dev;
 	if (dev && dev->driver)
 		core->owner = dev->driver->owner;
 	core->hw = hw;
include/linux/clk-provider.h +1 -0
index a39c0c530778..8a131eb71fdf 100644
@@ -35,6 +35,7 @@
 #define CLK_IS_CRITICAL		BIT(11) /* do not gate, ever */
 /* parents need enable during gate/ungate, set rate and re-parent */
 #define CLK_OPS_PARENT_ENABLE	BIT(12)
+#define CLK_RUNTIME_PM		BIT(13)
 
 struct clk;
 struct clk_hw;
Signature · 1 line
1.9.1
#3 lore raw

PATCH v2 1/5 clk: add support for runtime pm

Quoted 89 lines · Marek Szyprowski

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

This patch adds basic infrastructure in the clocks core to allow implementing driver for such clocks under power domains. Clock provider can supply a struct device pointer, which is the used by clock core for tracking and managing clock's controller runtime pm state. Each clk_prepare() operation will first call pm_runtime_get_sync() on the supplied device, while clk_unprepare() will do pm_runtime_put() 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 and set CLK_RUNTIME_PM flags for its clocks.
2. It needs to enable runtime PM for that device.
3. It needs to make sure the runtime PM status of the controller device reflects the HW state.

---
 drivers/clk/clk.c            | 107 +++++++++++++++++++++++++++++++++++++++----
 include/linux/clk-provider.h |   1 +
 2 files changed, 98 insertions(+), 10 deletions(-)
drivers/clk/clk.c +24 -0
index 820a939fb6bb..096a199b8e46 100644
@@ -21,6 +21,7 @@
 #include <linux/of.h>
 #include <linux/device.h>
 #include <linux/init.h>
+#include <linux/pm_runtime.h>
 #include <linux/sched.h>
 #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
        const struct clk_ops    *ops;
        struct clk_hw           *hw;
        struct module           *owner;
+       struct device           *dev;
        struct clk_core         *parent;
        const char              **parent_names;
        struct clk_core         **parents;
@@ -87,6 +89,26 @@ struct clk {
        struct hlist_node clks_node;
 };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+       int ret = 0;
+
+       if (!core->dev)
+               return 0;
+
+       ret = pm_runtime_get_sync(core->dev);
+       return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+       if (!core->dev)
+               return;
+
+       pm_runtime_put(core->dev);
+}
+
 /***           locking             ***/
 static void clk_prepare_lock(void)
 {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
 static bool clk_core_is_prepared(struct clk_core *core)
 {
+       bool status;
+
        /*
         * .is_prepared is optional for clocks that can prepare
         * fall back to software usage counter if it is missing

drivers/clk/clk.c static bool clk_core_is_prepared(struct clk_core *core) · line 184

Quoted 5 lines
drivers/clk/clk.c +1 -1
@@ -157,11 +181,17 @@ 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);
+       clk_pm_runtime_get(core);

I guess you should assign status to the return code, and check it.

Quoted 20 lines
drivers/clk/clk.c +8 -1
+       status = core->ops->is_prepared(core->hw);
+       clk_pm_runtime_put(core);
+
+       return status;
 }
 
 static bool clk_core_is_enabled(struct clk_core *core)
 {
+       bool status;
+
        /*
         * .is_enabled is only mandatory for clocks that gate
         * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +199,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 runtime pm is enabled before calling .is_enabled callback,

drivers/clk/clk.c static bool clk_core_is_enabled(struct clk_core *core) · line 213

Quoted 10 lines
drivers/clk/clk.c +10 -0
+        * 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 place.
+        */
+       if (core->dev) {
+               pm_runtime_get_noresume(core->dev);
+               if (pm_runtime_suspended(core->dev)) {

I think it's wrong to use pm_runtime_suspended().

What you should be checking, is whether the device is RPM_ACTIVE or if runtime PM isn't enabled for device.

In other words, you should use pm_runtime_active() to find out whether it's okay to invoke the ->is_enabled() ops or not.

Accordingly, I think the upper comment you added then needs to be a rephrased a bit to reflect this.

Quoted 63 lines
drivers/clk/clk.c +25 -5
+                       status = false;
+                       goto done;
+               }
+       }
+
+       status = core->ops->is_enabled(core->hw);
+done:
+       if (core->dev)
+               pm_runtime_put(core->dev);
+
+       return status;
 }
 
 /***    helper functions   ***/
@@ -489,6 +541,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 +584,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 +599,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)

drivers/clk/clk.c static void clk_unprepare_unused_subtree(struct clk_core *core) · line 809

Quoted 4 lines
drivers/clk/clk.c +1 -0
@@ -745,6 +806,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
        if (core->flags & CLK_IGNORE_UNUSED)
                return;
 
+       if (clk_pm_runtime_get(core) != 0)

You may simplify this:
if (clk_pm_runtime_get(core))

Quoted 15 lines
drivers/clk/clk.c +4 -0
+               return;
+
        if (clk_core_is_prepared(core)) {
                trace_clk_unprepare(core);
                if (core->ops->unprepare_unused)
@@ -753,6 +817,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)

drivers/clk/clk.c static void clk_disable_unused_subtree(struct clk_core *core) · line 837

Quoted 4 lines
drivers/clk/clk.c +1 -0
@@ -768,6 +834,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) != 0)

Is there any reason to why you haven't moved this further down in this function, like just before calling clk_core_is_enabled()?

You may also simplify this:
if (clk_pm_runtime_get(core))

drivers/clk/clk.c static void clk_disable_unused_subtree(struct clk_core *core) · line 839

Quoted 2 lines
drivers/clk/clk.c +2 -0
+               return;
+

You need to restore the call made to clk_core_prepare_enable() earlier, so please update the error handling to cope with this.

Quoted 79 lines
drivers/clk/clk.c +19 -3
        flags = clk_enable_lock();
 
        if (core->enable_count)
@@ -794,6 +863,8 @@ unlock_out:
        clk_enable_unlock(flags);
        if (core->flags & CLK_OPS_PARENT_ENABLE)
                clk_core_disable_unprepare(core->parent);
+
+       clk_pm_runtime_put(core);
 }
 
 static bool clk_ignore_unused;
@@ -1563,6 +1634,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 {
        struct clk_core *top, *fail_clk;
        unsigned long rate = req_rate;
+       int ret = 0;
 
        if (!core)
                return 0;
@@ -1579,21 +1651,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
        if (!top)
                return -EINVAL;
 
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               return ret;
+
        /* notify that we are about to change rates */
        fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
        if (fail_clk) {
                pr_debug("%s: failed to set %s rate\n", __func__,
                                fail_clk->name);
                clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-               return -EBUSY;
+               ret = -EBUSY;
+               goto err;
        }
 
        /* change the rates */
        clk_change_rate(top);
 
        core->req_rate = req_rate;
+err:
+       clk_pm_runtime_put(core);
 
-       return 0;
+       return ret;
 }
 
 /**
@@ -1824,12 +1903,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
                p_rate = parent->rate;
        }
 
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               goto out;
+
        /* propagate PRE_RATE_CHANGE notifications */
        ret = __clk_speculate_rates(core, p_rate);
 
        /* abort if a driver objects */
        if (ret & NOTIFY_STOP_MASK)
-               goto out;
+               goto runtime_put;
 
        /* do the re-parent */
        ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1925,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();
 

drivers/clk/clk.c struct clk *clk_register(struct device *dev, struct clk_hw *hw) · line 2635

Quoted 5 lines
drivers/clk/clk.c +2 -0
@@ -2546,6 +2631,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
                goto fail_name;
        }
        core->ops = hw->init->ops;
+       if (dev && (hw->init->flags & CLK_RUNTIME_PM))
+               core->dev = dev;

I guess you need this to play safe, although I am really wondering if we should try without.

Not that many clocks are currently being registered with a valid struct device pointer. For the other cases why not try to use runtime PM as per default?

Moreover we anyway rely on the clock provider to enable runtime PM for the clock device, and when that isn't the case the runtime PM deployment in the core should still be safe, right!?

Quoted 16 lines
drivers/clk/clk.c +0 -0
        if (dev && dev->driver)
                core->owner = dev->driver->owner;
        core->hw = hw;
include/linux/clk-provider.h +1 -1
index a39c0c530778..8a131eb71fdf 100644
@@ -35,6 +35,7 @@
 #define CLK_IS_CRITICAL                BIT(11) /* do not gate, ever */
 /* parents need enable during gate/ungate, set rate and re-parent */
 #define CLK_OPS_PARENT_ENABLE  BIT(12)
+#define CLK_RUNTIME_PM         BIT(13)
 
 struct clk;
 struct clk_hw;
--
Quoted 2 lines

1.9.1

Kind regards
Uffe

#4 lore raw

PATCH v2 1/5 clk: add support for runtime pm

Hi Ulf,

Thanks for your comments!

Quoted 94 lines · Ulf Hansson
Quoted 92 lines · Marek Szyprowski

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

This patch adds basic infrastructure in the clocks core to allow implementing driver for such clocks under power domains. Clock provider can supply a struct device pointer, which is the used by clock core for tracking and managing clock's controller runtime pm state. Each clk_prepare() operation will first call pm_runtime_get_sync() on the supplied device, while clk_unprepare() will do pm_runtime_put() 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 and set CLK_RUNTIME_PM flags for its clocks.
2. It needs to enable runtime PM for that device.
3. It needs to make sure the runtime PM status of the controller device reflects the HW state.

---
  drivers/clk/clk.c            | 107 +++++++++++++++++++++++++++++++++++++++----
  include/linux/clk-provider.h |   1 +
  2 files changed, 98 insertions(+), 10 deletions(-)
drivers/clk/clk.c +24 -0
index 820a939fb6bb..096a199b8e46 100644
@@ -21,6 +21,7 @@
  #include <linux/of.h>
  #include <linux/device.h>
  #include <linux/init.h>
+#include <linux/pm_runtime.h>
  #include <linux/sched.h>
  #include <linux/clkdev.h>
 
@@ -46,6 +47,7 @@ struct clk_core {
         const struct clk_ops    *ops;
         struct clk_hw           *hw;
         struct module           *owner;
+       struct device           *dev;
         struct clk_core         *parent;
         const char              **parent_names;
         struct clk_core         **parents;
@@ -87,6 +89,26 @@ struct clk {
         struct hlist_node clks_node;
  };
 
+/***           runtime pm          ***/
+static int clk_pm_runtime_get(struct clk_core *core)
+{
+       int ret = 0;
+
+       if (!core->dev)
+               return 0;
+
+       ret = pm_runtime_get_sync(core->dev);
+       return ret < 0 ? ret : 0;
+}
+
+static void clk_pm_runtime_put(struct clk_core *core)
+{
+       if (!core->dev)
+               return;
+
+       pm_runtime_put(core->dev);
+}
+
  /***           locking             ***/
  static void clk_prepare_lock(void)
  {
@@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
 
  static bool clk_core_is_prepared(struct clk_core *core)
  {
+       bool status;
+
         /*
          * .is_prepared is optional for clocks that can prepare
          * fall back to software usage counter if it is missing
@@ -157,11 +181,17 @@ static bool clk_core_is_prepared(struct clk_core *core)
         if (!core->ops->is_prepared)
                 return core->prepare_count;
 
Quoted 3 lines

drivers/clk/clk.c static bool clk_core_is_prepared(struct clk_core *core) · line 184

Quoted 2 lines
drivers/clk/clk.c +1 -1
-       return core->ops->is_prepared(core->hw);
+       clk_pm_runtime_get(core);

I guess you should assign status to the return code, and check it.

Okay. I assume that in case of any failure from runtime pm, the function should return false?

Quoted 33 lines
Quoted 21 lines
drivers/clk/clk.c +8 -1
+       status = core->ops->is_prepared(core->hw);
+       clk_pm_runtime_put(core);
+
+       return status;
  }
 
  static bool clk_core_is_enabled(struct clk_core *core)
  {
+       bool status;
+
         /*
          * .is_enabled is only mandatory for clocks that gate
          * fall back to software usage counter if .is_enabled is missing
@@ -169,7 +199,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 runtime pm is enabled before calling .is_enabled callback,

drivers/clk/clk.c static bool clk_core_is_enabled(struct clk_core *core) · line 213

Quoted 10 lines
drivers/clk/clk.c +10 -0
+        * 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 place.
+        */
+       if (core->dev) {
+               pm_runtime_get_noresume(core->dev);
+               if (pm_runtime_suspended(core->dev)) {

I think it's wrong to use pm_runtime_suspended().

Quoted 8 lines

What you should be checking, is whether the device is RPM_ACTIVE or if runtime PM isn't enabled for device.

In other words, you should use pm_runtime_active() to find out whether it's okay to invoke the ->is_enabled() ops or not.

Accordingly, I think the upper comment you added then needs to be a rephrased a bit to reflect this.

Okay, I will change it to use pm_runtime_active(). It looks that mentally I still assume that runtime pm will be disabled in system sleep transitions phase, so the only check that provided some useful information about previous runtime pm state was pm_runtime_suspended().

Quoted 84 lines
Quoted 64 lines
drivers/clk/clk.c +25 -5
+                       status = false;
+                       goto done;
+               }
+       }
+
+       status = core->ops->is_enabled(core->hw);
+done:
+       if (core->dev)
+               pm_runtime_put(core->dev);
+
+       return status;
  }
 
  /***    helper functions   ***/
@@ -489,6 +541,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 +584,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 +599,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)

drivers/clk/clk.c static void clk_unprepare_unused_subtree(struct clk_core *core) · line 809

Quoted 4 lines
drivers/clk/clk.c +1 -0
@@ -745,6 +806,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
         if (core->flags & CLK_IGNORE_UNUSED)
                 return;
 
+       if (clk_pm_runtime_get(core) != 0)

You may simplify this:
if (clk_pm_runtime_get(core))

Quoted 13 lines
drivers/clk/clk.c +4 -0
+               return;
+
         if (clk_core_is_prepared(core)) {
                 trace_clk_unprepare(core);
                 if (core->ops->unprepare_unused)
@@ -753,6 +817,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);
  }
 
Quoted 8 lines

drivers/clk/clk.c static void clk_disable_unused_subtree(struct clk_core *core) · line 837

Quoted 6 lines
drivers/clk/clk.c +1 -0
  static void clk_disable_unused_subtree(struct clk_core *core)
@@ -768,6 +834,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) != 0)

Is there any reason to why you haven't moved this further down in this function, like just before calling clk_core_is_enabled()?

Yes, clk_enable_lock() takes a spinlock, so I cannot call pm_runtime_get after it.

Quoted 96 lines

You may also simplify this:
if (clk_pm_runtime_get(core))

drivers/clk/clk.c static void clk_disable_unused_subtree(struct clk_core *core) · line 839

Quoted 2 lines
drivers/clk/clk.c +2 -0
+               return;
+

You need to restore the call made to clk_core_prepare_enable() earlier, so please update the error handling to cope with this.

Quoted 79 lines
drivers/clk/clk.c +19 -3
         flags = clk_enable_lock();
 
         if (core->enable_count)
@@ -794,6 +863,8 @@ unlock_out:
         clk_enable_unlock(flags);
         if (core->flags & CLK_OPS_PARENT_ENABLE)
                 clk_core_disable_unprepare(core->parent);
+
+       clk_pm_runtime_put(core);
  }
 
  static bool clk_ignore_unused;
@@ -1563,6 +1634,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
  {
         struct clk_core *top, *fail_clk;
         unsigned long rate = req_rate;
+       int ret = 0;
 
         if (!core)
                 return 0;
@@ -1579,21 +1651,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
         if (!top)
                 return -EINVAL;
 
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               return ret;
+
         /* notify that we are about to change rates */
         fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
         if (fail_clk) {
                 pr_debug("%s: failed to set %s rate\n", __func__,
                                 fail_clk->name);
                 clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
-               return -EBUSY;
+               ret = -EBUSY;
+               goto err;
         }
 
         /* change the rates */
         clk_change_rate(top);
 
         core->req_rate = req_rate;
+err:
+       clk_pm_runtime_put(core);
 
-       return 0;
+       return ret;
  }
 
  /**
@@ -1824,12 +1903,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
                 p_rate = parent->rate;
         }
 
+       ret = clk_pm_runtime_get(core);
+       if (ret)
+               goto out;
+
         /* propagate PRE_RATE_CHANGE notifications */
         ret = __clk_speculate_rates(core, p_rate);
 
         /* abort if a driver objects */
         if (ret & NOTIFY_STOP_MASK)
-               goto out;
+               goto runtime_put;
 
         /* do the re-parent */
         ret = __clk_set_parent(core, parent, p_index);
@@ -1842,6 +1925,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();
 

drivers/clk/clk.c struct clk *clk_register(struct device *dev, struct clk_hw *hw) · line 2635

Quoted 5 lines
drivers/clk/clk.c +2 -0
@@ -2546,6 +2631,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
                 goto fail_name;
         }
         core->ops = hw->init->ops;
+       if (dev && (hw->init->flags & CLK_RUNTIME_PM))
+               core->dev = dev;

I guess you need this to play safe, although I am really wondering if we should try without.

Quoted 3 lines

Not that many clocks are currently being registered with a valid struct device pointer. For the other cases why not try to use runtime PM as per default?

I've that tried initially, but it causes failure for all the clock controllers, which don't enable runtime pm. One of such case is max77686 PMIC, which provides 3 clocks. Maybe a negative flag (CLK_NO_RUNTIME_PM) will be a better solution, so by default the runtime pm calls will be enabled for every driver providing struct device?

Quoted 3 lines

Moreover we anyway rely on the clock provider to enable runtime PM for the clock device, and when that isn't the case the runtime PM deployment in the core should still be safe, right!?

I don't get the above comment. Do you want to check if runtime pm has been enabled during clock registration?

Quoted 13 lines
Quoted 13 lines
drivers/clk/clk.c +0 -0
        if (dev && dev->driver)
                 core->owner = dev->driver->owner;
         core->hw = hw;
include/linux/clk-provider.h +1 -0
index a39c0c530778..8a131eb71fdf 100644
@@ -35,6 +35,7 @@
  #define CLK_IS_CRITICAL                BIT(11) /* do not gate, ever */
  /* parents need enable during gate/ungate, set rate and re-parent */
  #define CLK_OPS_PARENT_ENABLE  BIT(12)
+#define CLK_RUNTIME_PM         BIT(13)
 
Quoted 6 lines
Quoted 5 lines
include/linux/clk-provider.h +0 -1
  struct clk;
  struct clk_hw;
--

1.9.1

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
#5 lore raw

PATCH v2 1/5 clk: add support for runtime pm

[...]

Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
drivers/clk/clk.c +0 -0
@@ -157,11 +181,17 @@ static bool clk_core_is_prepared(struct clk_core
 *core)
         if (!core->ops->is_prepared)
                 return core->prepare_count;
 
Quoted 8 lines
Quoted 4 lines

drivers/clk/clk.c static bool clk_core_is_prepared(struct clk_core *core) · line 184

Quoted 2 lines
drivers/clk/clk.c +1 -1
-       return core->ops->is_prepared(core->hw);
+       clk_pm_runtime_get(core);

I guess you should assign status to the return code, and check it.

Okay. I assume that in case of any failure from runtime pm, the function should return false?

I think so, yes.

Quoted 2 lines

[...]

Quoted 6 lines
Quoted 6 lines
Quoted 6 lines

static void clk_disable_unused_subtree(struct clk_core *core)

drivers/clk/clk.c +0 -0
@@ -768,6 +834,9 @@ static void clk_disable_unused_subtree(struct
 clk_core *core)
         if (core->flags & CLK_OPS_PARENT_ENABLE)
                 clk_core_prepare_enable(core->parent);
 
Quoted 8 lines
Quoted 4 lines

Review static void clk_disable_unused_subtree(struct · line 838

Quoted 1 line
(quoted diff) +1 -0
+       if (clk_pm_runtime_get(core) != 0)

Is there any reason to why you haven't moved this further down in this function, like just before calling clk_core_is_enabled()?

Yes, clk_enable_lock() takes a spinlock, so I cannot call pm_runtime_get after it.

Of course, you are right!

Quoted 12 lines
Quoted 12 lines

You may also simplify this:
if (clk_pm_runtime_get(core))

Review static void clk_disable_unused_subtree(struct · line 840

Quoted 2 lines
(quoted diff) +2 -0
+               return;
+

You need to restore the call made to clk_core_prepare_enable() earlier, so please update the error handling to cope with this.

[...]

Quoted 16 lines
Quoted 11 lines

drivers/clk/clk.c struct clk *clk_register(struct device *dev, struct · line 2636

Quoted 7 lines
drivers/clk/clk.c +2 -0
@@ -2546,6 +2631,8 @@ struct clk *clk_register(struct device *dev, struct
 clk_hw *hw)
                 goto fail_name;
         }
         core->ops = hw->init->ops;
+       if (dev && (hw->init->flags & CLK_RUNTIME_PM))
+               core->dev = dev;

I guess you need this to play safe, although I am really wondering if we should try without.

Quoted 3 lines

Not that many clocks are currently being registered with a valid struct device pointer. For the other cases why not try to use runtime PM as per default?

Quoted 5 lines

I've that tried initially, but it causes failure for all the clock controllers, which don't enable runtime pm. One of such case is max77686 PMIC, which provides 3 clocks. Maybe a negative flag (CLK_NO_RUNTIME_PM) will be a better solution, so by default the runtime pm calls will be enabled for every driver providing struct device?

I assume that's because the runtime PM errors in clk_pm_runtime_get() and friends, are being propagated to the callers? Especially because the runtime PM core returns error codes, in cases when runtime PM hasn't been enabled for the device.

Quoted 8 lines
Quoted 4 lines

Moreover we anyway rely on the clock provider to enable runtime PM for the clock device, and when that isn't the case the runtime PM deployment in the core should still be safe, right!?

I don't get the above comment. Do you want to check if runtime pm has been enabled during clock registration?

Yes, something like that. Apologize, I was clearly being too vague.

My point is, we don't need to invent a specific clock provider flag for this. Instead the clock core could just at clock registration check if runtime PM is enabled for the device (pm_runtime_enabled()),. and from that assign an internal clock core flag to keep track of whether runtime PM should be managed or not.

Quoted 21 lines
Quoted 14 lines
Quoted 14 lines
drivers/clk/clk.c +0 -0
        if (dev && dev->driver)
                 core->owner = dev->driver->owner;
         core->hw = hw;
include/linux/clk-provider.h +1 -0
index a39c0c530778..8a131eb71fdf 100644
@@ -35,6 +35,7 @@
  #define CLK_IS_CRITICAL                BIT(11) /* do not gate, ever */
  /* parents need enable during gate/ungate, set rate and re-parent */
  #define CLK_OPS_PARENT_ENABLE  BIT(12)
+#define CLK_RUNTIME_PM         BIT(13)
 
Quoted 6 lines
Quoted 5 lines
include/linux/clk-provider.h +0 -1
  struct clk;
  struct clk_hw;
--

1.9.1

Quoted 5 lines

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

Kind regards
Uffe

#6 lore raw

PATCH v2 2/5 clock: samsung: add support for runtime pm

In reply to Marek Szyprowski

This patch adds struct device pointer to samsung_clk_provider and forwarding it to clk_register_* functions, so drivers can register clocks, which use runtime pm feature. It also adds CLK_RUNTIME_PM flag to all clocks for which such device have been provided.

---
 drivers/clk/samsung/clk-pll.c |  4 ++--
 drivers/clk/samsung/clk.c     | 36 ++++++++++++++++++++++++------------
 drivers/clk/samsung/clk.h     |  1 +
 3 files changed, 27 insertions(+), 14 deletions(-)
drivers/clk/samsung/clk-pll.c +2 -2
index 48139bd510f1..fa929ddf3551 100644
@@ -1174,7 +1174,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
 	}
 
 	init.name = pll_clk->name;
-	init.flags = pll_clk->flags;
+	init.flags = pll_clk->flags | (ctx->dev ? CLK_RUNTIME_PM : 0);
 	init.parent_names = &pll_clk->parent_name;
 	init.num_parents = 1;
 
@@ -1285,7 +1285,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
 	pll->lock_reg = base + pll_clk->lock_offset;
 	pll->con_reg = base + pll_clk->con_offset;
 
-	clk = clk_register(NULL, &pll->hw);
+	clk = clk_register(ctx->dev, &pll->hw);
 	if (IS_ERR(clk)) {
 		pr_err("%s: failed to register pll clock %s : %ld\n",
 			__func__, pll_clk->name, PTR_ERR(clk));
drivers/clk/samsung/clk.c +24 -12
index b7d87d6db9dc..762019893383 100644
@@ -143,8 +143,11 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_rate(NULL, list->name,
-			list->parent_name, list->flags, list->fixed_rate);
+		unsigned int pm_flags = ctx->dev ? CLK_RUNTIME_PM : 0;
+
+		clk = clk_register_fixed_rate(ctx->dev, list->name,
+			list->parent_name, list->flags | pm_flags,
+			list->fixed_rate);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
@@ -172,8 +175,11 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
 	unsigned int idx;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_fixed_factor(NULL, list->name,
-			list->parent_name, list->flags, list->mult, list->div);
+		unsigned int pm_flags = ctx->dev ? CLK_RUNTIME_PM : 0;
+
+		clk = clk_register_fixed_factor(ctx->dev, list->name,
+			list->parent_name, list->flags | pm_flags, list->mult,
+			list->div);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
 				list->name);
@@ -193,8 +199,10 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_mux(NULL, list->name, list->parent_names,
-			list->num_parents, list->flags,
+		unsigned int pm_flags = ctx->dev ? CLK_RUNTIME_PM : 0;
+
+		clk = clk_register_mux(ctx->dev, list->name, list->parent_names,
+			list->num_parents, list->flags | pm_flags,
 			ctx->reg_base + list->offset,
 			list->shift, list->width, list->mux_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
@@ -225,15 +233,17 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
+		unsigned int pm_flags = ctx->dev ? CLK_RUNTIME_PM : 0;
+
 		if (list->table)
-			clk = clk_register_divider_table(NULL, list->name,
-				list->parent_name, list->flags,
+			clk = clk_register_divider_table(ctx->dev, list->name,
+				list->parent_name, list->flags | pm_flags,
 				ctx->reg_base + list->offset,
 				list->shift, list->width, list->div_flags,
 				list->table, &ctx->lock);
 		else
-			clk = clk_register_divider(NULL, list->name,
-				list->parent_name, list->flags,
+			clk = clk_register_divider(ctx->dev, list->name,
+				list->parent_name, list->flags | pm_flags,
 				ctx->reg_base + list->offset, list->shift,
 				list->width, list->div_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
@@ -264,8 +274,10 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
 	unsigned int idx, ret;
 
 	for (idx = 0; idx < nr_clk; idx++, list++) {
-		clk = clk_register_gate(NULL, list->name, list->parent_name,
-				list->flags, ctx->reg_base + list->offset,
+		unsigned int pm_flags = ctx->dev ? CLK_RUNTIME_PM : 0;
+
+		clk = clk_register_gate(ctx->dev, list->name, list->parent_name,
+				list->flags | pm_flags, ctx->reg_base + list->offset,
 				list->bit_idx, list->gate_flags, &ctx->lock);
 		if (IS_ERR(clk)) {
 			pr_err("%s: failed to register clock %s\n", __func__,
drivers/clk/samsung/clk.h +1 -0
index da3bdebabf1e..9263d8a27c6b 100644
@@ -26,6 +26,7 @@ struct clk;
  */
 struct samsung_clk_provider {
 	void __iomem *reg_base;
+	struct device *dev;
 	struct clk_onecell_data clk_data;
 	spinlock_t lock;
 };
Signature · 1 line
1.9.1
#7 lore raw

PATCH v2 3/5 clocks: exynos4x12: add runtime pm support for ISP clocks

In reply to Marek Szyprowski

Exynos4412 clock controller contains some additional clocks for FIMC-ISP (Camera ISP) subsystem. Registers for those clocks are partially located in the SOC area, which belongs to ISP power domain.

This patch implements integration of ISP clocks with ISP power domain by using runtime pm feature of clocks core. This finally solves all the mysterious freezes in accessing ISP clocks when ISP power domain is disabled.

---
 .../devicetree/bindings/clock/exynos4-clock.txt    |  22 ++
 drivers/clk/samsung/clk-exynos4.c                  | 227 +++++++++++++++------
 2 files changed, 188 insertions(+), 61 deletions(-)
Documentation/devicetree/bindings/clock/exynos4-clock.txt +22 -0
index f5a5b19ed3b2..429dd7e420e4 100644
@@ -41,3 +41,25 @@ Example 2: UART controller node that consumes the clock generated by the clock
 		clocks = <&clock CLK_UART2>, <&clock CLK_SCLK_UART2>;
 		clock-names = "uart", "clk_uart_baud0";
 	};
+
+Exynos4412 clock controller contains some additional clocks for FIMC-ISP
+(Camera ISP) subsystem. Registers for those clocks are partially located
+in the SOC area, which belongs to ISP power domain. To properly handle
+additional ISP clocks and their integration with power domains, an
+additional subnode "isp-clock-controller" with "samsung,exynos4412-isp-clock"
+compatible has to be defined under the main Exynos4 clock node. This subnode
+can be then used for linking with respective ISP power domain (for more
+information, see Samsung Exynos power domains bindings).
+
+Example 3: An example of a clock controller for Exynos4412 with ISP clocks.
+
+	clock: clock-controller at 10030000 {
+		compatible = "samsung,exynos4412-clock";
+		reg = <0x10030000 0x20000>;
+		#clock-cells = <1>;
+
+		isp-clock-controller {
+			compatible = "samsung,exynos4412-isp-clock";
+			power-domains = <&pd_isp>;
+		};
+	};
drivers/clk/samsung/clk-exynos4.c +166 -61

293 lines not on this page · raw message

Signature · 1 line
1.9.1
#8 lore raw

PATCH v2 4/5 ARM: dts: exynos: add support for ISP power domain to exynos4x12 clocks device

In reply to Marek Szyprowski

Exynos4412 clock controller contains some additional clocks for FIMC-ISP (Camera ISP) subsystem. Registers for those clocks are partially located in the SOC area, which belongs to ISP power domain.

This patch implements integration of ISP clocks with ISP power domain.

---
 arch/arm/boot/dts/exynos4x12.dtsi | 5 +++++
 1 file changed, 5 insertions(+)
arch/arm/boot/dts/exynos4x12.dtsi +5 -0
index 3394bdcf10ae..4daea67546b9 100644
@@ -74,6 +74,11 @@
 		compatible = "samsung,exynos4412-clock";
 		reg = <0x10030000 0x20000>;
 		#clock-cells = <1>;
+
+		isp-clock-controller {
+			compatible = "samsung,exynos4412-isp-clock";
+			power-domains = <&pd_isp>;
+		};
 	};
 
 	mct at 10050000 {
Signature · 1 line
1.9.1
#9 lore raw

PATCH v2 5/5 clocks: exynos5433: add runtime pm support

In reply to Marek Szyprowski

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

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

---
 drivers/clk/samsung/clk-exynos5433.c | 385 ++++++++++++++++++++++++++++-------
 drivers/clk/samsung/clk.h            |   6 +
 2 files changed, 314 insertions(+), 77 deletions(-)
drivers/clk/samsung/clk-exynos5433.c +308 -77
index ea1608682d7f..d507f2ace6c2 100644
@@ -9,9 +9,14 @@
  * Common Clock Framework support for Exynos5443 SoC.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
 
 #include <dt-bindings/clock/exynos5433.h>
 
@@ -2333,6 +2338,10 @@ static const unsigned long g2d_clk_regs[] __initconst = {
 	DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
 };
 
+static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
+	{ MUX_SEL_G2D0, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g2d_266_user_p)		= { "oscclk", "aclk_g2d_266", };
 PNAME(mout_aclk_g2d_400_user_p)		= { "oscclk", "aclk_g2d_400", };
@@ -2418,16 +2427,11 @@ static const struct samsung_cmu_info g2d_cmu_info __initconst = {
 	.nr_clk_ids		= G2D_NR_CLK,
 	.clk_regs		= g2d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g2d_clk_regs),
+	.suspend_regs		= g2d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g2d_suspend_regs),
+	.clk_name		= "aclk_g2d_400",
 };
 
-static void __init exynos5433_cmu_g2d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g2d_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
-		exynos5433_cmu_g2d_init);
-
 /*
  * Register offset definitions for CMU_DISP
  */
@@ -2492,6 +2496,14 @@ static const unsigned long disp_clk_regs[] __initconst = {
 	CLKOUT_CMU_DISP_DIV_STAT,
 };
 
+static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
+	{ MUX_SEL_DISP0, 0 },
+	{ MUX_SEL_DISP1, 0 },
+	{ MUX_SEL_DISP2, 0 },
+	{ MUX_SEL_DISP3, 0 },
+	{ MUX_SEL_DISP4, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_disp_pll_p)			= { "oscclk", "fout_disp_pll", };
 PNAME(mout_sclk_dsim1_user_p)		= { "oscclk", "sclk_dsim1_disp", };
@@ -2837,16 +2849,11 @@ static const struct samsung_cmu_info disp_cmu_info __initconst = {
 	.nr_clk_ids		= DISP_NR_CLK,
 	.clk_regs		= disp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(disp_clk_regs),
+	.suspend_regs		= disp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(disp_suspend_regs),
+	.clk_name		= "aclk_disp_333",
 };
 
-static void __init exynos5433_cmu_disp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &disp_cmu_info);
-}
-
-CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
-		exynos5433_cmu_disp_init);
-
 /*
  * Register offset definitions for CMU_AUD
  */
@@ -2881,6 +2888,11 @@ static const unsigned long aud_clk_regs[] __initconst = {
 	ENABLE_IP_AUD1,
 };
 
+static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
+	{ MUX_SEL_AUD0, 0 },
+	{ MUX_SEL_AUD1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aud_pll_user_aud_p)	= { "oscclk", "fout_aud_pll", };
 PNAME(mout_sclk_aud_pcm_p)	= { "mout_aud_pll_user", "ioclk_audiocdclk0",};
@@ -3007,16 +3019,10 @@ static const struct samsung_cmu_info aud_cmu_info __initconst = {
 	.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),
 };
 
-static void __init exynos5433_cmu_aud_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &aud_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
-		exynos5433_cmu_aud_init);
-
-
 /*
  * Register offset definitions for CMU_BUS{0|1|2}
  */
@@ -3218,6 +3224,10 @@ static const unsigned long g3d_clk_regs[] __initconst = {
 	CLK_STOPCTRL,
 };
 
+static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
+	{ MUX_SEL_G3D, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_aclk_g3d_400_p)	= { "mout_g3d_pll", "aclk_g3d_400", };
 PNAME(mout_g3d_pll_p)		= { "oscclk", "fout_g3d_pll", };
@@ -3291,15 +3301,11 @@ static const struct samsung_cmu_info g3d_cmu_info __initconst = {
 	.nr_clk_ids		= G3D_NR_CLK,
 	.clk_regs		= g3d_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(g3d_clk_regs),
+	.suspend_regs		= g3d_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(g3d_suspend_regs),
+	.clk_name		= "aclk_g3d_400",
 };
 
-static void __init exynos5433_cmu_g3d_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &g3d_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
-		exynos5433_cmu_g3d_init);
-
 /*
  * Register offset definitions for CMU_GSCL
  */
@@ -3338,6 +3344,12 @@ static const unsigned long gscl_clk_regs[] __initconst = {
 	ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
 };
 
+static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
+	{ MUX_SEL_GSCL, 0 },
+	{ ENABLE_ACLK_GSCL, 0xfff },
+	{ ENABLE_PCLK_GSCL, 0xff },
+};
+
 /* list of all parent clock list */
 PNAME(aclk_gscl_111_user_p)	= { "oscclk", "aclk_gscl_111", };
 PNAME(aclk_gscl_333_user_p)	= { "oscclk", "aclk_gscl_333", };
@@ -3432,15 +3444,11 @@ static const struct samsung_cmu_info gscl_cmu_info __initconst = {
 	.nr_clk_ids		= GSCL_NR_CLK,
 	.clk_regs		= gscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(gscl_clk_regs),
+	.suspend_regs		= gscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(gscl_suspend_regs),
+	.clk_name		= "aclk_gscl_111",
 };
 
-static void __init exynos5433_cmu_gscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &gscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
-		exynos5433_cmu_gscl_init);
-
 /*
  * Register offset definitions for CMU_APOLLO
  */
@@ -3966,6 +3974,11 @@ static const unsigned long mscl_clk_regs[] __initconst = {
 	ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
 };
 
+static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
+	{ MUX_SEL_MSCL0, 0 },
+	{ MUX_SEL_MSCL1, 0 },
+};
+
 /* list of all parent clock list */
 PNAME(mout_sclk_jpeg_user_p)		= { "oscclk", "sclk_jpeg_mscl", };
 PNAME(mout_aclk_mscl_400_user_p)	= { "oscclk", "aclk_mscl_400", };
@@ -4078,15 +4091,11 @@ static const struct samsung_cmu_info mscl_cmu_info __initconst = {
 	.nr_clk_ids		= MSCL_NR_CLK,
 	.clk_regs		= mscl_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mscl_clk_regs),
+	.suspend_regs		= mscl_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mscl_suspend_regs),
+	.clk_name		= "aclk_mscl_400",
 };
 
-static void __init exynos5433_cmu_mscl_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mscl_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
-		exynos5433_cmu_mscl_init);
-
 /*
  * Register offset definitions for CMU_MFC
  */
@@ -4116,6 +4125,10 @@ static const unsigned long mfc_clk_regs[] __initconst = {
 	ENABLE_IP_MFC_SECURE_SMMU_MFC,
 };
 
+static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
+	{ MUX_SEL_MFC, 0 },
+};
+
 PNAME(mout_aclk_mfc_400_user_p)		= { "oscclk", "aclk_mfc_400", };
 
 static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
@@ -4186,15 +4199,11 @@ static const struct samsung_cmu_info mfc_cmu_info __initconst = {
 	.nr_clk_ids		= MFC_NR_CLK,
 	.clk_regs		= mfc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(mfc_clk_regs),
+	.suspend_regs		= mfc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(mfc_suspend_regs),
+	.clk_name		= "aclk_mfc_400",
 };
 
-static void __init exynos5433_cmu_mfc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &mfc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
-		exynos5433_cmu_mfc_init);
-
 /*
  * Register offset definitions for CMU_HEVC
  */
@@ -4224,6 +4233,10 @@ static const unsigned long hevc_clk_regs[] __initconst = {
 	ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
 };
 
+static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
+	{ MUX_SEL_HEVC, 0 },
+};
+
 PNAME(mout_aclk_hevc_400_user_p)	= { "oscclk", "aclk_hevc_400", };
 
 static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
@@ -4296,15 +4309,11 @@ static const struct samsung_cmu_info hevc_cmu_info __initconst = {
 	.nr_clk_ids		= HEVC_NR_CLK,
 	.clk_regs		= hevc_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(hevc_clk_regs),
+	.suspend_regs		= hevc_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(hevc_suspend_regs),
+	.clk_name		= "aclk_hevc_400",
 };
 
-static void __init exynos5433_cmu_hevc_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &hevc_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
-		exynos5433_cmu_hevc_init);
-
 /*
  * Register offset definitions for CMU_ISP
  */
@@ -4338,6 +4347,10 @@ static const unsigned long isp_clk_regs[] __initconst = {
 	ENABLE_IP_ISP3,
 };
 
+static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
+	{ MUX_SEL_ISP, 0 },
+};
+
 PNAME(mout_aclk_isp_dis_400_user_p)	= { "oscclk", "aclk_isp_dis_400", };
 PNAME(mout_aclk_isp_400_user_p)		= { "oscclk", "aclk_isp_400", };
 
@@ -4549,15 +4562,11 @@ static const struct samsung_cmu_info isp_cmu_info __initconst = {
 	.nr_clk_ids		= ISP_NR_CLK,
 	.clk_regs		= isp_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(isp_clk_regs),
+	.suspend_regs		= isp_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(isp_suspend_regs),
+	.clk_name		= "aclk_isp_400",
 };
 
-static void __init exynos5433_cmu_isp_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &isp_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
-		exynos5433_cmu_isp_init);
-
 /*
  * Register offset definitions for CMU_CAM0
  */
@@ -4621,6 +4630,15 @@ static const unsigned long cam0_clk_regs[] __initconst = {
 	ENABLE_IP_CAM02,
 	ENABLE_IP_CAM03,
 };
+
+static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
+	{ MUX_SEL_CAM00, 0 },
+	{ MUX_SEL_CAM01, 0 },
+	{ MUX_SEL_CAM02, 0 },
+	{ MUX_SEL_CAM03, 0 },
+	{ MUX_SEL_CAM04, 0 },
+};
+
 PNAME(mout_aclk_cam0_333_user_p)	= { "oscclk", "aclk_cam0_333", };
 PNAME(mout_aclk_cam0_400_user_p)	= { "oscclk", "aclk_cam0_400", };
 PNAME(mout_aclk_cam0_552_user_p)	= { "oscclk", "aclk_cam0_552", };
@@ -5026,15 +5044,11 @@ static const struct samsung_cmu_info cam0_cmu_info __initconst = {
 	.nr_clk_ids		= CAM0_NR_CLK,
 	.clk_regs		= cam0_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam0_clk_regs),
+	.suspend_regs		= cam0_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam0_suspend_regs),
+	.clk_name		= "aclk_cam0_400",
 };
 
-static void __init exynos5433_cmu_cam0_init(struct device_node *np)
-{
-	samsung_cmu_register_one(np, &cam0_cmu_info);
-}
-CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
-		exynos5433_cmu_cam0_init);
-
 /*
  * Register offset definitions for CMU_CAM1
  */
@@ -5081,6 +5095,12 @@ static const unsigned long cam1_clk_regs[] __initconst = {
 	ENABLE_IP_CAM12,
 };
 
+static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
+	{ MUX_SEL_CAM10, 0 },
+	{ MUX_SEL_CAM11, 0 },
+	{ MUX_SEL_CAM12, 0 },
+};
+
 PNAME(mout_sclk_isp_uart_user_p)	= { "oscclk", "sclk_isp_uart_cam1", };
 PNAME(mout_sclk_isp_spi1_user_p)	= { "oscclk", "sclk_isp_spi1_cam1", };
 PNAME(mout_sclk_isp_spi0_user_p)	= { "oscclk", "sclk_isp_spi0_cam1", };
@@ -5399,11 +5419,222 @@ static const struct samsung_cmu_info cam1_cmu_info __initconst = {
 	.nr_clk_ids		= CAM1_NR_CLK,
 	.clk_regs		= cam1_clk_regs,
 	.nr_clk_regs		= ARRAY_SIZE(cam1_clk_regs),
+	.suspend_regs		= cam1_suspend_regs,
+	.nr_suspend_regs	= ARRAY_SIZE(cam1_suspend_regs),
+	.clk_name		= "aclk_cam1_400",
+};
+
+
+struct exynos5433_cmu_data {
+	struct samsung_clk_provider ctx;
+
+	struct samsung_clk_reg_dump *clk_save;
+	unsigned int nr_clk_save;
+	const struct samsung_clk_reg_dump *clk_suspend;
+	unsigned int nr_clk_suspend;
+
+	struct clk *clk;
+	struct clk **pclks;
+	int nr_pclks;
+};
+
+static int exynos5433_cmu_suspend(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	samsung_clk_save(data->ctx.reg_base, data->clk_save,
+			 data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_suspend,
+			    data->nr_clk_suspend);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable(data->pclks[i]);
+
+	clk_disable(data->clk);
+
+	return 0;
+}
+
+static int exynos5433_cmu_resume(struct device *dev)
+{
+	struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
+	int i;
+
+	clk_enable(data->clk);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_enable(data->pclks[i]);
+
+	samsung_clk_restore(data->ctx.reg_base, data->clk_save,
+			    data->nr_clk_save);
+
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_disable(data->pclks[i]);
+
+	return 0;
+}
+
+static int __init exynos5433_cmu_probe(struct platform_device *pdev)
+{
+	const struct samsung_cmu_info *info;
+	struct exynos5433_cmu_data *data;
+	struct samsung_clk_provider *ctx;
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	void __iomem *reg_base;
+	struct clk **clk_table;
+	int i;
+
+	info = of_device_get_match_data(dev);
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	ctx = &data->ctx;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	reg_base = devm_ioremap_resource(dev, res);
+	if (!reg_base) {
+		dev_err(dev, "failed to map registers\n");
+		return -ENOMEM;
+	}
+
+	clk_table = devm_kcalloc(dev, info->nr_clk_ids, sizeof(struct clk *),
+				 GFP_KERNEL);
+	if (!clk_table)
+		return -ENOMEM;
+
+	for (i = 0; i < info->nr_clk_ids; ++i)
+		clk_table[i] = ERR_PTR(-ENOENT);
+
+	ctx->clk_data.clks = clk_table;
+	ctx->clk_data.clk_num = info->nr_clk_ids;
+	ctx->reg_base = reg_base;
+	ctx->dev = dev;
+	spin_lock_init(&ctx->lock);
+
+	data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
+						    info->nr_clk_regs);
+	data->nr_clk_save = info->nr_clk_regs;
+	data->clk_suspend = info->suspend_regs;
+	data->nr_clk_suspend = info->nr_suspend_regs;
+	data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
+						    "#clock-cells");
+	if (data->nr_pclks > 0) {
+		data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
+					   data->nr_pclks, GFP_KERNEL);
+
+		for (i = 0; i < data->nr_pclks; i++) {
+			struct clk *clk = of_clk_get(dev->of_node, i);
+
+			if (IS_ERR(clk))
+				return PTR_ERR(clk);
+			data->pclks[i] = clk;
+		}
+	}
+
+	/*
+	 * Prepare all parent clocks here to avoid potential deadlock caused
+	 * by global clock "prepare lock" grabbed by runtime pm callbacks
+	 * from pm workers.
+	 */
+	for (i = 0; i < data->nr_pclks; i++)
+		clk_prepare(data->pclks[i]);
+
+	if (info->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);
+
+	if (info->clk_name)
+		data->clk = clk_get(dev, info->clk_name);
+	clk_prepare_enable(data->clk);
+
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	platform_set_drvdata(pdev, data);
+	samsung_clk_of_add_provider(dev->of_node, ctx);
+
+	return 0;
+}
+
+static const struct of_device_id exynos5433_cmu_of_match[] = {
+	{
+		.compatible = "samsung,exynos5433-cmu-aud",
+		.data = &aud_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam0",
+		.data = &cam0_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-cam1",
+		.data = &cam1_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-disp",
+		.data = &disp_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g2d",
+		.data = &g2d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-g3d",
+		.data = &g3d_cmu_info,
+	}, {
+		.compatible = "samsung,exynos5433-cmu-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);
drivers/clk/samsung/clk.h +6 -0
index 9263d8a27c6b..664020cb4794 100644
@@ -354,6 +354,12 @@ struct samsung_cmu_info {
 	/* list and number of clocks registers */
 	const unsigned long *clk_regs;
 	unsigned int nr_clk_regs;
+
+	/* list and number of clocks registers to set before suspend */
+	const struct samsung_clk_reg_dump *suspend_regs;
+	unsigned int nr_suspend_regs;
+	/* name of the parent clock needed for CMU register access */
+	const char *clk_name;
 };
 
 extern struct samsung_clk_provider *__init samsung_clk_init(
Signature · 1 line
1.9.1
#10 lore raw

v2 cover Add runtime PM support for clocks (on Exynos SoC example)

In reply to Marek Szyprowski

Hello Marek,

I'm using the patches from the v4.8-clocks-pm-v2 branch plus the ones from the v4.8-clocks-pm-v2 branch on top of 4.8.0.

I see some warnings on boot coming from driver core. It appears that the warnings are actually meaningful, since IOMMUs stop working completly. E.g. if I modprobe s5p-mfc later, firmware loading fails because apparantly the IOMMU domain isn't online.

Quoted 1 line
WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c

I added some debug printk() to device_links_driver_bound(), to show the link status. Apparantly it is always DEVICE_LINK_AVAILABLE.

Here's the (semi) full log:

Quoted 330 lines
[    0.000000] Booting Linux on physical CPU 0xa00
[    0.000000] Linux version 4.8.0-vanilla+ (liquid at chidori) (gcc version 4.9.3 (Gentoo 4.9.3 p1.5, pie-0.6.4) ) #3 SMP PREEMPT Thu Oct 6 19:14:15 CEST 2016
[    0.000000] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt:Machine model: Hardkernel ODROID-X2 board based on Exynos4412
[    0.000000] Reserved memory: created DMA memory pool at 0xbf700000, size 8 MiB
[    0.000000] OF: reserved mem: initialized node region_mfc_right, compatible id shared-dma-pool
[    0.000000] Reserved memory: created DMA memory pool at 0xbe700000, size 16 MiB
[    0.000000] OF: reserved mem: initialized node region_mfc_left, compatible id shared-dma-pool
[    0.000000] cma: Reserved 128 MiB at 0xb6400000
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] Samsung CPU ID: 0xe4412220
[    0.000000] Running under secure firmware.
[    0.000000] percpu: Embedded 14 pages/cpu @eefb3000 s26176 r8192 d22976 u57344
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 516352
[    0.000000] Kernel command line: video=HDMI-A-1:1280x720M at 60 console=ttySAC1,115200n8 root=PARTUUID=8c900d97-367f-47a9-bd66-6eced1a29836 rootfstype=f2fs rootwait ro earlyprintk console=tty1 console=ttySAC1,115200
[    0.000000] PID hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
[    0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Memory: 1913244K/2071552K available (6144K kernel code, 223K rwdata, 1432K rodata, 1024K init, 246K bss, 27236K reserved, 131072K cma-reserved, 1154048K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xf0800000 - 0xff800000   ( 240 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xf0000000   ( 768 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0700000   (7136 kB)
[    0.000000]       .init : 0xc0900000 - 0xc0a00000   (1024 kB)
[    0.000000]       .data : 0xc0a00000 - 0xc0a37fc0   ( 224 kB)
[    0.000000]        .bss : 0xc0a39000 - 0xc0a76a08   ( 247 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000]  Build-time adjustment of leaf fanout to 32.
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] L2C: platform modifies aux control register: 0x02070000 -> 0x3e470001
[    0.000000] L2C: platform provided aux values permit register corruption.
[    0.000000] L2C: DT/platform modifies aux control register: 0x02070000 -> 0x3e470001
[    0.000000] L2C-310 enabling early BRESP for Cortex-A9
[    0.000000] L2C-310: enabling full line of zeros but not enabled in Cortex-A9
[    0.000000] L2C-310 ID prefetch enabled, offset 8 lines
[    0.000000] L2C-310 dynamic clock gating enabled, standby mode enabled
[    0.000000] L2C-310 cache controller enabled, 16 ways, 1024 kB
[    0.000000] L2C-310: CACHE_ID 0x4100c4c8, AUX_CTRL 0x7e470001
[    0.000000] Exynos4x12 clocks: sclk_apll = 1000000000, sclk_mpll = 800000000
[    0.000000]  sclk_epll = 96000000, sclk_vpll = 350000000, arm_clk = 1000000000
[    0.000000] Switching to timer-based delay loop, resolution 41ns
[    0.000000] clocksource: mct-frc: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[    0.000004] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 89478484971ns
[    0.000263] Console: colour dummy device 80x30
[    0.000978] console [tty1] enabled
[    0.001003] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=120000)
[    0.001039] pid_max: default: 32768 minimum: 301
[    0.001118] Mount-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.001142] Mountpoint-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.001577] CPU: Testing write buffer coherency: ok
[    0.001875] CPU0: thread -1, cpu 0, socket 10, mpidr 80000a00
[    0.002240] Setting up static identity map for 0x40100000 - 0x40100058
[    0.055360] CPU1: thread -1, cpu 1, socket 10, mpidr 80000a01
[    0.075335] CPU2: thread -1, cpu 2, socket 10, mpidr 80000a02
[    0.095316] CPU3: thread -1, cpu 3, socket 10, mpidr 80000a03
[    0.095374] Brought up 4 CPUs
[    0.095445] SMP: Total of 4 processors activated (192.00 BogoMIPS).
[    0.095464] CPU: All CPU(s) started in SVC mode.
[    0.096280] devtmpfs: initialized
[    0.110373] VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4
[    0.110612] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 9556302231375000 ns
[    0.114190] pinctrl core: initialized pinctrl subsystem
[    0.114738] lcd0-power-domain at 10023C80 has as child subdomain: tv-power-domain at 10023C20.
[    0.115573] NET: Registered protocol family 16
[    0.117150] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.134991] cpuidle: using governor ladder
[    0.154989] cpuidle: using governor menu
[    0.155398] hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers.
[    0.155434] hw-breakpoint: maximum watchpoint size is 4 bytes.
[    0.197368] SCSI subsystem initialized
[    0.197498] usbcore: registered new interface driver usbfs
[    0.197568] usbcore: registered new interface driver hub
[    0.197664] usbcore: registered new device driver usb
[    0.198081] s3c-i2c 13860000.i2c: slave address 0x10
[    0.198111] s3c-i2c 13860000.i2c: bus frequency set to 390 KHz
[    0.198570] s3c-i2c 13860000.i2c: i2c-0: S3C I2C adapter
[    0.198763] s3c-i2c 13870000.i2c: slave address 0x10
[    0.198791] s3c-i2c 13870000.i2c: bus frequency set to 390 KHz
[    0.199046] s3c-i2c 13870000.i2c: i2c-1: S3C I2C adapter
[    0.199192] s3c-i2c 13880000.i2c: slave address 0x00
[    0.199218] s3c-i2c 13880000.i2c: bus frequency set to 97 KHz
[    0.199375] s3c-i2c 13880000.i2c: i2c-2: S3C I2C adapter
[    0.199481] s3c-i2c 138e0000.i2c: slave address 0x00
[    0.199506] s3c-i2c 138e0000.i2c: bus frequency set to 97 KHz
[    0.199758] s3c-i2c 138e0000.i2c: i2c-8: S3C I2C adapter
[    0.200052] Linux video capture interface: v2.00
[    0.205439] Advanced Linux Sound Architecture Driver Initialized.
[    0.205901] Bluetooth: Core ver 2.21
[    0.205947] NET: Registered protocol family 31
[    0.205966] Bluetooth: HCI device and connection manager initialized
[    0.205992] Bluetooth: HCI socket layer initialized
[    0.206014] Bluetooth: L2CAP socket layer initialized
[    0.206036] Bluetooth: SCO socket layer initialized
[    0.206567] clocksource: Switched to clocksource mct-frc
[    0.214799] NET: Registered protocol family 2
[    0.215264] TCP established hash table entries: 8192 (order: 3, 32768 bytes)
[    0.215352] TCP bind hash table entries: 8192 (order: 4, 65536 bytes)
[    0.215463] TCP: Hash tables configured (established 8192 bind 8192)
[    0.215531] UDP hash table entries: 512 (order: 2, 16384 bytes)
[    0.215573] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes)
[    0.215717] NET: Registered protocol family 1
[    0.216073] hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7 counters available
[    0.217474] futex hash table entries: 1024 (order: 4, 65536 bytes)
[    0.217587] audit: initializing netlink subsys (disabled)
[    0.217641] audit: type=2000 audit(0.215:1): initialized
[    0.218191] workingset: timestamp_bits=30 max_order=19 bucket_order=0
[    0.230957] bounce: pool size: 64 pages
[    0.231124] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251)
[    0.231157] io scheduler noop registered
[    0.231175] io scheduler deadline registered
[    0.231306] io scheduler cfq registered (default)
[    0.231963] 125b0000.exynos-usbphy supply vbus not found, using dummy regulator
[    0.235681] dma-pl330 12680000.pdma: Loaded driver for PL330 DMAC-141330
[    0.235717] dma-pl330 12680000.pdma:         DBUFF-32x4bytes Num_Chans-8 Num_Peri-32 Num_Events-32
[    0.238256] dma-pl330 12690000.pdma: Loaded driver for PL330 DMAC-141330
[    0.238291] dma-pl330 12690000.pdma:         DBUFF-32x4bytes Num_Chans-8 Num_Peri-32 Num_Events-32
[    0.239110] dma-pl330 12850000.mdma: Loaded driver for PL330 DMAC-141330
[    0.239141] dma-pl330 12850000.mdma:         DBUFF-64x8bytes Num_Chans-8 Num_Peri-1 Num_Events-32
[    0.239800] 13800000.serial: ttySAC0 at MMIO 0x13800000 (irq = 53, base_baud = 0) is a S3C6400/10
[    0.240144] 13810000.serial: ttySAC1 at MMIO 0x13810000 (irq = 54, base_baud = 0) is a S3C6400/10
[    1.015731] console [ttySAC1] enabled
[    1.019770] 13820000.serial: ttySAC2 at MMIO 0x13820000 (irq = 55, base_baud = 0) is a S3C6400/10
[    1.028570] 13830000.serial: ttySAC3 at MMIO 0x13830000 (irq = 56, base_baud = 0) is a S3C6400/10
[    1.038157] [drm] Initialized drm 1.1.0 20060810
[    1.042093] exynos-mixer 12c10000.mixer: Linked as a consumer to 12e20000.sysmmu
[    1.049118] iommu: Adding device 12c10000.mixer to group 0
[    1.055403] DEBUG: device_links_driver_bound(): 1
[    1.059238] ------------[ cut here ]------------
[    1.063826] WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    1.072851] Modules linked in:
[    1.075859] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.8.0-vanilla+ #3
[    1.082458] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    1.088533] Backtrace: 
[    1.090970] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    1.098524]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    1.104161] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    1.111376] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    1.118306]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    1.123946] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    1.131506]  r9:00000000 r8:c0a258d8 r7:ee3114e4 r6:eeafa874 r5:00000000 r4:ee3114c0
[    1.139231] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    1.148615] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    1.157287]  r9:00000000 r8:c0a258d8 r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eeafa810
[    1.165011] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    1.173345]  r4:eeafa810 r3:600000d3
[    1.176894] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    1.185323]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eeafa844 r5:c0a258d8
[    1.193128]  r4:eeafa810 r3:00000000
[    1.196685] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    1.204853]  r6:c0407eb0 r5:c0a258d8 r4:00000000 r3:c0407eb0
[    1.210485] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    1.218480]  r6:c0a26758 r5:ee27f780 r4:c0a258d8
[    1.223072] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    1.231072] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    1.239139]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a258d8
[    1.244773] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    1.253809]  r5:00000000 r4:00000004
[    1.257364] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    1.266402] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    1.274554]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    1.281232] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    1.289917]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    1.297722]  r4:c093a520
[    1.300243] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    1.308493]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    1.316297]  r4:00000000
[    1.318815] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    1.326373]  r4:00000000 r3:ee880000
[    1.329952] ---[ end trace 402e0b75dfe2947b ]---
[    1.336115] exynos-hdmi 12d00000.hdmi: Failed to get supply 'vdd': -517
[    1.341761] s5p-g2d 10800000.g2d: Linked as a consumer to 10a40000.sysmmu
[    1.347960] iommu: Adding device 10800000.g2d to group 1
[    1.353512] s5p-g2d 10800000.g2d: The Exynos G2D (ver 4.1) successfully probed.
[    1.360510] DEBUG: device_links_driver_bound(): 1
[    1.365183] ------------[ cut here ]------------
[    1.369786] WARNING: CPU: 1 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    1.378819] Modules linked in:
[    1.381829] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G        W       4.8.0-vanilla+ #3
[    1.389651] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    1.395718] Backtrace: 
[    1.398154] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    1.405709]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    1.411344] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    1.418560] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    1.425491]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    1.431132] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    1.438691]  r9:00000000 r8:c0a2621c r7:ee910ae4 r6:eeac4a74 r5:00000000 r4:ee910ac0
[    1.446416] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    1.455800] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    1.464472]  r9:00000000 r8:c0a2621c r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eeac4a10
[    1.472196] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    1.480530]  r4:eeac4a10 r3:00000000
[    1.484080] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    1.492508]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eeac4a44 r5:c0a2621c
[    1.500313]  r4:eeac4a10 r3:00000000
[    1.503870] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    1.512038]  r6:c0407eb0 r5:c0a2621c r4:00000000 r3:c0407eb0
[    1.517671] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    1.525665]  r6:c0a26758 r5:ee932b80 r4:c0a2621c
[    1.530257] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    1.538257] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    1.546324]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a2621c
[    1.551959] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    1.560994]  r5:00000000 r4:0000000a
[    1.564548] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    1.573587] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    1.581739]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    1.588415] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    1.597103]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    1.604907]  r4:c093a520
[    1.607427] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    1.615678]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    1.623482]  r4:00000000
[    1.625999] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    1.633558]  r4:00000000 r3:ee880000
[    1.637133] ---[ end trace 402e0b75dfe2947c ]---
[    1.642227] exynos-drm-fimc 11800000.fimc: Linked as a consumer to 11a20000.sysmmu
[    1.649335] iommu: Adding device 11800000.fimc to group 2
[    1.655529] exynos-drm-fimc 11810000.fimc: Linked as a consumer to 11a30000.sysmmu
[    1.662263] iommu: Adding device 11810000.fimc to group 3
[    1.668447] exynos-drm-fimc 11820000.fimc: Linked as a consumer to 11a40000.sysmmu
[    1.675196] iommu: Adding device 11820000.fimc to group 4
[    1.681278] exynos-drm-fimc 11820000.fimc: drm fimc registered successfully.
[    1.687589] DEBUG: device_links_driver_bound(): 1
[    1.692264] ------------[ cut here ]------------
[    1.696852] WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    1.705880] Modules linked in:
[    1.708891] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W       4.8.0-vanilla+ #3
[    1.716713] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    1.722780] Backtrace: 
[    1.725216] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    1.732771]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    1.738407] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    1.745624] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    1.752553]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    1.758194] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    1.765754]  r9:00000000 r8:c0a26330 r7:ee311d24 r6:eea83074 r5:00000000 r4:ee311d00
[    1.773479] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    1.782862] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    1.791534]  r9:00000000 r8:c0a26330 r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eea83010
[    1.799258] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    1.807592]  r4:eea83010 r3:600000d3
[    1.811142] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    1.819571]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eea83044 r5:c0a26330
[    1.827376]  r4:eea83010 r3:00000000
[    1.830932] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    1.839100]  r6:c0407eb0 r5:c0a26330 r4:00000000 r3:c0407eb0
[    1.844733] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    1.852728]  r6:c0a26758 r5:ee27fa00 r4:c0a26330
[    1.857319] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    1.865320] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    1.873386]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a26330
[    1.879021] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    1.888057]  r5:00000000 r4:0000000b
[    1.891610] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    1.900649] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    1.908801]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    1.915478] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    1.924165]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    1.931969]  r4:c093a520
[    1.934489] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    1.942740]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    1.950545]  r4:00000000
[    1.953062] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    1.960620]  r4:00000000 r3:ee880000
[    1.964193] ---[ end trace 402e0b75dfe2947d ]---
[    1.969039] exynos-drm-fimc 11830000.fimc: Linked as a consumer to 11a50000.sysmmu
[    1.976392] iommu: Adding device 11830000.fimc to group 5
[    1.982437] exynos-drm-fimc 11830000.fimc: drm fimc registered successfully.
[    1.988799] DEBUG: device_links_driver_bound(): 1
[    1.993464] ------------[ cut here ]------------
[    1.998056] WARNING: CPU: 2 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    2.007076] Modules linked in:
[    2.010087] CPU: 2 PID: 1 Comm: swapper/0 Tainted: G        W       4.8.0-vanilla+ #3
[    2.017909] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    2.023975] Backtrace: 
[    2.026412] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    2.033967]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    2.039603] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    2.046819] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    2.053750]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    2.059390] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    2.066950]  r9:00000000 r8:c0a26330 r7:ee313264 r6:eea83274 r5:00000000 r4:ee313240
[    2.074675] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    2.084058] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    2.092731]  r9:00000000 r8:c0a26330 r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eea83210
[    2.100454] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    2.108788]  r4:eea83210 r3:600000d3
[    2.112338] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    2.120767]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eea83244 r5:c0a26330
[    2.128571]  r4:eea83210 r3:00000000
[    2.132128] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    2.140296]  r6:c0407eb0 r5:c0a26330 r4:00000000 r3:c0407eb0
[    2.145929] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    2.153924]  r6:c0a26758 r5:ee27fa00 r4:c0a26330
[    2.158516] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    2.166516] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    2.174582]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a26330
[    2.180217] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    2.189252]  r5:00000000 r4:0000000b
[    2.192806] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    2.201845] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    2.209997]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    2.216674] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    2.225361]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    2.233165]  r4:c093a520
[    2.235685] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    2.243936]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    2.251740]  r4:00000000
[    2.254258] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    2.261816]  r4:00000000 r3:ee880000
[    2.265388] ---[ end trace 402e0b75dfe2947e ]---
[    2.270457] exynos-drm-ipp exynos-drm-ipp: drm ipp registered successfully.
[    2.280368] loop: module loaded
[    2.293185] random: fast init done
[    2.303857] usbcore: registered new interface driver smsc95xx
[    2.491598] dwc2 12480000.hsotg: Specified GNPTXFDEP=1024 > 768
[    2.491906] dwc2 12480000.hsotg: EPs: 16, dedicated fifos, 7808 entries in SPRAM
[    2.499801] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    2.505798] ehci-exynos: EHCI EXYNOS driver
[    2.510438] exynos-ehci 12580000.ehci: EHCI Host Controller
[    2.515535] exynos-ehci 12580000.ehci: new USB bus registered, assigned bus number 1
[    2.523434] exynos-ehci 12580000.ehci: irq 51, io mem 0x12580000
[    2.541596] exynos-ehci 12580000.ehci: USB 2.0 started, EHCI 1.00
Quoted 1 line

<snip>

I'll try to do another test with just the 5 patches from this set applied (without the IOMMU probe deferral).

With best wishes,
Tobias

Quoted 79 lines · Marek Szyprowski

Dear All,

This patchset adds runtime PM support to common clock framework. This is an attempt to implement support for clock controllers, which belongs to a power domain. This approach works surprisingly well on Exynos 4412 and 5433 SoCs, 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 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 exynos 4412 and 5433 clocks drivers change the way the clock provider is initialized. Instead of CLK_OF_DECLARE based initialization, a complete platform device driver infrastructure is being used. This is needed to let driver to use runtime pm feature and integrate with generic power domains. The side-effect of this change is a delay in clock provider registeration during system boot, so early initialized drivers might get EPROBEDEFER error when requesting their clocks. This is an issue for IOMMU drivers, so this patchset will be fully functional once the deferred probe for IOMMU will be merged.

The side-effect of this patchset is the one can finally read /sys/kernel/debug/clk/clk_summary on all Exynos4412 boards without any freeze.

If one wants to test this patchset (on Exynos4412 Trats2 device with FIMC-IS driver), I've provided a branch with all needed patches (fixes for Exynos, FIMC-IS driver and IOMMU deferred probe):
https://git.linaro.org/people/marek.szyprowski/linux-srpol.git v4.8-clocks-pm-v2

Patches are based on vanilla v4.8-rc7 kernel.

Best regards
Marek Szyprowski
Samsung R&D Institute Poland

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

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

Marek Szyprowski (5):
clk: add support for runtime pm
clock: samsung: add support for runtime pm
clocks: exynos4x12: add runtime pm support for ISP clocks
ARM: dts: exynos: add support for ISP power domain to exynos4x12
clocks device
clocks: exynos5433: add runtime pm support

 .../devicetree/bindings/clock/exynos4-clock.txt    |  22 ++
 arch/arm/boot/dts/exynos4x12.dtsi                  |   5 +
 drivers/clk/clk.c                                  | 107 +++++-
 drivers/clk/samsung/clk-exynos4.c                  | 227 ++++++++----
 drivers/clk/samsung/clk-exynos5433.c               | 385 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   4 +-
 drivers/clk/samsung/clk.c                          |  36 +-
 drivers/clk/samsung/clk.h                          |   7 +
 include/linux/clk-provider.h                       |   1 +
 9 files changed, 632 insertions(+), 162 deletions(-)
#11 lore raw

v2 cover Add runtime PM support for clocks (on Exynos SoC example)

For reference, the build was done with this config:
https://github.com/tobiasjakobi/odroid-environment/blob/master/sourcecode/system/vanilla-4.8.conf

- Tobias

Quoted 442 lines · Tobias Jakobi

Hello Marek,

I'm using the patches from the v4.8-clocks-pm-v2 branch plus the ones from the v4.8-clocks-pm-v2 branch on top of 4.8.0.

I see some warnings on boot coming from driver core. It appears that the warnings are actually meaningful, since IOMMUs stop working completly. E.g. if I modprobe s5p-mfc later, firmware loading fails because apparantly the IOMMU domain isn't online.

Quoted 1 line
WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c

I added some debug printk() to device_links_driver_bound(), to show the link status. Apparantly it is always DEVICE_LINK_AVAILABLE.

Here's the (semi) full log:

Quoted 330 lines
[    0.000000] Booting Linux on physical CPU 0xa00
[    0.000000] Linux version 4.8.0-vanilla+ (liquid at chidori) (gcc version 4.9.3 (Gentoo 4.9.3 p1.5, pie-0.6.4) ) #3 SMP PREEMPT Thu Oct 6 19:14:15 CEST 2016
[    0.000000] CPU: ARMv7 Processor [413fc090] revision 0 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt:Machine model: Hardkernel ODROID-X2 board based on Exynos4412
[    0.000000] Reserved memory: created DMA memory pool at 0xbf700000, size 8 MiB
[    0.000000] OF: reserved mem: initialized node region_mfc_right, compatible id shared-dma-pool
[    0.000000] Reserved memory: created DMA memory pool at 0xbe700000, size 16 MiB
[    0.000000] OF: reserved mem: initialized node region_mfc_left, compatible id shared-dma-pool
[    0.000000] cma: Reserved 128 MiB at 0xb6400000
[    0.000000] Memory policy: Data cache writealloc
[    0.000000] Samsung CPU ID: 0xe4412220
[    0.000000] Running under secure firmware.
[    0.000000] percpu: Embedded 14 pages/cpu @eefb3000 s26176 r8192 d22976 u57344
[    0.000000] Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 516352
[    0.000000] Kernel command line: video=HDMI-A-1:1280x720M at 60 console=ttySAC1,115200n8 root=PARTUUID=8c900d97-367f-47a9-bd66-6eced1a29836 rootfstype=f2fs rootwait ro earlyprintk console=tty1 console=ttySAC1,115200
[    0.000000] PID hash table entries: 4096 (order: 2, 16384 bytes)
[    0.000000] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
[    0.000000] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
[    0.000000] Memory: 1913244K/2071552K available (6144K kernel code, 223K rwdata, 1432K rodata, 1024K init, 246K bss, 27236K reserved, 131072K cma-reserved, 1154048K highmem)
[    0.000000] Virtual kernel memory layout:
[    0.000000]     vector  : 0xffff0000 - 0xffff1000   (   4 kB)
[    0.000000]     fixmap  : 0xffc00000 - 0xfff00000   (3072 kB)
[    0.000000]     vmalloc : 0xf0800000 - 0xff800000   ( 240 MB)
[    0.000000]     lowmem  : 0xc0000000 - 0xf0000000   ( 768 MB)
[    0.000000]     pkmap   : 0xbfe00000 - 0xc0000000   (   2 MB)
[    0.000000]     modules : 0xbf000000 - 0xbfe00000   (  14 MB)
[    0.000000]       .text : 0xc0008000 - 0xc0700000   (7136 kB)
[    0.000000]       .init : 0xc0900000 - 0xc0a00000   (1024 kB)
[    0.000000]       .data : 0xc0a00000 - 0xc0a37fc0   ( 224 kB)
[    0.000000]        .bss : 0xc0a39000 - 0xc0a76a08   ( 247 kB)
[    0.000000] SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=4, Nodes=1
[    0.000000] Preemptible hierarchical RCU implementation.
[    0.000000]  Build-time adjustment of leaf fanout to 32.
[    0.000000] NR_IRQS:16 nr_irqs:16 16
[    0.000000] L2C: platform modifies aux control register: 0x02070000 -> 0x3e470001
[    0.000000] L2C: platform provided aux values permit register corruption.
[    0.000000] L2C: DT/platform modifies aux control register: 0x02070000 -> 0x3e470001
[    0.000000] L2C-310 enabling early BRESP for Cortex-A9
[    0.000000] L2C-310: enabling full line of zeros but not enabled in Cortex-A9
[    0.000000] L2C-310 ID prefetch enabled, offset 8 lines
[    0.000000] L2C-310 dynamic clock gating enabled, standby mode enabled
[    0.000000] L2C-310 cache controller enabled, 16 ways, 1024 kB
[    0.000000] L2C-310: CACHE_ID 0x4100c4c8, AUX_CTRL 0x7e470001
[    0.000000] Exynos4x12 clocks: sclk_apll = 1000000000, sclk_mpll = 800000000
[    0.000000]  sclk_epll = 96000000, sclk_vpll = 350000000, arm_clk = 1000000000
[    0.000000] Switching to timer-based delay loop, resolution 41ns
[    0.000000] clocksource: mct-frc: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 79635851949 ns
[    0.000004] sched_clock: 32 bits at 24MHz, resolution 41ns, wraps every 89478484971ns
[    0.000263] Console: colour dummy device 80x30
[    0.000978] console [tty1] enabled
[    0.001003] Calibrating delay loop (skipped), value calculated using timer frequency.. 48.00 BogoMIPS (lpj=120000)
[    0.001039] pid_max: default: 32768 minimum: 301
[    0.001118] Mount-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.001142] Mountpoint-cache hash table entries: 2048 (order: 1, 8192 bytes)
[    0.001577] CPU: Testing write buffer coherency: ok
[    0.001875] CPU0: thread -1, cpu 0, socket 10, mpidr 80000a00
[    0.002240] Setting up static identity map for 0x40100000 - 0x40100058
[    0.055360] CPU1: thread -1, cpu 1, socket 10, mpidr 80000a01
[    0.075335] CPU2: thread -1, cpu 2, socket 10, mpidr 80000a02
[    0.095316] CPU3: thread -1, cpu 3, socket 10, mpidr 80000a03
[    0.095374] Brought up 4 CPUs
[    0.095445] SMP: Total of 4 processors activated (192.00 BogoMIPS).
[    0.095464] CPU: All CPU(s) started in SVC mode.
[    0.096280] devtmpfs: initialized
[    0.110373] VFP support v0.3: implementor 41 architecture 3 part 30 variant 9 rev 4
[    0.110612] clocksource: jiffies: mask: 0xffffffff max_cycles: 0xffffffff, max_idle_ns: 9556302231375000 ns
[    0.114190] pinctrl core: initialized pinctrl subsystem
[    0.114738] lcd0-power-domain at 10023C80 has as child subdomain: tv-power-domain at 10023C20.
[    0.115573] NET: Registered protocol family 16
[    0.117150] DMA: preallocated 256 KiB pool for atomic coherent allocations
[    0.134991] cpuidle: using governor ladder
[    0.154989] cpuidle: using governor menu
[    0.155398] hw-breakpoint: found 5 (+1 reserved) breakpoint and 1 watchpoint registers.
[    0.155434] hw-breakpoint: maximum watchpoint size is 4 bytes.
[    0.197368] SCSI subsystem initialized
[    0.197498] usbcore: registered new interface driver usbfs
[    0.197568] usbcore: registered new interface driver hub
[    0.197664] usbcore: registered new device driver usb
[    0.198081] s3c-i2c 13860000.i2c: slave address 0x10
[    0.198111] s3c-i2c 13860000.i2c: bus frequency set to 390 KHz
[    0.198570] s3c-i2c 13860000.i2c: i2c-0: S3C I2C adapter
[    0.198763] s3c-i2c 13870000.i2c: slave address 0x10
[    0.198791] s3c-i2c 13870000.i2c: bus frequency set to 390 KHz
[    0.199046] s3c-i2c 13870000.i2c: i2c-1: S3C I2C adapter
[    0.199192] s3c-i2c 13880000.i2c: slave address 0x00
[    0.199218] s3c-i2c 13880000.i2c: bus frequency set to 97 KHz
[    0.199375] s3c-i2c 13880000.i2c: i2c-2: S3C I2C adapter
[    0.199481] s3c-i2c 138e0000.i2c: slave address 0x00
[    0.199506] s3c-i2c 138e0000.i2c: bus frequency set to 97 KHz
[    0.199758] s3c-i2c 138e0000.i2c: i2c-8: S3C I2C adapter
[    0.200052] Linux video capture interface: v2.00
[    0.205439] Advanced Linux Sound Architecture Driver Initialized.
[    0.205901] Bluetooth: Core ver 2.21
[    0.205947] NET: Registered protocol family 31
[    0.205966] Bluetooth: HCI device and connection manager initialized
[    0.205992] Bluetooth: HCI socket layer initialized
[    0.206014] Bluetooth: L2CAP socket layer initialized
[    0.206036] Bluetooth: SCO socket layer initialized
[    0.206567] clocksource: Switched to clocksource mct-frc
[    0.214799] NET: Registered protocol family 2
[    0.215264] TCP established hash table entries: 8192 (order: 3, 32768 bytes)
[    0.215352] TCP bind hash table entries: 8192 (order: 4, 65536 bytes)
[    0.215463] TCP: Hash tables configured (established 8192 bind 8192)
[    0.215531] UDP hash table entries: 512 (order: 2, 16384 bytes)
[    0.215573] UDP-Lite hash table entries: 512 (order: 2, 16384 bytes)
[    0.215717] NET: Registered protocol family 1
[    0.216073] hw perfevents: enabled with armv7_cortex_a9 PMU driver, 7 counters available
[    0.217474] futex hash table entries: 1024 (order: 4, 65536 bytes)
[    0.217587] audit: initializing netlink subsys (disabled)
[    0.217641] audit: type=2000 audit(0.215:1): initialized
[    0.218191] workingset: timestamp_bits=30 max_order=19 bucket_order=0
[    0.230957] bounce: pool size: 64 pages
[    0.231124] Block layer SCSI generic (bsg) driver version 0.4 loaded (major 251)
[    0.231157] io scheduler noop registered
[    0.231175] io scheduler deadline registered
[    0.231306] io scheduler cfq registered (default)
[    0.231963] 125b0000.exynos-usbphy supply vbus not found, using dummy regulator
[    0.235681] dma-pl330 12680000.pdma: Loaded driver for PL330 DMAC-141330
[    0.235717] dma-pl330 12680000.pdma:         DBUFF-32x4bytes Num_Chans-8 Num_Peri-32 Num_Events-32
[    0.238256] dma-pl330 12690000.pdma: Loaded driver for PL330 DMAC-141330
[    0.238291] dma-pl330 12690000.pdma:         DBUFF-32x4bytes Num_Chans-8 Num_Peri-32 Num_Events-32
[    0.239110] dma-pl330 12850000.mdma: Loaded driver for PL330 DMAC-141330
[    0.239141] dma-pl330 12850000.mdma:         DBUFF-64x8bytes Num_Chans-8 Num_Peri-1 Num_Events-32
[    0.239800] 13800000.serial: ttySAC0 at MMIO 0x13800000 (irq = 53, base_baud = 0) is a S3C6400/10
[    0.240144] 13810000.serial: ttySAC1 at MMIO 0x13810000 (irq = 54, base_baud = 0) is a S3C6400/10
[    1.015731] console [ttySAC1] enabled
[    1.019770] 13820000.serial: ttySAC2 at MMIO 0x13820000 (irq = 55, base_baud = 0) is a S3C6400/10
[    1.028570] 13830000.serial: ttySAC3 at MMIO 0x13830000 (irq = 56, base_baud = 0) is a S3C6400/10
[    1.038157] [drm] Initialized drm 1.1.0 20060810
[    1.042093] exynos-mixer 12c10000.mixer: Linked as a consumer to 12e20000.sysmmu
[    1.049118] iommu: Adding device 12c10000.mixer to group 0
[    1.055403] DEBUG: device_links_driver_bound(): 1
[    1.059238] ------------[ cut here ]------------
[    1.063826] WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    1.072851] Modules linked in:
[    1.075859] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 4.8.0-vanilla+ #3
[    1.082458] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    1.088533] Backtrace: 
[    1.090970] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    1.098524]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    1.104161] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    1.111376] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    1.118306]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    1.123946] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    1.131506]  r9:00000000 r8:c0a258d8 r7:ee3114e4 r6:eeafa874 r5:00000000 r4:ee3114c0
[    1.139231] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    1.148615] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    1.157287]  r9:00000000 r8:c0a258d8 r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eeafa810
[    1.165011] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    1.173345]  r4:eeafa810 r3:600000d3
[    1.176894] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    1.185323]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eeafa844 r5:c0a258d8
[    1.193128]  r4:eeafa810 r3:00000000
[    1.196685] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    1.204853]  r6:c0407eb0 r5:c0a258d8 r4:00000000 r3:c0407eb0
[    1.210485] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    1.218480]  r6:c0a26758 r5:ee27f780 r4:c0a258d8
[    1.223072] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    1.231072] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    1.239139]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a258d8
[    1.244773] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    1.253809]  r5:00000000 r4:00000004
[    1.257364] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    1.266402] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    1.274554]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    1.281232] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    1.289917]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    1.297722]  r4:c093a520
[    1.300243] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    1.308493]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    1.316297]  r4:00000000
[    1.318815] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    1.326373]  r4:00000000 r3:ee880000
[    1.329952] ---[ end trace 402e0b75dfe2947b ]---
[    1.336115] exynos-hdmi 12d00000.hdmi: Failed to get supply 'vdd': -517
[    1.341761] s5p-g2d 10800000.g2d: Linked as a consumer to 10a40000.sysmmu
[    1.347960] iommu: Adding device 10800000.g2d to group 1
[    1.353512] s5p-g2d 10800000.g2d: The Exynos G2D (ver 4.1) successfully probed.
[    1.360510] DEBUG: device_links_driver_bound(): 1
[    1.365183] ------------[ cut here ]------------
[    1.369786] WARNING: CPU: 1 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    1.378819] Modules linked in:
[    1.381829] CPU: 1 PID: 1 Comm: swapper/0 Tainted: G        W       4.8.0-vanilla+ #3
[    1.389651] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    1.395718] Backtrace: 
[    1.398154] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    1.405709]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    1.411344] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    1.418560] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    1.425491]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    1.431132] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    1.438691]  r9:00000000 r8:c0a2621c r7:ee910ae4 r6:eeac4a74 r5:00000000 r4:ee910ac0
[    1.446416] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    1.455800] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    1.464472]  r9:00000000 r8:c0a2621c r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eeac4a10
[    1.472196] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    1.480530]  r4:eeac4a10 r3:00000000
[    1.484080] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    1.492508]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eeac4a44 r5:c0a2621c
[    1.500313]  r4:eeac4a10 r3:00000000
[    1.503870] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    1.512038]  r6:c0407eb0 r5:c0a2621c r4:00000000 r3:c0407eb0
[    1.517671] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    1.525665]  r6:c0a26758 r5:ee932b80 r4:c0a2621c
[    1.530257] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    1.538257] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    1.546324]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a2621c
[    1.551959] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    1.560994]  r5:00000000 r4:0000000a
[    1.564548] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    1.573587] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    1.581739]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    1.588415] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    1.597103]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    1.604907]  r4:c093a520
[    1.607427] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    1.615678]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    1.623482]  r4:00000000
[    1.625999] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    1.633558]  r4:00000000 r3:ee880000
[    1.637133] ---[ end trace 402e0b75dfe2947c ]---
[    1.642227] exynos-drm-fimc 11800000.fimc: Linked as a consumer to 11a20000.sysmmu
[    1.649335] iommu: Adding device 11800000.fimc to group 2
[    1.655529] exynos-drm-fimc 11810000.fimc: Linked as a consumer to 11a30000.sysmmu
[    1.662263] iommu: Adding device 11810000.fimc to group 3
[    1.668447] exynos-drm-fimc 11820000.fimc: Linked as a consumer to 11a40000.sysmmu
[    1.675196] iommu: Adding device 11820000.fimc to group 4
[    1.681278] exynos-drm-fimc 11820000.fimc: drm fimc registered successfully.
[    1.687589] DEBUG: device_links_driver_bound(): 1
[    1.692264] ------------[ cut here ]------------
[    1.696852] WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    1.705880] Modules linked in:
[    1.708891] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W       4.8.0-vanilla+ #3
[    1.716713] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    1.722780] Backtrace: 
[    1.725216] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    1.732771]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    1.738407] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    1.745624] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    1.752553]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    1.758194] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    1.765754]  r9:00000000 r8:c0a26330 r7:ee311d24 r6:eea83074 r5:00000000 r4:ee311d00
[    1.773479] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    1.782862] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    1.791534]  r9:00000000 r8:c0a26330 r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eea83010
[    1.799258] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    1.807592]  r4:eea83010 r3:600000d3
[    1.811142] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    1.819571]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eea83044 r5:c0a26330
[    1.827376]  r4:eea83010 r3:00000000
[    1.830932] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    1.839100]  r6:c0407eb0 r5:c0a26330 r4:00000000 r3:c0407eb0
[    1.844733] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    1.852728]  r6:c0a26758 r5:ee27fa00 r4:c0a26330
[    1.857319] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    1.865320] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    1.873386]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a26330
[    1.879021] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    1.888057]  r5:00000000 r4:0000000b
[    1.891610] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    1.900649] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    1.908801]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    1.915478] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    1.924165]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    1.931969]  r4:c093a520
[    1.934489] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    1.942740]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    1.950545]  r4:00000000
[    1.953062] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    1.960620]  r4:00000000 r3:ee880000
[    1.964193] ---[ end trace 402e0b75dfe2947d ]---
[    1.969039] exynos-drm-fimc 11830000.fimc: Linked as a consumer to 11a50000.sysmmu
[    1.976392] iommu: Adding device 11830000.fimc to group 5
[    1.982437] exynos-drm-fimc 11830000.fimc: drm fimc registered successfully.
[    1.988799] DEBUG: device_links_driver_bound(): 1
[    1.993464] ------------[ cut here ]------------
[    1.998056] WARNING: CPU: 2 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c
[    2.007076] Modules linked in:
[    2.010087] CPU: 2 PID: 1 Comm: swapper/0 Tainted: G        W       4.8.0-vanilla+ #3
[    2.017909] Hardware name: SAMSUNG EXYNOS (Flattened Device Tree)
[    2.023975] Backtrace: 
[    2.026412] [<c010bb84>] (dump_backtrace) from [<c010bd80>] (show_stack+0x18/0x1c)
[    2.033967]  r6:60000053 r5:c0a1ff00 r4:00000000 r3:00040800
[    2.039603] [<c010bd68>] (show_stack) from [<c0353640>] (dump_stack+0x9c/0xb0)
[    2.046819] [<c03535a4>] (dump_stack) from [<c011fa74>] (__warn+0xec/0x104)
[    2.053750]  r6:c08038b0 r5:00000000 r4:00000000 r3:00040800
[    2.059390] [<c011f988>] (__warn) from [<c011fb44>] (warn_slowpath_null+0x28/0x30)
[    2.066950]  r9:00000000 r8:c0a26330 r7:ee313264 r6:eea83274 r5:00000000 r4:ee313240
[    2.074675] [<c011fb1c>] (warn_slowpath_null) from [<c0404114>] (device_links_driver_bound+0x124/0x12c)
[    2.084058] [<c0403ff0>] (device_links_driver_bound) from [<c0407950>] (driver_bound+0x68/0xb8)
[    2.092731]  r9:00000000 r8:c0a26330 r7:00000001 r6:c0a6d79c r5:c0a6d794 r4:eea83210
[    2.100454] [<c04078e8>] (driver_bound) from [<c0407e4c>] (driver_probe_device+0x280/0x2e4)
[    2.108788]  r4:eea83210 r3:600000d3
[    2.112338] [<c0407bcc>] (driver_probe_device) from [<c0407f64>] (__driver_attach+0xb4/0xb8)
[    2.120767]  r10:00000000 r9:c03f1178 r8:ffffffff r7:00000000 r6:eea83244 r5:c0a26330
[    2.128571]  r4:eea83210 r3:00000000
[    2.132128] [<c0407eb0>] (__driver_attach) from [<c0405f1c>] (bus_for_each_dev+0x74/0xa8)
[    2.140296]  r6:c0407eb0 r5:c0a26330 r4:00000000 r3:c0407eb0
[    2.145929] [<c0405ea8>] (bus_for_each_dev) from [<c0407760>] (driver_attach+0x24/0x28)
[    2.153924]  r6:c0a26758 r5:ee27fa00 r4:c0a26330
[    2.158516] [<c040773c>] (driver_attach) from [<c0407320>] (bus_add_driver+0x1a8/0x220)
[    2.166516] [<c0407178>] (bus_add_driver) from [<c0408894>] (driver_register+0x80/0x100)
[    2.174582]  r7:c0744e38 r6:c0744dc0 r5:00000000 r4:c0a26330
[    2.180217] [<c0408814>] (driver_register) from [<c0409920>] (__platform_driver_register+0x48/0x50)
[    2.189252]  r5:00000000 r4:0000000b
[    2.192806] [<c04098d8>] (__platform_driver_register) from [<c03f1240>] (exynos_drm_init+0xc8/0xfc)
[    2.201845] [<c03f1178>] (exynos_drm_init) from [<c01017c4>] (do_one_initcall+0x58/0x19c)
[    2.209997]  r8:c0a02448 r7:c0a39000 r6:c0a39000 r5:00000006 r4:c093a520
[    2.216674] [<c010176c>] (do_one_initcall) from [<c0900edc>] (kernel_init_freeable+0x1e4/0x288)
[    2.225361]  r10:c0926834 r9:c090060c r8:0000008e r7:c0a39000 r6:c0a39000 r5:00000006
[    2.233165]  r4:c093a520
[    2.235685] [<c0900cf8>] (kernel_init_freeable) from [<c06dc2dc>] (kernel_init+0x10/0x11c)
[    2.243936]  r10:00000000 r9:00000000 r8:00000000 r7:00000000 r6:00000000 r5:c06dc2cc
[    2.251740]  r4:00000000
[    2.254258] [<c06dc2cc>] (kernel_init) from [<c0107dd8>] (ret_from_fork+0x14/0x3c)
[    2.261816]  r4:00000000 r3:ee880000
[    2.265388] ---[ end trace 402e0b75dfe2947e ]---
[    2.270457] exynos-drm-ipp exynos-drm-ipp: drm ipp registered successfully.
[    2.280368] loop: module loaded
[    2.293185] random: fast init done
[    2.303857] usbcore: registered new interface driver smsc95xx
[    2.491598] dwc2 12480000.hsotg: Specified GNPTXFDEP=1024 > 768
[    2.491906] dwc2 12480000.hsotg: EPs: 16, dedicated fifos, 7808 entries in SPRAM
[    2.499801] ehci_hcd: USB 2.0 'Enhanced' Host Controller (EHCI) Driver
[    2.505798] ehci-exynos: EHCI EXYNOS driver
[    2.510438] exynos-ehci 12580000.ehci: EHCI Host Controller
[    2.515535] exynos-ehci 12580000.ehci: new USB bus registered, assigned bus number 1
[    2.523434] exynos-ehci 12580000.ehci: irq 51, io mem 0x12580000
[    2.541596] exynos-ehci 12580000.ehci: USB 2.0 started, EHCI 1.00
Quoted 1 line

<snip>

I'll try to do another test with just the 5 patches from this set applied (without the IOMMU probe deferral).

With best wishes,
Tobias

Quoted 68 lines · Marek Szyprowski

Dear All,

This patchset adds runtime PM support to common clock framework. This is an attempt to implement support for clock controllers, which belongs to a power domain. This approach works surprisingly well on Exynos 4412 and 5433 SoCs, 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 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 exynos 4412 and 5433 clocks drivers change the way the clock provider is initialized. Instead of CLK_OF_DECLARE based initialization, a complete platform device driver infrastructure is being used. This is needed to let driver to use runtime pm feature and integrate with generic power domains. The side-effect of this change is a delay in clock provider registeration during system boot, so early initialized drivers might get EPROBEDEFER error when requesting their clocks. This is an issue for IOMMU drivers, so this patchset will be fully functional once the deferred probe for IOMMU will be merged.

The side-effect of this patchset is the one can finally read /sys/kernel/debug/clk/clk_summary on all Exynos4412 boards without any freeze.

If one wants to test this patchset (on Exynos4412 Trats2 device with FIMC-IS driver), I've provided a branch with all needed patches (fixes for Exynos, FIMC-IS driver and IOMMU deferred probe):
https://git.linaro.org/people/marek.szyprowski/linux-srpol.git v4.8-clocks-pm-v2

Patches are based on vanilla v4.8-rc7 kernel.

Best regards
Marek Szyprowski
Samsung R&D Institute Poland

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

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

Marek Szyprowski (5):
clk: add support for runtime pm
clock: samsung: add support for runtime pm
clocks: exynos4x12: add runtime pm support for ISP clocks
ARM: dts: exynos: add support for ISP power domain to exynos4x12
clocks device
clocks: exynos5433: add runtime pm support

Quoted 11 lines
 .../devicetree/bindings/clock/exynos4-clock.txt    |  22 ++
 arch/arm/boot/dts/exynos4x12.dtsi                  |   5 +
 drivers/clk/clk.c                                  | 107 +++++-
 drivers/clk/samsung/clk-exynos4.c                  | 227 ++++++++----
 drivers/clk/samsung/clk-exynos5433.c               | 385 ++++++++++++++++-----
 drivers/clk/samsung/clk-pll.c                      |   4 +-
 drivers/clk/samsung/clk.c                          |  36 +-
 drivers/clk/samsung/clk.h                          |   7 +
 include/linux/clk-provider.h                       |   1 +
 9 files changed, 632 insertions(+), 162 deletions(-)

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

#12 lore raw

v2 cover Add runtime PM support for clocks (on Exynos SoC example)

In reply to Tobias Jakobi

Hi Tobias,

Quoted 5 lines · Tobias Jakobi

Hello Marek,

I'm using the patches from the v4.8-clocks-pm-v2 branch plus the ones from the v4.8-clocks-pm-v2 branch on top of 4.8.0.

Quoted 8 lines

I see some warnings on boot coming from driver core. It appears that the warnings are actually meaningful, since IOMMUs stop working completly. E.g. if I modprobe s5p-mfc later, firmware loading fails because apparantly the IOMMU domain isn't online.

Quoted 1 line
WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356 device_links_driver_bound+0x124/0x12c

I added some debug printk() to device_links_driver_bound(), to show the link status. Apparantly it is always DEVICE_LINK_AVAILABLE.

Please note that some additional patches are needed to get IOMMU working properly with both runtime-pm patches and deferred probe, which feature in turn is needed to get it working after adding clocks-pm changes. I will send such patch soon (as a new version of IOMMU deferred probe support patches were posted a few days ago).

Quoted 1 line

(...)

Best regards

Signature · 2 lines
Marek Szyprowski, PhD
Samsung R&D Institute Poland
#13 lore raw

v2 cover Add runtime PM support for clocks (on Exynos SoC example)

Hey Marek,

Quoted 16 lines · Marek Szyprowski

Hi Tobias,

Quoted 12 lines · Tobias Jakobi

Hello Marek,

I'm using the patches from the v4.8-clocks-pm-v2 branch plus the ones from the v4.8-clocks-pm-v2 branch on top of 4.8.0.

I see some warnings on boot coming from driver core. It appears that the warnings are actually meaningful, since IOMMUs stop working completly. E.g. if I modprobe s5p-mfc later, firmware loading fails because apparantly the IOMMU domain isn't online.

Quoted 2 lines
WARNING: CPU: 0 PID: 1 at drivers/base/core.c:356
device_links_driver_bound+0x124/0x12c
Quoted 8 lines
Quoted 2 lines

I added some debug printk() to device_links_driver_bound(), to show the link status. Apparantly it is always DEVICE_LINK_AVAILABLE.

Please note that some additional patches are needed to get IOMMU working properly with both runtime-pm patches and deferred probe, which feature in turn is needed to get it working after adding clocks-pm changes. I will send such patch soon (as a new version of IOMMU deferred probe support patches were posted a few days ago).

I see. I thought that this was supposed to work with the patches in v4.8-clocks-pm-v2.

I picked the patches in this range (endpoints included).
- arm: dma-mapping: Don't override dma_ops in arch_setup_dma_ops()
- clocks: exynos5433: add runtime pm support

And this range in v4.8-iommu-pm-v4>
- driver core: Add a wrapper around __device_release_driver()
- iommu/exynos: Add proper runtime pm support

Anyway, looking forward to test the new patchset.

- Tobias

Quoted 4 lines
Quoted 2 lines

(...)

Best regards