linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4 00/10] clk: implement clock rate protection mechanism
@ 2017-09-24 20:00 Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 01/10] clk: fix incorrect usage of ENOSYS Jerome Brunet
                   ` (9 more replies)
  0 siblings, 10 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman, Peter De Schrijver

This Patchset is related the RFC [0] and the discussion around
CLK_SET_RATE_GATE available here [1]

This patchset introduce clock protection to the CCF core. This can then
be used for:

* Provide a way for a consumer to claim exclusivity over the rate control
  of a provider. Some clock consumers require that a clock rate must not
  deviate from its selected frequency. There can be several reasons for
  this, not least of which is that some hardware may not be able to
  handle or recover from a glitch caused by changing the clock rate while
  the hardware is in operation. For such HW, The ability to get exclusive
  control of a clock's rate, and release that exclusivity, could be seen
  as a fundamental clock rate control primitive. The exclusivity is not
  preemptible, so when claimed more than once, is rate is effectively
  locked.

* Provide a similar functionality to providers themselves, fixing
  CLK_SET_RATE_GATE flag (enforce clock gating along the tree). While
  there might still be a few platforms relying the broken implementation,
  tests done has shown this change to be pretty safe.


Changes since v3: [3]
 - Reorder patches following Stephen comments
 - Add before/after examples to the cosmetic change
 - Remove loops around protection where possible
 - Rename the API from "protect" to "exclusive" which decribe what the
   code better

Changes since v2: [2]
 - Fix issues reported by Adriana Reus (Thanks !)
 - Dropped patch "clk: move CLK_SET_RATE_GATE protection from prepare
   to enable". This was broken as the protect count, like the prepare_count
   should only be accessed under the prepare_lock.

Changes since v1: [1]
 - Check if the rate would actually change before continuing, and bail-out
   early if not.

Changes since RFC: [0]
 - s/clk_protect/clk_rate_protect
 - Request rework around core_nolock function
 - Add clk_set_rate_protect
 - Reword clk_rate_protect and clk_unprotect documentation
 - Add few comments to explain the code
 - Add fixes for CLK_SET_RATE_GATE

This was tested with the audio use case mentioned in [1]

[0]: https://lkml.kernel.org/r/20170321183330.26722-1-jbrunet@baylibre.com
[1]: https://lkml.kernel.org/r/148942423440.82235.17188153691656009029@resonance
[2]: https://lkml.kernel.org/r/20170521215958.19743-1-jbrunet@baylibre.com
[3]: https://lkml.kernel.org/r/20170612194438.12298-1-jbrunet@baylibre.com

Jerome Brunet (10):
  clk: fix incorrect usage of ENOSYS
  clk: take the prepare lock out of clk_core_set_parent
  clk: add clk_core_set_phase_nolock function
  clk: rework calls to round and determine rate callbacks
  clk: use round rate to bail out early in set_rate
  clk: add clock protection mechanism to clk core
  clk: cosmetic changes to clk_summary debugfs entry
  clk: fix CLK_SET_RATE_GATE with clock rate protection
  clk: add clk_rate_exclusive api
  clk: fix set_rate_range when current rate is out of range

 drivers/clk/clk.c            | 505 +++++++++++++++++++++++++++++++++++++------
 include/linux/clk-provider.h |   1 +
 include/linux/clk.h          |  57 +++++
 3 files changed, 492 insertions(+), 71 deletions(-)

-- 
2.13.5

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

* [PATCH v4 01/10] clk: fix incorrect usage of ENOSYS
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 02/10] clk: take the prepare lock out of clk_core_set_parent Jerome Brunet
                   ` (8 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

ENOSYS is special and should only be used for incorrect syscall number.
It does not seem to be the case here.

Reported by checkpatch.pl while working on clock protection.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index c8d83acda006..b31e56b09e25 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1804,7 +1804,7 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 
 	/* verify ops for for multi-parent clks */
 	if ((core->num_parents > 1) && (!core->ops->set_parent)) {
-		ret = -ENOSYS;
+		ret = -EPERM;
 		goto out;
 	}
 
-- 
2.13.5

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

* [PATCH v4 02/10] clk: take the prepare lock out of clk_core_set_parent
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 01/10] clk: fix incorrect usage of ENOSYS Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 03/10] clk: add clk_core_set_phase_nolock function Jerome Brunet
                   ` (7 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Rework set_parent core function so it can be called when the prepare lock
is already held by the caller.

This rework is done to ease the integration of the "protected" clock
functionality.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 41 ++++++++++++++++++++---------------------
 1 file changed, 20 insertions(+), 21 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index b31e56b09e25..3518f251cfd5 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1787,32 +1787,28 @@ bool clk_has_parent(struct clk *clk, struct clk *parent)
 }
 EXPORT_SYMBOL_GPL(clk_has_parent);
 
-static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
+static int clk_core_set_parent_nolock(struct clk_core *core,
+				      struct clk_core *parent)
 {
 	int ret = 0;
 	int p_index = 0;
 	unsigned long p_rate = 0;
 
+	lockdep_assert_held(&prepare_lock);
+
 	if (!core)
 		return 0;
 
-	/* prevent racing with updates to the clock topology */
-	clk_prepare_lock();
-
 	if (core->parent == parent)
-		goto out;
+		return 0;
 
 	/* verify ops for for multi-parent clks */
-	if ((core->num_parents > 1) && (!core->ops->set_parent)) {
-		ret = -EPERM;
-		goto out;
-	}
+	if (core->num_parents > 1 && !core->ops->set_parent)
+		return -EPERM;
 
 	/* check that we are allowed to re-parent if the clock is in use */
-	if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count) {
-		ret = -EBUSY;
-		goto out;
-	}
+	if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count)
+		return -EBUSY;
 
 	/* try finding the new parent index */
 	if (parent) {
@@ -1820,8 +1816,7 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		if (p_index < 0) {
 			pr_debug("%s: clk %s can not be parent of clk %s\n",
 					__func__, parent->name, core->name);
-			ret = p_index;
-			goto out;
+			return p_index;
 		}
 		p_rate = parent->rate;
 	}
@@ -1831,7 +1826,7 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 
 	/* abort if a driver objects */
 	if (ret & NOTIFY_STOP_MASK)
-		goto out;
+		return ret;
 
 	/* do the re-parent */
 	ret = __clk_set_parent(core, parent, p_index);
@@ -1844,9 +1839,6 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
 		__clk_recalc_accuracies(core);
 	}
 
-out:
-	clk_prepare_unlock();
-
 	return ret;
 }
 
@@ -1869,10 +1861,17 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
  */
 int clk_set_parent(struct clk *clk, struct clk *parent)
 {
+	int ret;
+
 	if (!clk)
 		return 0;
 
-	return clk_core_set_parent(clk->core, parent ? parent->core : NULL);
+	clk_prepare_lock();
+	ret = clk_core_set_parent_nolock(clk->core,
+					 parent ? parent->core : NULL);
+	clk_prepare_unlock();
+
+	return ret;
 }
 EXPORT_SYMBOL_GPL(clk_set_parent);
 
@@ -2753,7 +2752,7 @@ void clk_unregister(struct clk *clk)
 		/* Reparent all children to the orphan list. */
 		hlist_for_each_entry_safe(child, t, &clk->core->children,
 					  child_node)
-			clk_core_set_parent(child, NULL);
+			clk_core_set_parent_nolock(child, NULL);
 	}
 
 	hlist_del_init(&clk->core->child_node);
-- 
2.13.5

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

* [PATCH v4 03/10] clk: add clk_core_set_phase_nolock function
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 01/10] clk: fix incorrect usage of ENOSYS Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 02/10] clk: take the prepare lock out of clk_core_set_parent Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 04/10] clk: rework calls to round and determine rate callbacks Jerome Brunet
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Create a core function for set_phase, as it is done for set_rate and
set_parent.

This rework is done to ease the integration of "protected" clock
functionality.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 33 +++++++++++++++++++++------------
 1 file changed, 21 insertions(+), 12 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 3518f251cfd5..0913688dacb0 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1875,6 +1875,25 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
 }
 EXPORT_SYMBOL_GPL(clk_set_parent);
 
+static int clk_core_set_phase_nolock(struct clk_core *core, int degrees)
+{
+	int ret = -EINVAL;
+
+	lockdep_assert_held(&prepare_lock);
+
+	if (!core)
+		return 0;
+
+	trace_clk_set_phase(core, degrees);
+
+	if (core->ops->set_phase)
+		ret = core->ops->set_phase(core->hw, degrees);
+
+	trace_clk_set_phase_complete(core, degrees);
+
+	return ret;
+}
+
 /**
  * clk_set_phase - adjust the phase shift of a clock signal
  * @clk: clock signal source
@@ -1897,7 +1916,7 @@ EXPORT_SYMBOL_GPL(clk_set_parent);
  */
 int clk_set_phase(struct clk *clk, int degrees)
 {
-	int ret = -EINVAL;
+	int ret;
 
 	if (!clk)
 		return 0;
@@ -1908,17 +1927,7 @@ int clk_set_phase(struct clk *clk, int degrees)
 		degrees += 360;
 
 	clk_prepare_lock();
-
-	trace_clk_set_phase(clk->core, degrees);
-
-	if (clk->core->ops->set_phase)
-		ret = clk->core->ops->set_phase(clk->core->hw, degrees);
-
-	trace_clk_set_phase_complete(clk->core, degrees);
-
-	if (!ret)
-		clk->core->phase = degrees;
-
+	ret = clk_core_set_phase_nolock(clk->core, degrees);
 	clk_prepare_unlock();
 
 	return ret;
-- 
2.13.5

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

* [PATCH v4 04/10] clk: rework calls to round and determine rate callbacks
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (2 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 03/10] clk: add clk_core_set_phase_nolock function Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 05/10] clk: use round rate to bail out early in set_rate Jerome Brunet
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Rework the way the callbacks round_rate() and determine_rate() are called.
The goal is to do this at a single point and make it easier to add
conditions before calling them.

Because of this factorization, rate returned by determine_rate() is also
checked against the min and max rate values

This rework is done to ease the integration of "protected" clock
functionality.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 82 +++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 52 insertions(+), 30 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0913688dacb0..0905139a1893 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -833,10 +833,9 @@ static int clk_disable_unused(void)
 }
 late_initcall_sync(clk_disable_unused);
 
-static int clk_core_round_rate_nolock(struct clk_core *core,
-				      struct clk_rate_request *req)
+static int clk_core_determine_round_nolock(struct clk_core *core,
+					   struct clk_rate_request *req)
 {
-	struct clk_core *parent;
 	long rate;
 
 	lockdep_assert_held(&prepare_lock);
@@ -844,15 +843,6 @@ static int clk_core_round_rate_nolock(struct clk_core *core,
 	if (!core)
 		return 0;
 
-	parent = core->parent;
-	if (parent) {
-		req->best_parent_hw = parent->hw;
-		req->best_parent_rate = parent->rate;
-	} else {
-		req->best_parent_hw = NULL;
-		req->best_parent_rate = 0;
-	}
-
 	if (core->ops->determine_rate) {
 		return core->ops->determine_rate(core->hw, req);
 	} else if (core->ops->round_rate) {
@@ -862,15 +852,58 @@ static int clk_core_round_rate_nolock(struct clk_core *core,
 			return rate;
 
 		req->rate = rate;
-	} else if (core->flags & CLK_SET_RATE_PARENT) {
-		return clk_core_round_rate_nolock(parent, req);
 	} else {
-		req->rate = core->rate;
+		return -EINVAL;
 	}
 
 	return 0;
 }
 
+static void clk_core_init_rate_req(struct clk_core * const core,
+				   struct clk_rate_request *req)
+{
+	struct clk_core *parent;
+
+	if (WARN_ON(!core || !req))
+		return;
+
+	parent = core->parent;
+	if (parent) {
+		req->best_parent_hw = parent->hw;
+		req->best_parent_rate = parent->rate;
+	} else {
+		req->best_parent_hw = NULL;
+		req->best_parent_rate = 0;
+	}
+}
+
+static bool clk_core_can_round(struct clk_core * const core)
+{
+	if (core->ops->determine_rate || core->ops->round_rate)
+		return true;
+
+	return false;
+}
+
+static int clk_core_round_rate_nolock(struct clk_core *core,
+				      struct clk_rate_request *req)
+{
+	lockdep_assert_held(&prepare_lock);
+
+	if (!core)
+		return 0;
+
+	clk_core_init_rate_req(core, req);
+
+	if (clk_core_can_round(core))
+		return clk_core_determine_round_nolock(core, req);
+	else if (core->flags & CLK_SET_RATE_PARENT)
+		return clk_core_round_rate_nolock(core->parent, req);
+
+	req->rate = core->rate;
+	return 0;
+}
+
 /**
  * __clk_determine_rate - get the closest rate actually supported by a clock
  * @hw: determine the rate of this clock
@@ -1356,34 +1389,23 @@ static struct clk_core *clk_calc_new_rates(struct clk_core *core,
 	clk_core_get_boundaries(core, &min_rate, &max_rate);
 
 	/* find the closest rate and parent clk/rate */
-	if (core->ops->determine_rate) {
+	if (clk_core_can_round(core)) {
 		struct clk_rate_request req;
 
 		req.rate = rate;
 		req.min_rate = min_rate;
 		req.max_rate = max_rate;
-		if (parent) {
-			req.best_parent_hw = parent->hw;
-			req.best_parent_rate = parent->rate;
-		} else {
-			req.best_parent_hw = NULL;
-			req.best_parent_rate = 0;
-		}
 
-		ret = core->ops->determine_rate(core->hw, &req);
+		clk_core_init_rate_req(core, &req);
+
+		ret = clk_core_determine_round_nolock(core, &req);
 		if (ret < 0)
 			return NULL;
 
 		best_parent_rate = req.best_parent_rate;
 		new_rate = req.rate;
 		parent = req.best_parent_hw ? req.best_parent_hw->core : NULL;
-	} else if (core->ops->round_rate) {
-		ret = core->ops->round_rate(core->hw, rate,
-					    &best_parent_rate);
-		if (ret < 0)
-			return NULL;
 
-		new_rate = ret;
 		if (new_rate < min_rate || new_rate > max_rate)
 			return NULL;
 	} else if (!parent || !(core->flags & CLK_SET_RATE_PARENT)) {
-- 
2.13.5

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

* [PATCH v4 05/10] clk: use round rate to bail out early in set_rate
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (3 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 04/10] clk: rework calls to round and determine rate callbacks Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 06/10] clk: add clock protection mechanism to clk core Jerome Brunet
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

The current implementation of clk_core_set_rate_nolock() bails out early
if the requested rate is exactly the same as the one set. It should bail
out if the request would not result in a rate a change. This is important
when the rate is not exactly what is requested, which is fairly common
with PLLs.

Ex: provider able to give any rate with steps of 100Hz
 - 1st consumer request 48000Hz and gets it.
 - 2nd consumer request 48010Hz as well. If we were to perform the usual
   mechanism, we would get 48000Hz as well. The clock would not change so
   there is no point performing any checks to make sure the clock can
   change, we know it won't.

This is important to prepare the addition of the clock protection
mechanism

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 0905139a1893..44022299741a 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1582,15 +1582,36 @@ static void clk_change_rate(struct clk_core *core)
 		clk_change_rate(core->new_child);
 }
 
+static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
+						     unsigned long req_rate)
+{
+	int ret;
+	struct clk_rate_request req;
+
+	lockdep_assert_held(&prepare_lock);
+
+	if (!core)
+		return 0;
+
+	clk_core_get_boundaries(core, &req.min_rate, &req.max_rate);
+	req.rate = req_rate;
+
+	ret = clk_core_round_rate_nolock(core, &req);
+
+	return ret ? 0 : req.rate;
+}
+
 static int clk_core_set_rate_nolock(struct clk_core *core,
 				    unsigned long req_rate)
 {
 	struct clk_core *top, *fail_clk;
-	unsigned long rate = req_rate;
+	unsigned long rate;
 
 	if (!core)
 		return 0;
 
+	rate = clk_core_req_round_rate_nolock(core, req_rate);
+
 	/* bail early if nothing to do */
 	if (rate == clk_core_get_rate_nolock(core))
 		return 0;
@@ -1599,7 +1620,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 		return -EBUSY;
 
 	/* calculate new rates and get the topmost changed clock */
-	top = clk_calc_new_rates(core, rate);
+	top = clk_calc_new_rates(core, req_rate);
 	if (!top)
 		return -EINVAL;
 
-- 
2.13.5

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

* [PATCH v4 06/10] clk: add clock protection mechanism to clk core
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (4 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 05/10] clk: use round rate to bail out early in set_rate Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 07/10] clk: cosmetic changes to clk_summary debugfs entry Jerome Brunet
                   ` (3 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

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.

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c            | 119 ++++++++++++++++++++++++++++++++++++++++---
 include/linux/clk-provider.h |   1 +
 2 files changed, 113 insertions(+), 7 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 44022299741a..9d371088bb24 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -60,6 +60,7 @@ struct clk_core {
 	bool			orphan;
 	unsigned int		enable_count;
 	unsigned int		prepare_count;
+	unsigned int		protect_count;
 	unsigned long		min_rate;
 	unsigned long		max_rate;
 	unsigned long		accuracy;
@@ -148,6 +149,11 @@ static void clk_enable_unlock(unsigned long 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)
 {
 	/*
@@ -328,6 +334,11 @@ bool clk_hw_is_prepared(const struct clk_hw *hw)
 	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)
 {
 	return clk_core_is_enabled(hw->core);
@@ -466,6 +477,68 @@ EXPORT_SYMBOL_GPL(__clk_mux_determine_rate_closest);
 
 /***        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)
 {
 	lockdep_assert_held(&prepare_lock);
@@ -843,7 +916,9 @@ static int clk_core_determine_round_nolock(struct clk_core *core,
 	if (!core)
 		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);
 	} else if (core->ops->round_rate) {
 		rate = core->ops->round_rate(core->hw, req->rate,
@@ -1585,7 +1660,7 @@ static void clk_change_rate(struct clk_core *core)
 static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
 						     unsigned long req_rate)
 {
-	int ret;
+	int ret, cnt;
 	struct clk_rate_request req;
 
 	lockdep_assert_held(&prepare_lock);
@@ -1593,11 +1668,19 @@ static unsigned long clk_core_req_round_rate_nolock(struct clk_core *core,
 	if (!core)
 		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);
 	req.rate = req_rate;
 
 	ret = clk_core_round_rate_nolock(core, &req);
 
+	/* restore the protection */
+	clk_core_rate_restore_protect(core, cnt);
+
 	return ret ? 0 : req.rate;
 }
 
@@ -1616,6 +1699,10 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (rate == clk_core_get_rate_nolock(core))
 		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)
 		return -EBUSY;
 
@@ -1853,6 +1940,9 @@ static int clk_core_set_parent_nolock(struct clk_core *core,
 	if ((core->flags & CLK_SET_PARENT_GATE) && core->prepare_count)
 		return -EBUSY;
 
+	if (clk_core_rate_is_protected(core))
+		return -EBUSY;
+
 	/* try finding the new parent index */
 	if (parent) {
 		p_index = clk_fetch_parent_index(core, parent);
@@ -1927,6 +2017,9 @@ static int clk_core_set_phase_nolock(struct clk_core *core, int degrees)
 	if (!core)
 		return 0;
 
+	if (clk_core_rate_is_protected(core))
+		return -EBUSY;
+
 	trace_clk_set_phase(core, degrees);
 
 	if (core->ops->set_phase)
@@ -2057,11 +2150,12 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
 	if (!c)
 		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, "",
 		   30 - level * 3, c->name,
-		   c->enable_count, c->prepare_count, clk_core_get_rate(c),
-		   clk_core_get_accuracy(c), clk_core_get_phase(c));
+		   c->enable_count, c->prepare_count, c->protect_count,
+		   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,
@@ -2083,8 +2177,8 @@ static int clk_summary_show(struct seq_file *s, void *data)
 	struct clk_core *c;
 	struct hlist_head **lists = (struct hlist_head **)s->private;
 
-	seq_puts(s, "   clock                         enable_cnt  prepare_cnt        rate   accuracy   phase\n");
-	seq_puts(s, "----------------------------------------------------------------------------------------\n");
+	seq_puts(s, "   clock                         enable_cnt  prepare_cnt  protect_cnt        rate   accuracy   phase\n");
+	seq_puts(s, "----------------------------------------------------------------------------------------------------\n");
 
 	clk_prepare_lock();
 
@@ -2119,6 +2213,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, "\"enable_count\": %d,", c->enable_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, "\"accuracy\": %lu,", clk_core_get_accuracy(c));
 	seq_printf(s, "\"phase\": %d", clk_core_get_phase(c));
@@ -2249,6 +2344,11 @@ static int clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
 	if (!d)
 		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,
 			(u32 *)&core->notifier_count);
 	if (!d)
