Commit e55a839a authored by Jerome Brunet's avatar Jerome Brunet Committed by Michael Turquette

clk: add clock protection mechanism to clk core

The patch adds clk_core_protect and clk_core_unprotect to the internal
CCF API. These functions allow to set a new constraint along the clock
tree to prevent any change, even indirect, which may result in rate
change or glitch.
Tested-by: default avatarMaxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: default avatarMichael Turquette <mturquette@baylibre.com>
Signed-off-by: default avatarJerome Brunet <jbrunet@baylibre.com>
Signed-off-by: default avatarMichael Turquette <mturquette@baylibre.com>
Link: lkml.kernel.org/r/20171201215200.23523-7-jbrunet@baylibre.com
parent ca5e089a
...@@ -62,6 +62,7 @@ struct clk_core { ...@@ -62,6 +62,7 @@ struct clk_core {
bool orphan; bool orphan;
unsigned int enable_count; unsigned int enable_count;
unsigned int prepare_count; unsigned int prepare_count;
unsigned int protect_count;
unsigned long min_rate; unsigned long min_rate;
unsigned long max_rate; unsigned long max_rate;
unsigned long accuracy; unsigned long accuracy;
...@@ -170,6 +171,11 @@ static void clk_enable_unlock(unsigned long flags) ...@@ -170,6 +171,11 @@ static void clk_enable_unlock(unsigned long flags)
spin_unlock_irqrestore(&enable_lock, flags); spin_unlock_irqrestore(&enable_lock, flags);
} }
static bool clk_core_rate_is_protected(struct clk_core *core)
{
return core->protect_count;
}
static bool clk_core_is_prepared(struct clk_core *core) static bool clk_core_is_prepared(struct clk_core *core)
{ {
bool ret = false; bool ret = false;
...@@ -381,6 +387,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw) ...@@ -381,6 +387,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
return clk_core_is_prepared(hw->core); return clk_core_is_prepared(hw->core);
} }
bool clk_hw_rate_is_protected(const struct clk_hw *hw)
{
return clk_core_rate_is_protected(hw->core);
}
bool clk_hw_is_enabled(const struct clk_hw *hw) bool clk_hw_is_enabled(const struct clk_hw *hw)
{ {
return clk_core_is_enabled(hw->core); return clk_core_is_enabled(hw->core);
...@@ -519,6 +530,68 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest); ...@@ -519,6 +530,68 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
/*** clk api ***/ /*** clk api ***/
static void clk_core_rate_unprotect(struct clk_core *core)
{
lockdep_assert_held(&prepare_lock);
if (!core)
return;
if (WARN_ON(core->protect_count == 0))
return;
if (--core->protect_count > 0)
return;
clk_core_rate_unprotect(core->parent);
}
static int clk_core_rate_nuke_protect(struct clk_core *core)
{
int ret;
lockdep_assert_held(&prepare_lock);
if (!core)
return -EINVAL;
if (core->protect_count == 0)
return 0;
ret = core->protect_count;
core->protect_count = 1;
clk_core_rate_unprotect(core);
return ret;
}
static void clk_core_rate_protect(struct clk_core *core)
{
lockdep_assert_held(&prepare_lock);
if (!core)
return;
if (core->protect_count == 0)
clk_core_rate_protect(core->parent);
core->protect_count++;
}
static void clk_core_rate_restore_protect(struct clk_core *core, int count)
{
lockdep_assert_held(&prepare_lock);
if (!core)
return;
if (count == 0)
return;
clk_core_rate_protect(core);
core->protect_count = count;
}
static void clk_core_unprepare(struct clk_core *core) static void clk_core_unprepare(struct clk_core *core)
{ {
lockdep_assert_held(&prepare_lock); lockdep_assert_held(&prepare_lock);
...@@ -915,7 +988,9 @@ static int clk_core_determine_round_nolock(struct clk_core *core, ...@@ -915,7 +988,9 @@ static int clk_core_determine_round_nolock(struct clk_core *core,
if (!core) if (!core)
return 0; return 0;
if (core->ops->determine_rate) { if (clk_core_rate_is_protected(core)) {
req->rate = core->rate;
} else if (core->ops->determine_rate) {
return core->ops->determine_rate(core->hw, req); return core->ops->determine_rate(core->hw, req);
} else if (core->ops->round_rate) { } else if (core->ops->round_rate) {
rate = core->ops->round_rate(core->hw, req->rate, rate = core->ops->round_rate(core->hw, req->rate,
...@@ -1661,7 +1736,7 @@ static void clk_change_rate(struct clk_core *core) ...@@ -1661,7 +1736,7 @@ static void clk_change_rate(struct clk_core *core)
static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
unsigned long req_rate) unsigned long req_rate)
{ {
int ret; int ret, cnt;
struct clk_rate_request req; struct clk_rate_request req;
lockdep_assert_held(&prepare_lock); lockdep_assert_held(&prepare_lock);
...@@ -1669,11 +1744,19 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core, ...@@ -1669,11 +1744,19 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
if (!core) if (!core)
return 0; return 0;
/* simulate what the rate would be if it could be freely set */
cnt = clk_core_rate_nuke_protect(core);
if (cnt < 0)
return cnt;
clk_core_get_boundaries(core, &req.min_rate, &req.max_rate); clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
req.rate = req_rate; req.rate = req_rate;
ret = clk_core_round_rate_nolock(core, &req); ret = clk_core_round_rate_nolock(core, &req);
/* restore the protection */
clk_core_rate_restore_protect(core, cnt);
return ret ? 0 : req.rate; return ret ? 0 : req.rate;
} }
...@@ -1693,6 +1776,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core, ...@@ -1693,6 +1776,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
if (rate == clk_core_get_rate_nolock(core)) if (rate == clk_core_get_rate_nolock(core))
return 0; return 0;
/* fail on a direct rate set of a protected provider */
if (clk_core_rate_is_protected(core))
return -EBUSY;
if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count) if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count)
return -EBUSY; return -EBUSY;
...@@ -1937,6 +2024,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core, ...@@ -1937,6 +2024,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count)
return -EBUSY; return -EBUSY;
if (clk_core_rate_is_protected(core))
return -EBUSY;
/* try finding the new parent index */ /* try finding the new parent index */
if (parent) { if (parent) {
p_index = clk_fetch_parent_index(core, parent); p_index = clk_fetch_parent_index(core, parent);
...@@ -2018,6 +2108,9 @@ static int clk_core_set_phase_nolock(struct clk_core *core, int degrees) ...@@ -2018,6 +2108,9 @@ static int clk_core_set_phase_nolock(struct clk_core *core, int degrees)
if (!core) if (!core)
return 0; return 0;
if (clk_core_rate_is_protected(core))
return -EBUSY;
trace_clk_set_phase(core, degrees); trace_clk_set_phase(core, degrees);
if (core->ops->set_phase) if (core->ops->set_phase)
...@@ -2148,11 +2241,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c, ...@@ -2148,11 +2241,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
if (!c) if (!c)
return; return;
seq_printf(s, "%*s%-*s %11d %12d %11lu %10lu %-3d\n", seq_printf(s, "%*s%-*s %11d %12d %12d %11lu %10lu %-3d\n",
level * 3 + 1, "", level * 3 + 1, "",
30 - level * 3, c->name, 30 - level * 3, c->name,
c->enable_count, c->prepare_count, clk_core_get_rate(c), c->enable_count, c->prepare_count, c->protect_count,
clk_core_get_accuracy(c), clk_core_get_phase(c)); clk_core_get_rate(c), clk_core_get_accuracy(c),
clk_core_get_phase(c));
} }
static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c, static void clk_summary_show_subtree(struct seq_file *s, struct clk_core *c,
...@@ -2174,8 +2268,8 @@ static int clk_summary_show(struct seq_file *s, void *data) ...@@ -2174,8 +2268,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
struct clk_core *c; struct clk_core *c;
struct hlist_head **lists = (struct hlist_head **)s->private; struct hlist_head **lists = (struct hlist_head **)s->private;
seq_puts(s, " clock enable_cnt prepare_cnt rate accuracy phase\n"); seq_puts(s, " clock enable_cnt prepare_cnt protect_cnt rate accuracy phase\n");
seq_puts(s, "----------------------------------------------------------------------------------------\n"); seq_puts(s, "----------------------------------------------------------------------------------------------------\n");
clk_prepare_lock(); clk_prepare_lock();
...@@ -2210,6 +2304,7 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level) ...@@ -2210,6 +2304,7 @@ static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
seq_printf(s, "\"%s\": { ", c->name); seq_printf(s, "\"%s\": { ", c->name);
seq_printf(s, "\"enable_count\": %d,", c->enable_count); seq_printf(s, "\"enable_count\": %d,", c->enable_count);
seq_printf(s, "\"prepare_count\": %d,", c->prepare_count); seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
seq_printf(s, "\"protect_count\": %d,", c->protect_count);
seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c)); seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c));
seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c)); seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c));
seq_printf(s, "\"phase\": %d", clk_core_get_phase(c)); seq_printf(s, "\"phase\": %d", clk_core_get_phase(c));
...@@ -2340,6 +2435,11 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry) ...@@ -2340,6 +2435,11 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
if (!d) if (!d)
goto err_out; goto err_out;
d = debugfs_create_u32("clk_protect_count", S_IRUGO, core->dentry,
(u32 *)&core->protect_count);
if (!d)
goto err_out;
d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry, d = debugfs_create_u32("clk_notifier_count", S_IRUGO, core->dentry,
(u32 *)&core->notifier_count); (u32 *)&core->notifier_count);
if (!d) if (!d)
...@@ -2911,6 +3011,11 @@ void clk_unregister(struct clk *clk) ...@@ -2911,6 +3011,11 @@ void clk_unregister(struct clk *clk)
if (clk->core->prepare_count) if (clk->core->prepare_count)
pr_warn("%s: unregistering prepared clock: %s\n", pr_warn("%s: unregistering prepared clock: %s\n",
__func__, clk->core->name); __func__, clk->core->name);
if (clk->core->protect_count)
pr_warn("%s: unregistering protected clock: %s\n",
__func__, clk->core->name);
kref_put(&clk->core->ref, __clk_release); kref_put(&clk->core->ref, __clk_release);
unlock: unlock:
clk_prepare_unlock(); clk_prepare_unlock();
......
...@@ -744,6 +744,7 @@ unsigned long clk_hw_get_rate(const struct clk_hw *hw); ...@@ -744,6 +744,7 @@ unsigned long clk_hw_get_rate(const struct clk_hw *hw);
unsigned long __clk_get_flags(struct clk *clk); unsigned long __clk_get_flags(struct clk *clk);
unsigned long clk_hw_get_flags(const struct clk_hw *hw); unsigned long clk_hw_get_flags(const struct clk_hw *hw);
bool clk_hw_is_prepared(const struct clk_hw *hw); bool clk_hw_is_prepared(const struct clk_hw *hw);
bool clk_hw_rate_is_protected(const struct clk_hw *hw);
bool clk_hw_is_enabled(const struct clk_hw *hw); bool clk_hw_is_enabled(const struct clk_hw *hw);
bool __clk_is_enabled(struct clk *clk); bool __clk_is_enabled(struct clk *clk);
struct clk *__clk_lookup(const char *name); struct clk *__clk_lookup(const char *name);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment