From 5e2521feab41b71fc80b1b41d4eb6ec967919eed Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 29 Sep 2014 08:10:49 +0200 Subject: [PATCH] clk: Allow drivers to reference unused clocks Some drivers are designed to take over a virtual device set up at boot time by firmware. This can be useful for early boot output on a display device when no debug serial console is available or for transitioning from firmware to the Linux kernel in a seemless way. This type of driver relies on firmware to have set up hardware in a way that it can scan out a framebuffer using the display hardware. Some of the resources used by the display hardware (clocks, power supplies, ...) will typically be turned off (at late_initcall time) by the respective Linux kernel subsystems unless explicitly claimed by some hardware- specific driver. However, if this driver is built as a module (loaded from a filesystem) or defer probing, they will not claim the resources until long after late_initcall time. This doesn't allow for a seemless transition. It can also happen that the hardware-specific driver is never loaded. It may be that no such driver exists, or it fails to load. Users may even decide not to load it on purpose, perhaps because it is buggy. Instead of turning the display off in such cases, a better option is to keep running with the existing framebuffer, which may also be helpful in diagnosing why the real driver wasn't loaded. This patch provides a way for drivers that make use of clocks set up by firmware to prevent clocks from being automatically disabled. Similarly a way is provided to signal that they no longer need the clocks (when the hardware-specific driver has taken over for example). Signed-off-by: Thierry Reding --- drivers/clk/clk.c | 58 +++++++++++++++++++++++++++++++++++------------------ include/linux/clk.h | 26 ++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 19 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 52d58279a612..2007f10e244c 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -486,38 +486,58 @@ out: return; } -static bool clk_ignore_unused; -static int __init clk_ignore_unused_setup(char *__unused) +static unsigned long clk_ignore_unused_count = 1; + +void clk_ignore_unused(void) { - clk_ignore_unused = true; - return 1; + clk_prepare_lock(); + + if (clk_ignore_unused_count == 0) + pr_warn("clk: unused clocks already disabled\n"); + else + clk_ignore_unused_count++; + + clk_prepare_unlock(); } -__setup("clk_ignore_unused", clk_ignore_unused_setup); -static int clk_disable_unused(void) +void clk_unignore_unused(void) { struct clk *clk; - if (clk_ignore_unused) { - pr_warn("clk: Not disabling unused clocks\n"); - return 0; - } - clk_prepare_lock(); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_disable_unused_subtree(clk); + if (--clk_ignore_unused_count == 0) { + hlist_for_each_entry(clk, &clk_root_list, child_node) + clk_disable_unused_subtree(clk); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_disable_unused_subtree(clk); + hlist_for_each_entry(clk, &clk_orphan_list, child_node) + clk_disable_unused_subtree(clk); - hlist_for_each_entry(clk, &clk_root_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(clk, &clk_root_list, child_node) + clk_unprepare_unused_subtree(clk); - hlist_for_each_entry(clk, &clk_orphan_list, child_node) - clk_unprepare_unused_subtree(clk); + hlist_for_each_entry(clk, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(clk); + } clk_prepare_unlock(); +} + +static int __init clk_ignore_unused_setup(char *__unused) +{ + clk_ignore_unused(); + return 1; +} +__setup("clk_ignore_unused", clk_ignore_unused_setup); + +static int clk_disable_unused(void) +{ + clk_unignore_unused(); + + if (clk_ignore_unused_count > 0) { + pr_warn("clk: Not disabling unused clocks\n"); + return 0; + } return 0; } diff --git a/include/linux/clk.h b/include/linux/clk.h index afb44bfaf8d1..b587f2b2f2f5 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -106,6 +106,24 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb); */ long clk_get_accuracy(struct clk *clk); +/** + * clk_ignore_unused - request that unused clocks be kept running + * + * This function can be used by drivers to request that unused clocks are kept + * running. It is useful for drivers that take over hardware previously set up + * by firmware and which may not explicitly claim all clocks. + */ +void clk_ignore_unused(void); + +/** + * clk_unignore_unused - release unused clocks + * + * Use this function to undo the effects of clk_ignore_unused(). It is meant + * to be called by drivers for firmware devices after they've handed off the + * device to a proper hardware-specific driver. + */ +void clk_unignore_unused(void); + #else static inline long clk_get_accuracy(struct clk *clk) @@ -113,6 +131,14 @@ static inline long clk_get_accuracy(struct clk *clk) return -ENOTSUPP; } +static inline void clk_ignore_unused(void) +{ +} + +static inline void clk_unignore_unused(void) +{ +} + #endif /** -- 2.1.0