@@ -2812,6 +2912,11 @@ void clk_unregister(struct clk *clk)
 	if (clk->core->prepare_count)
 		pr_warn("%s: unregistering prepared clock: %s\n",
 					__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);
 unlock:
 	clk_prepare_unlock();
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 5100ec1b5d55..2a225a820cfe 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -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_hw_get_flags(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_is_enabled(struct clk *clk);
 struct clk *__clk_lookup(const char *name);
-- 
2.13.5

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

* [PATCH v4 07/10] clk: cosmetic changes to clk_summary debugfs entry
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (5 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 06/10] clk: add clock protection mechanism to clk core Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 08/10] clk: fix CLK_SET_RATE_GATE with clock rate protection Jerome Brunet
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

clk_summary debugfs entry was already well over the traditional 80
characters per line limit but it grew even larger with the addition of
clock protection.

   clock                         enable_cnt  prepare_cnt  protect_cnt        rate   accuracy   phase
----------------------------------------------------------------------------------------------------
 wifi32k                                  1            1            0       32768          0 0
 vcpu                                     0            0            0  2016000000          0 0
 xtal                                     5            5            0    24000000          0 0

This patch reduce the width a bit:
                                 enable  prepare  protect
   clock                          count    count    count        rate   accuracy   phase
----------------------------------------------------------------------------------------
 wifi32k                              1        1        0       32768          0 0
 vcpu                                 0        0        0  2016000000          0 0
 xtal                                 5        5        0    24000000          0 0

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 9d371088bb24..4693d5e20327 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -2150,7 +2150,7 @@ static void clk_summary_show_one(struct seq_file *s, struct clk_core *c,
 	if (!c)
 		return;
 
-	seq_printf(s, "%*s%-*s %11d %12d %12d %11lu %10lu %-3d\n",
+	seq_printf(s, "%*s%-*s %7d %8d %8d %11lu %10lu %-3d\n",
 		   level * 3 + 1, "",
 		   30 - level * 3, c->name,
 		   c->enable_count, c->prepare_count, c->protect_count,
@@ -2177,8 +2177,9 @@ static int clk_summary_show(struct seq_file *s, void *data)
 	struct clk_core *c;
 	struct hlist_head **lists = (struct hlist_head **)s->private;
 
-	seq_puts(s, "   clock                         enable_cnt  prepare_cnt  protect_cnt        rate   accuracy   phase\n");
-	seq_puts(s, "----------------------------------------------------------------------------------------------------\n");
+	seq_puts(s, "                                 enable  prepare  protect                               \n");
+	seq_puts(s, "   clock                          count    count    count        rate   accuracy   phase\n");
+	seq_puts(s, "----------------------------------------------------------------------------------------\n");
 
 	clk_prepare_lock();
 
-- 
2.13.5

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

* [PATCH v4 08/10] clk: fix CLK_SET_RATE_GATE with clock rate protection
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (6 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 07/10] clk: cosmetic changes to clk_summary debugfs entry Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 09/10] clk: add clk_rate_exclusive api Jerome Brunet
  2017-09-24 20:00 ` [PATCH v4 10/10] clk: fix set_rate_range when current rate is out of range Jerome Brunet
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Using clock rate protection, we can now enforce CLK_SET_RATE_GATE along the
clock tree

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Tested-by: Quentin Schulz <quentin.schulz@free-electrons.com>
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 4693d5e20327..f990ef127a83 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -552,6 +552,9 @@ static void clk_core_unprepare(struct clk_core *core)
 	if (WARN_ON(core->prepare_count == 1 && core->flags & CLK_IS_CRITICAL))
 		return;
 
+	if (core->flags & CLK_SET_RATE_GATE)
+		clk_core_rate_unprotect(core);
+
 	if (--core->prepare_count > 0)
 		return;
 
@@ -622,6 +625,16 @@ static int clk_core_prepare(struct clk_core *core)
 
 	core->prepare_count++;
 
+	/*
+	 * CLK_SET_RATE_GATE is a special case of clock protection
+	 * Instead of a consumer claiming exclusive rate control, it is
+	 * actually the provider which prevents any consumer from making any
+	 * operation which could result in a rate change or rate glitch while
+	 * the clock is prepared.
+	 */
+	if (core->flags & CLK_SET_RATE_GATE)
+		clk_core_rate_protect(core);
+
 	return 0;
 }
 
@@ -1703,9 +1716,6 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
 	if (clk_core_rate_is_protected(core))
 		return -EBUSY;
 
-	if ((core->flags & CLK_SET_RATE_GATE) && core->prepare_count)
-		return -EBUSY;
-
 	/* calculate new rates and get the topmost changed clock */
 	top = clk_calc_new_rates(core, req_rate);
 	if (!top)
-- 
2.13.5

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

* [PATCH v4 09/10] clk: add clk_rate_exclusive api
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (7 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 08/10] clk: fix CLK_SET_RATE_GATE with clock rate protection Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  2017-10-26  5:26   ` Michael Turquette
  2017-09-24 20:00 ` [PATCH v4 10/10] clk: fix set_rate_range when current rate is out of range Jerome Brunet
  9 siblings, 1 reply; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Using clock rate protection, we can now provide a way for clock consumer
to claim exclusive control over the rate of a producer

So far, rate change operations have been a "last write wins" affair. This
changes allows drivers to explicitly protect against this behavior, if
required.

Of course, if exclusivity over a producer is claimed more than once, the
rate is effectively locked as exclusivity cannot be preempted

Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c   | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/clk.h |  57 ++++++++++++++++++
 2 files changed, 224 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index f990ef127a83..cbfff541ec8a 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -85,6 +85,7 @@ struct clk {
 	const char *con_id;
 	unsigned long min_rate;
 	unsigned long max_rate;
+	unsigned int exclusive_count;
 	struct hlist_node clks_node;
 };
 
@@ -512,6 +513,45 @@ static int clk_core_rate_nuke_protect(struct clk_core *core)
 	return ret;
 }
 
+/**
+ * clk_rate_exclusive_put - release exclusivity over clock rate control
+ * @clk: the clk over which the exclusivity is released
+ *
+ * clk_rate_exclusive_put() completes a critical section during which a clock
+ * consumer cannot tolerate any other consumer making any operation on the
+ * clock which could result in a rate change or rate glitch. Exclusive clocks
+ * cannot have their rate changed, either directly or indirectly due to changes
+ * further up the parent chain of clocks. As a result, clocks up parent chain
+ * also get under exclusive control of the calling consumer.
+ *
+ * If exlusivity is claimed more than once on clock, even by the same consumer,
+ * the rate effectively gets locked as exclusivity can't be preempted.
+ *
+ * Calls to clk_rate_exclusive_put() must be balanced with calls to
+ * clk_rate_exclusive_get(). Calls to this function may sleep, and do not return
+ * error status.
+ */
+void clk_rate_exclusive_put(struct clk *clk)
+{
+	if (!clk)
+		return;
+
+	clk_prepare_lock();
+
+	/*
+	 * if there is something wrong with this consumer protect count, stop
+	 * here before messing with the provider
+	 */
+	if (WARN_ON(clk->exclusive_count <= 0))
+		goto out;
+
+	clk_core_rate_unprotect(clk->core);
+	clk->exclusive_count--;
+out:
+	clk_prepare_unlock();
+}
+EXPORT_SYMBOL_GPL(clk_rate_exclusive_put);
+
 static void clk_core_rate_protect(struct clk_core *core)
 {
 	lockdep_assert_held(&prepare_lock);
@@ -539,6 +579,36 @@ static void clk_core_rate_restore_protect(struct clk_core *core, int count)
 	core->protect_count = count;
 }
 
+/**
+ * clk_rate_exclusive_get - get exclusivity over the clk rate control
+ * @clk: the clk over which the exclusity of rate control is requested
+ *
+ * clk_rate_exlusive_get() begins a critical section during which a clock
+ * consumer cannot tolerate any other consumer making any operation on the
+ * clock which could result in a rate change or rate glitch. Exclusive clocks
+ * cannot have their rate changed, either directly or indirectly due to changes
+ * further up the parent chain of clocks. As a result, clocks up parent chain
+ * also get under exclusive control of the calling consumer.
+ *
+ * If exlusivity is claimed more than once on clock, even by the same consumer,
+ * the rate effectively gets locked as exclusivity can't be preempted.
+ *
+ * Calls to clk_rate_exclusive_get() should be balanced with calls to
+ * clk_rate_exclusive_put(). Calls to this function may sleep, and do not
+ * return error status.
+ */
+void clk_rate_exclusive_get(struct clk *clk)
+{
+	if (!clk)
+		return;
+
+	clk_prepare_lock();
+	clk_core_rate_protect(clk->core);
+	clk->exclusive_count++;
+	clk_prepare_unlock();
+}
+EXPORT_SYMBOL_GPL(clk_rate_exclusive_get);
+
 static void clk_core_unprepare(struct clk_core *core)
 {
 	lockdep_assert_held(&prepare_lock);
@@ -929,6 +999,12 @@ static int clk_core_determine_round_nolock(struct clk_core *core,
 	if (!core)
 		return 0;
 
+	/*
+	 * At this point, core protection will be disabled if
+	 * - if the provider is not protected at all
+	 * - if the calling consumer is the only one which has exclusivity
+	 *   over the provider
+	 */
 	if (clk_core_rate_is_protected(core)) {
 		req->rate = core->rate;
 	} else if (core->ops->determine_rate) {
@@ -1045,10 +1121,17 @@ long clk_round_rate(struct clk *clk, unsigned long rate)
 
 	clk_prepare_lock();
 
+	if (clk->exclusive_count)
+		clk_core_rate_unprotect(clk->core);
+
 	clk_core_get_boundaries(clk->core, &req.min_rate, &req.max_rate);
 	req.rate = rate;
 
 	ret = clk_core_round_rate_nolock(clk->core, &req);
+
+	if (clk->exclusive_count)
+		clk_core_rate_protect(clk->core);
+
 	clk_prepare_unlock();
 
 	if (ret)
@@ -1769,8 +1852,14 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
 	/* prevent racing with updates to the clock topology */
 	clk_prepare_lock();
 
+	if (clk->exclusive_count)
+		clk_core_rate_unprotect(clk->core);
+
 	ret = clk_core_set_rate_nolock(clk->core, rate);
 
+	if (clk->exclusive_count)
+		clk_core_rate_protect(clk->core);
+
 	clk_prepare_unlock();
 
 	return ret;
@@ -1778,6 +1867,50 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
 EXPORT_SYMBOL_GPL(clk_set_rate);
 
 /**
+ * clk_set_rate_exclusive - specify a new rate get exclusive control
+ * @clk: the clk whose rate is being changed
+ * @rate: the new rate for clk
+ *
+ * This is a combination of clk_set_rate() and clk_rate_exclusive_get()
+ * within a critical section
+ *
+ * This can be used initially to ensure that at least 1 consumer is
+ * statisfied when several consumers are competing for exclusivity over the
+ * same clock provider.
+ *
+ * The exclusivity is not applied if setting the rate failed.
+ *
+ * Returns 0 on success, -EERROR otherwise.
+ */
+int clk_set_rate_exclusive(struct clk *clk, unsigned long rate)
+{
+	int ret;
+
+	if (!clk)
+		return 0;
+
+	/* prevent racing with updates to the clock topology */
+	clk_prepare_lock();
+
+	/*
+	 * The temporary protection removal is not here, on purpose
+	 * This function is meant to be used instead of clk_rate_protect,
+	 * so before the consumer code path protect the clock provider
+	 */
+
+	ret = clk_core_set_rate_nolock(clk->core, rate);
+	if (!ret) {
+		clk_core_rate_protect(clk->core);
+		clk->exclusive_count++;
+	}
+
+	clk_prepare_unlock();
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(clk_set_rate_exclusive);
+
+/**
  * clk_set_rate_range - set a rate range for a clock source
  * @clk: clock source
  * @min: desired minimum clock rate in Hz, inclusive
@@ -1801,12 +1934,18 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
 
 	clk_prepare_lock();
 
+	if (clk->exclusive_count)
+		clk_core_rate_unprotect(clk->core);
+
 	if (min != clk->min_rate || max != clk->max_rate) {
 		clk->min_rate = min;
 		clk->max_rate = max;
 		ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
 	}
 
+	if (clk->exclusive_count)
+		clk_core_rate_protect(clk->core);
+
 	clk_prepare_unlock();
 
 	return ret;
@@ -2010,8 +2149,16 @@ int clk_set_parent(struct clk *clk, struct clk *parent)
 		return 0;
 
 	clk_prepare_lock();
+
+	if (clk->exclusive_count)
+		clk_core_rate_unprotect(clk->core);
+
 	ret = clk_core_set_parent_nolock(clk->core,
 					 parent ? parent->core : NULL);
+
+	if (clk->exclusive_count)
+		clk_core_rate_protect(clk->core);
+
 	clk_prepare_unlock();
 
 	return ret;
@@ -2073,7 +2220,15 @@ int clk_set_phase(struct clk *clk, int degrees)
 		degrees += 360;
 
 	clk_prepare_lock();
+
+	if (clk->exclusive_count)
+		clk_core_rate_unprotect(clk->core);
+
 	ret = clk_core_set_phase_nolock(clk->core, degrees);
+
+	if (clk->exclusive_count)
+		clk_core_rate_protect(clk->core);
+
 	clk_prepare_unlock();
 
 	return ret;
@@ -3086,6 +3241,18 @@ void __clk_put(struct clk *clk)
 
 	clk_prepare_lock();
 
+	/*
+	 * Before calling clk_put, all calls to clk_rate_exclusive_get() from a
+	 * given user should be balanced with calls to clk_rate_exclusive_put()
+	 * and by that same consumer
+	 */
+	if (WARN_ON(clk->exclusive_count)) {
+		/* We voiced our concern, let's sanitize the situation */
+		clk->core->protect_count -= (clk->exclusive_count - 1);
+		clk_core_rate_unprotect(clk->core);
+		clk->exclusive_count = 0;
+	}
+
 	hlist_del(&clk->clks_node);
 	if (clk->min_rate > clk->core->req_rate ||
 	    clk->max_rate < clk->core->req_rate)
diff --git a/include/linux/clk.h b/include/linux/clk.h
index 12c96d94d1fa..fdababf80420 100644
--- a/include/linux/clk.h
+++ b/include/linux/clk.h
@@ -331,6 +331,36 @@ struct clk *devm_clk_get(struct device *dev, const char *id);
  */
 struct clk *devm_get_clk_from_child(struct device *dev,
 				    struct device_node *np, const char *con_id);
+/**
+ * clk_rate_exclusive_get - get exclusivity over the rate control of a
+ *                          producer
+ * @clk: clock source
+ *
+ * This function allows drivers to get exclusive control over the rate of a
+ * provider. It prevents any other consumer to execute, even indirectly,
+ * opereation which could alter the rate of the provider or cause glitches
+ *
+ * If exlusivity is claimed more than once on clock, even by the same driver,
+ * the rate effectively gets locked as exclusivity can't be preempted.
+ *
+ * Must not be called from within atomic context.
+ */
+void clk_rate_exclusive_get(struct clk *clk);
+
+/**
+ * clk_rate_exclusive_put - release exclusivity over the rate control of a
+ *                          producer
+ * @clk: clock source
+ *
+ * This function allows drivers to release the exclusivity it previously got
+ * from clk_rate_exclusive_get()
+ *
+ * The caller must balance the number of clk_rate_exclusive_get() and
+ * clk_rate_exclusive_put() calls.
+ *
+ * Must not be called from within atomic context.
+ */
+void clk_rate_exclusive_put(struct clk *clk);
 
 /**
  * clk_enable - inform the system when the clock source should be running.
@@ -473,6 +503,23 @@ long clk_round_rate(struct clk *clk, unsigned long rate);
 int clk_set_rate(struct clk *clk, unsigned long rate);
 
 /**
+ * clk_set_rate_exclusive- set the clock rate and claim exclusivity over
+ *                         clock source
+ * @clk: clock source
+ * @rate: desired clock rate in Hz
+ *
+ * This helper function allows drivers to atomically set the rate of a producer
+ * and claim exclusivity over the rate control of the producer.
+ *
+ * It is essentially a combination of clk_set_rate() and
+ * clk_rate_exclusite_get(). Caller must balance this call with a call to
+ * clk_rate_exclusive_put()
+ *
+ * Returns success (0) or negative errno.
+ */
+int clk_set_rate_exclusive(struct clk *clk, unsigned long rate);
+
+/**
  * clk_has_parent - check if a clock is a possible parent for another
  * @clk: clock source
  * @parent: parent clock source
@@ -583,6 +630,11 @@ static inline void clk_bulk_put(int num_clks, struct clk_bulk_data *clks) {}
 
 static inline void devm_clk_put(struct device *dev, struct clk *clk) {}
 
+
+static inline void clk_exclusive_get(struct clk *clk) {}
+
+static inline void clk_exclusive_put(struct clk *clk) {}
+
 static inline int clk_enable(struct clk *clk)
 {
 	return 0;
@@ -609,6 +661,11 @@ static inline int clk_set_rate(struct clk *clk, unsigned long rate)
 	return 0;
 }
 
+static inline int clk_set_rate_exclusive(struct clk *clk, unsigned long rate)
+{
+	return 0;
+}
+
 static inline long clk_round_rate(struct clk *clk, unsigned long rate)
 {
 	return 0;
-- 
2.13.5

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

* [PATCH v4 10/10] clk: fix set_rate_range when current rate is out of range
  2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
                   ` (8 preceding siblings ...)
  2017-09-24 20:00 ` [PATCH v4 09/10] clk: add clk_rate_exclusive api Jerome Brunet
@ 2017-09-24 20:00 ` Jerome Brunet
  9 siblings, 0 replies; 14+ messages in thread
From: Jerome Brunet @ 2017-09-24 20:00 UTC (permalink / raw)
  To: Stephen Boyd, Michael Turquette
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman, Peter De Schrijver

Calling clk_core_set_rate() with core->req_rate is basically a no-op
because of the early bail-out mechanism.

This may leave the clock in inconsistent state if the rate is out the
requested range. Calling clk_core_set_rate() with the closest rate
limit could solve the problem but:
- The underlying determine_rate() callback needs to account for this
  corner case (rounding within the range, if possible)
- if only round_rate() is available, we rely on luck unfortunately.

Fixes: 1c8e600440c7 ("clk: Add rate constraints to clocks")
Signed-off-by: Jerome Brunet <jbrunet@baylibre.com>
---
 drivers/clk/clk.c | 37 +++++++++++++++++++++++++++++++++----
 1 file changed, 33 insertions(+), 4 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index cbfff541ec8a..8bc3d9d4c7ff 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1921,6 +1921,7 @@ EXPORT_SYMBOL_GPL(clk_set_rate_exclusive);
 int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
 {
 	int ret = 0;
+	unsigned long old_min, old_max, rate;
 
 	if (!clk)
 		return 0;
@@ -1937,10 +1938,38 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
 	if (clk->exclusive_count)
 		clk_core_rate_unprotect(clk->core);
 
-	if (min != clk->min_rate || max != clk->max_rate) {
-		clk->min_rate = min;
-		clk->max_rate = max;
-		ret = clk_core_set_rate_nolock(clk->core, clk->core->req_rate);
+	/* Save the current values in case we need to rollback the change */
+	old_min = clk->min_rate;
+	old_max = clk->max_rate;
+	clk->min_rate = min;
+	clk->max_rate = max;
+
+	rate = clk_core_get_rate_nolock(clk->core);
+	if (rate < min || rate > max) {
+		/*
+		 * FIXME:
+		 * We are in bit of trouble here, current rate is outside the
+		 * the requested range. We are going try to request appropriate
+		 * range boundary but there is a catch. It may fail for the
+		 * usual reason (clock broken, clock protected, etc) but also
+		 * because:
+		 * - round_rate() was not favorable and fell on the wrong
+		 *   side of the boundary
+		 * - the determine_rate() callback does not really check for
+		 *   this corner case when determining the rate
+		 */
+
+		if (rate < min)
+			rate = min;
+		else
+			rate = max;
+
+		ret = clk_core_set_rate_nolock(clk->core, rate);
+		if (ret) {
+			/* rollback the changes */
+			clk->min_rate = old_min;
+			clk->max_rate = old_max;
+		}
 	}
 
 	if (clk->exclusive_count)
-- 
2.13.5

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

* Re: [PATCH v4 09/10] clk: add clk_rate_exclusive api
  2017-09-24 20:00 ` [PATCH v4 09/10] clk: add clk_rate_exclusive api Jerome Brunet
@ 2017-10-26  5:26   ` Michael Turquette
  2017-10-31 17:29     ` Jerome Brunet
  0 siblings, 1 reply; 14+ messages in thread
From: Michael Turquette @ 2017-10-26  5:26 UTC (permalink / raw)
  To: Jerome Brunet, Stephen Boyd
  Cc: Jerome Brunet, linux-clk, linux-kernel, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Hi Jerome,

Quoting Jerome Brunet (2017-09-24 22:00:29)
> @@ -1778,6 +1867,50 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
>  EXPORT_SYMBOL_GPL(clk_set_rate);
>  
>  /**
> + * clk_set_rate_exclusive - specify a new rate get exclusive control
> + * @clk: the clk whose rate is being changed
> + * @rate: the new rate for clk
> + *
> + * This is a combination of clk_set_rate() and clk_rate_exclusive_get()
> + * within a critical section
> + *
> + * This can be used initially to ensure that at least 1 consumer is
> + * statisfied when several consumers are competing for exclusivity over the
> + * same clock provider.

Please add the following here:

  Calls to clk_rate_exclusive_get() should be balanced with calls to
  clk_rate_exclusive_put().

Otherwise looks good to me.

Best regards,
Mike

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

* Re: [PATCH v4 09/10] clk: add clk_rate_exclusive api
  2017-10-26  5:26   ` Michael Turquette
@ 2017-10-31 17:29     ` Jerome Brunet
  2017-10-31 17:32       ` Michael Turquette
  0 siblings, 1 reply; 14+ messages in thread
From: Jerome Brunet @ 2017-10-31 17:29 UTC (permalink / raw)
  To: Michael Turquette, Stephen Boyd
  Cc: linux-clk, linux-kernel, Russell King, Linus Walleij,
	Quentin Schulz, Kevin Hilman

On Thu, 2017-10-26 at 07:26 +0200, Michael Turquette wrote:
> Hi Jerome,
> 
> Quoting Jerome Brunet (2017-09-24 22:00:29)
> > @@ -1778,6 +1867,50 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
> >  EXPORT_SYMBOL_GPL(clk_set_rate);
> >  
> >  /**
> > + * clk_set_rate_exclusive - specify a new rate get exclusive control
> > + * @clk: the clk whose rate is being changed
> > + * @rate: the new rate for clk
> > + *
> > + * This is a combination of clk_set_rate() and clk_rate_exclusive_get()
> > + * within a critical section
> > + *
> > + * This can be used initially to ensure that at least 1 consumer is
> > + * statisfied when several consumers are competing for exclusivity over the
> > + * same clock provider.
> 
> Please add the following here:
> 
>   Calls to clk_rate_exclusive_get() should be balanced with calls to
>   clk_rate_exclusive_put().

Oh indeed !
I can do a resend with it or, if you prefer, you may directly amend the patch.
As you prefer

Thanks

> 
> Otherwise looks good to me.
> 
> Best regards,
> Mike

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

* Re: [PATCH v4 09/10] clk: add clk_rate_exclusive api
  2017-10-31 17:29     ` Jerome Brunet
@ 2017-10-31 17:32       ` Michael Turquette
  0 siblings, 0 replies; 14+ messages in thread
From: Michael Turquette @ 2017-10-31 17:32 UTC (permalink / raw)
  To: Jerome Brunet
  Cc: Stephen Boyd, linux-clk, Linux Kernel Mailing List, Russell King,
	Linus Walleij, Quentin Schulz, Kevin Hilman

Hi Jérôme,

On Tue, Oct 31, 2017 at 5:29 PM, Jerome Brunet <jbrunet@baylibre.com> wrote:
> On Thu, 2017-10-26 at 07:26 +0200, Michael Turquette wrote:
>> Hi Jerome,
>>
>> Quoting Jerome Brunet (2017-09-24 22:00:29)
>> > @@ -1778,6 +1867,50 @@ int clk_set_rate(struct clk *clk, unsigned long rate)
>> >  EXPORT_SYMBOL_GPL(clk_set_rate);
>> >
>> >  /**
>> > + * clk_set_rate_exclusive - specify a new rate get exclusive control
>> > + * @clk: the clk whose rate is being changed
>> > + * @rate: the new rate for clk
>> > + *
>> > + * This is a combination of clk_set_rate() and clk_rate_exclusive_get()
>> > + * within a critical section
>> > + *
>> > + * This can be used initially to ensure that at least 1 consumer is
>> > + * statisfied when several consumers are competing for exclusivity over the
>> > + * same clock provider.
>>
>> Please add the following here:
>>
>>   Calls to clk_rate_exclusive_get() should be balanced with calls to
>>   clk_rate_exclusive_put().
>
> Oh indeed !
> I can do a resend with it or, if you prefer, you may directly amend the patch.
> As you prefer

Previously we agreed that these should go onto the next -rc1 so that
they have more soak time. Being a very lazy person, may I ask you to
rebase the patches on -rc1 when it comes out (with this small change)
and then I'll pull them? You can send a PR directly if you like.

Best regards,
Mike

>
> Thanks
>
>>
>> Otherwise looks good to me.
>>
>> Best regards,
>> Mike
>

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

end of thread, other threads:[~2017-10-31 17:33 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-09-24 20:00 [PATCH v4 00/10] clk: implement clock rate protection mechanism Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 01/10] clk: fix incorrect usage of ENOSYS Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 02/10] clk: take the prepare lock out of clk_core_set_parent Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 03/10] clk: add clk_core_set_phase_nolock function Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 04/10] clk: rework calls to round and determine rate callbacks Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 05/10] clk: use round rate to bail out early in set_rate Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 06/10] clk: add clock protection mechanism to clk core Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 07/10] clk: cosmetic changes to clk_summary debugfs entry Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 08/10] clk: fix CLK_SET_RATE_GATE with clock rate protection Jerome Brunet
2017-09-24 20:00 ` [PATCH v4 09/10] clk: add clk_rate_exclusive api Jerome Brunet
2017-10-26  5:26   ` Michael Turquette
2017-10-31 17:29     ` Jerome Brunet
2017-10-31 17:32       ` Michael Turquette
2017-09-24 20:00 ` [PATCH v4 10/10] clk: fix set_rate_range when current rate is out of range Jerome Brunet

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