All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers
@ 2008-12-23 10:13 Paul Walmsley
  2008-12-23 10:13 ` [PATCH 1/7] OMAP2/3 clock: implement clock notifier infrastructure Paul Walmsley
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap

Hello,

This series implements a clock rate change notifier for OMAP2/3 [1].
It applies on top of the "OMAP clock: bug fixes, cleanup,
optimization" series posted to linux-omap on Mon, 22 Dec 2008.

(The notes below assume that the notifiers are used by device drivers;
however, they are also usable by core code.)

The clock notifier is called for both rate and parent changes, and
is called even when the current rate is the same as the future rate,
in the event that the reprogramming causes a glitch.

A single notifier is defined, with four types of messages:

CLK_PREPARE_RATE_CHANGE: Used to ask the driver whether a rate
   change is OK to complete.  When drivers receive this message,
   they should start any actions that must complete before a
   rate change can succeed.

CLK_ABORT_RATE_CHANGE: Indicates that a notifier callback
   has aborted the rate change, or that the clock rate/parent
   change function has failed.  Upon receipt of this message,
   drivers can 

CLK_PRE_RATE_CHANGE: Indicates that the rate/parent change is about
   to take place. The callback must not return until the driver
   is ready for the change.

CLK_POST_RATE_CHANGE: Indicates that the rate/parent change has
   successfully completed.

...

There are three possible sequences of notifier messages that a driver
can receive:

PREPARE -> PRE -> POST: 
   Successful rate/parent change.

PREPARE -> ABORT: 
   Rate/parent change aborted by one of the callbacks.

PREPARE -> PRE -> ABORT
   Rate/parent change aborted by the clock's set_rate()/set_parent()
   functions.

...

If clock notifier callbacks are expected to abort clock changes, they
probably should not be used as the only method for aborting changes.
The clk_set_rate() or clk_set_parent() functions will return -EAGAIN
if a driver aborts the change.  Higher-level code such as CPUFreq may
attempt to call the function repeatedly, pointlessly burning CPU
cycles.  In these situations, application code or system daemons
must also be used to prevent the frequency change from reaching the
clock code when it is expected to fail for long durations.

Callbacks are called with the clock framework spinlock held.  Callback
code should be extremely fast and lightweight and must not call back
into the clock framework.

Since support for clock notifiers currently does not exist in
include/linux/clk.h, drivers that wish to use them should pass
function pointers for clk_notifier_register() and
clk_notifier_unregister() via their platform_data structs.

Please note that these patches have only been lightly tested.  Further
testing is ongoing; help always appreciated.

Compile-tested on OMAP1.  Boot-tested on N800.  Notifier callback
tested on 3430SDP GP ES2.1.

- Paul

--

1, There's no technical reason why these can't also be implemented for
OMAP1.  I just don't have a convenient way to test OMAP1 builds at the
moment.


size:
   text    data     bss     dec     hex filename
3582501  190928  108824 3882253  3b3d0d vmlinux.3430sdp.orig
3583605  191824  108824 3884253  3b44dd vmlinux.3430sdp.patched

diffstat:
 arch/arm/mach-omap2/clock.c             |   69 +++++++++--
 arch/arm/plat-omap/clock.c              |  203 +++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/mach/clock.h |   83 +++++++++++++
 3 files changed, 346 insertions(+), 9 deletions(-)



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

* [PATCH 1/7] OMAP2/3 clock: implement clock notifier infrastructure
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2008-12-23 10:13 ` [PATCH 2/7] OMAP clock: add " Paul Walmsley
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

This patch implements the remaining code for notification of clock
rate changes via the clock framework:

- a notifier registration function, clk_notifier_register()

- a notifier unregistration function, clk_notifier_unregister()

The implementation is via an atomic notifier, called with the clockfw
spinlock held.  Callback functions must not sleep and must not re-enter
the clock framework, and should execute quickly.

There are likely to be few users of these notifiers, compared to the
total number of clocks.  So, rather than storing one notifier per
struct clk, notifiers are stored in a separate, dynamically allocated
list, effectively trading execution speed (in terms of a sequential
scan of the notifier list) for memory savings.  The implementation is
completely hidden from the callbacks and is easily changed if
necessary.

Until prototypes for these functions are made available in
include/linux/clk.h, drivers should pass function pointers to
clk_notifier_register() and clk_notifier_unregister() via their
platform_data struct.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/plat-omap/clock.c              |  122 +++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/mach/clock.h |   82 +++++++++++++++++++++
 2 files changed, 204 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c
index 8d43d78..4c2ed56 100644
--- a/arch/arm/plat-omap/clock.c
+++ b/arch/arm/plat-omap/clock.c
@@ -21,6 +21,7 @@
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/cpufreq.h>
+#include <linux/notifier.h>
 #include <linux/debugfs.h>
 #include <linux/io.h>
 #include <linux/bootmem.h>
@@ -34,6 +35,8 @@ static DEFINE_SPINLOCK(clockfw_lock);
 
 static struct clk_functions *arch_clock;
 
+static LIST_HEAD(clk_notifier_list);
+
 /**
  * omap_clk_for_each_child - call callback on each child clock of clk
  * @clk: struct clk * to use as the "parent"
@@ -526,6 +529,125 @@ void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
 EXPORT_SYMBOL(clk_init_cpufreq_table);
 #endif
 
+/**
+ * clk_notifier_register - add a clock parameter change notifier
+ * @clk: struct clk * to watch
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request notification for changes to the clock 'clk'.  This uses an
+ * atomic notifier.  The callback will be called with interrupts
+ * disabled; therefore callback code should be very lightweight.
+ * Callback code must not call back into the clock framework.
+ * Callback code will be passed the previous and new rate of the
+ * clock.
+ *
+ * clk_notifier_register() must be called from process
+ * context.  Returns -EINVAL if called with null arguments, -ENOMEM
+ * upon allocation failure; otherwise, passes along the return value
+ * of atomic_notifier_chain_register().
+ */
+int clk_notifier_register(struct clk *clk, struct notifier_block *nb)
+{
+	struct clk_notifier *cn = NULL, *cn_new = NULL;
+	int r;
+	unsigned long flags;
+	struct clk *clkp;
+
+	if (!clk || !nb)
+		return -EINVAL;
+
+	/* Allocate this here speculatively so we can avoid GFP_ATOMIC */
+	cn_new = kzalloc(sizeof(struct clk_notifier), GFP_KERNEL);
+	if (!cn_new)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&clockfw_lock, flags);
+
+	list_for_each_entry(cn, &clk_notifier_list, node) {
+		if (cn->clk == clk)
+			break;
+	}
+
+	if (cn->clk != clk) {
+		cn_new->clk = clk;
+		ATOMIC_INIT_NOTIFIER_HEAD(&cn_new->notifier_head);
+
+		list_add(&cn_new->node, &clk_notifier_list);
+		cn = cn_new;
+	} else {
+		kfree(cn_new); /* didn't need it after all */
+	}
+
+	r = atomic_notifier_chain_register(&cn->notifier_head, nb);
+	if (!r) {
+		clkp = clk;
+		do {
+			clkp->notifier_count++;
+		} while ((clkp = clkp->parent));
+	}
+
+	spin_unlock_irqrestore(&clockfw_lock, flags);
+
+	return r;
+}
+
+/**
+ * clk_notifier_unregister - remove a clock change notifier
+ * @clk: struct clk *
+ * @nb: struct notifier_block * with callback info
+ *
+ * Request no further notification for changes to clock 'clk'.  This
+ * function presently does not release memory allocated by its
+ * corresponding _register function; see inline comments for more
+ * information.  Returns -EINVAL if called with null arguments;
+ * otherwise, passes along the return value of
+ * atomic_notifier_chain_unregister().
+ */
+int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb)
+{
+	struct clk_notifier *cn = NULL;
+	struct clk *clkp;
+	int r = -EINVAL;
+	unsigned long flags;
+
+	if (!clk || !nb)
+		return -EINVAL;
+
+	spin_lock_irqsave(&clockfw_lock, flags);
+
+	list_for_each_entry(cn, &clk_notifier_list, node) {
+		if (cn->clk == clk)
+			break;
+	}
+
+	if (cn->clk == clk) {
+		r = atomic_notifier_chain_unregister(&cn->notifier_head, nb);
+
+		if (!r) {
+			clkp = clk;
+			do {
+				clkp->notifier_count--;
+			} while ((clkp = clkp->parent));
+		}
+
+		/*
+		 * XXX ugh, layering violation.  there should be some
+		 * support in the notifier code for this.
+		 */
+		if (!cn->notifier_head.head)
+			kfree(cn);
+
+	} else {
+		r = -ENOENT;
+	}
+
+	spin_unlock_irqrestore(&clockfw_lock, flags);
+
+	return r;
+}
+
+
+
 /*-------------------------------------------------------------------------*/
 
 #ifdef CONFIG_OMAP_RESET_CLOCKS
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index f0194bc..d08f16c 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -10,6 +10,8 @@
  * published by the Free Software Foundation.
  */
 
+#include <linux/notifier.h>
+
 #ifndef __ARCH_ARM_OMAP_CLOCK_H
 #define __ARCH_ARM_OMAP_CLOCK_H
 
@@ -71,6 +73,40 @@ struct clk_child {
 	u8			flags;
 };
 
+/**
+ * struct clk_notifier - associate a clk with a notifier
+ * @clk: struct clk * to associate the notifier with
+ * @notifier_head: an atomic_notifier_head for this clk
+ * @node: linked list pointers
+ *
+ * A list of struct clk_notifier is maintained by the notifier code.
+ * An entry is created whenever code registers the first notifier on a
+ * particular @clk.  Future notifiers on that @clk are added to the
+ * @notifier_head.
+ */
+struct clk_notifier {
+	struct clk			*clk;
+	struct atomic_notifier_head	notifier_head;
+	struct list_head		node;
+};
+
+/**
+ * struct clk_notifier_data - XXX documentation here
+ * @clk: struct clk * to associate the notifier with
+ * @old_rate: previous rate of this clock
+ * @new_rate: new rate of this clock
+ *
+ * new_rate is what the rate will be in the future if this is called
+ * in a pre-notifier, and is what the rate is now set to if called in
+ * a post-notifier.  old_rate is always the clock's rate before this
+ * particular rate change.
+ */
+struct clk_notifier_data {
+	struct clk		*clk;
+	unsigned long		old_rate;
+	unsigned long		new_rate;
+};
+
 struct clk {
 	struct list_head	node;
 	const char		*name;
@@ -87,6 +123,7 @@ struct clk {
 	void			(*init)(struct clk *);
 	int			(*enable)(struct clk *);
 	void			(*disable)(struct clk *);
+	u16			notifier_count;
 	__u8			enable_bit;
 	__s8			usecount;
 	u8			idlest_bit;
@@ -139,6 +176,8 @@ extern void followparent_recalc(struct clk *clk, unsigned long parent_rate,
 extern void clk_allow_idle(struct clk *clk);
 extern void clk_deny_idle(struct clk *clk);
 extern void clk_enable_init_clocks(void);
+extern int clk_notifier_register(struct clk *clk, struct notifier_block *nb);
+extern int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb);
 #ifdef CONFIG_CPU_FREQ
 extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table);
 #endif
@@ -196,4 +235,47 @@ void omap_clk_del_child(struct clk *clk, struct clk *clk2);
 #define CLK_REG_IN_PRM		(1 << 0)
 #define CLK_REG_IN_SCM		(1 << 1)
 
+/*
+ * Clk notifier callback types
+ *
+ * Since the notifier is called with interrupts disabled, any actions
+ * taken by callbacks must be extremely fast and lightweight.
+ *
+ * CLK_PREPARE_RATE_CHANGE: called by clock code to get pre-approval
+ *     for a rate change.  Upon receiving this notification, device
+ *     drivers should expect either a CLK_PRE_RATE_CHANGE event or a
+ *     CLK_ABORT_RATE_CHANGE event to follow shortly.  One example of
+ *     a possible action might be to switch to PIO mode for future
+ *     transfers until a CLK_ABORT_RATE_CHANGE or CLK_POST_RATE_CHANGE
+ *     message is received.  Drivers should return NOTIFY_DONE (*not*
+ *     NOTIFY_OK) if they approve the rate change, or return
+ *     NOTIFY_BAD if they do not approve the change.
+ *
+ * CLK_ABORT_RATE_CHANGE: called if one of the notifier callbacks
+ *     called with CLK_PREPARE_RATE_CHANGE refuses the rate change, or
+ *     if the rate change failed for some reason after
+ *     CLK_PRE_RATE_CHANGE.  In this case, all registered notifiers on
+ *     the clock will be called with CLK_ABORT_RATE_CHANGE -- even if
+ *     they had not yet received the CLK_PREPARE_RATE_CHANGE
+ *     notification. Callbacks must always return NOTIFY_DONE.
+ *
+ * CLK_PRE_RATE_CHANGE - called after all callbacks have approved the
+ *     rate change, immediately before the clock rate is changed, to
+ *     indicate that the rate change will proceed.  Drivers must
+ *     immediately terminate any operations that will be affected by
+ *     the rate change.  Note that the rate change could still fail,
+ *     at which point the driver should receive a
+ *     CLK_ABORT_RATE_CHANGE message.  Callbacks must always return
+ *     NOTIFY_DONE.
+ *
+ * CLK_POST_RATE_CHANGE - called after the clock rate change has
+ *     successfully completed.  Callbacks must always return
+ *     NOTIFY_DONE.
+ *
+ */
+#define CLK_PREPARE_RATE_CHANGE		1
+#define CLK_ABORT_RATE_CHANGE		2
+#define CLK_PRE_RATE_CHANGE		3
+#define CLK_POST_RATE_CHANGE		4
+
 #endif



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

* [PATCH 2/7] OMAP clock: add notifier infrastructure
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
  2008-12-23 10:13 ` [PATCH 1/7] OMAP2/3 clock: implement clock notifier infrastructure Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2008-12-23 10:13 ` [PATCH 3/7] OMAP2/3 clock: store planned clock rates into temporary rate storage Paul Walmsley
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

Add the clock notifier infrastructure.  The external function exposed here
is omap_clk_notify_downstream().  This is intended to be used by
architecture-specific clk_set_rate() and clk_set_parent() functions, e.g.,
omap2_clk_set_rate().

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/plat-omap/clock.c              |   81 +++++++++++++++++++++++++++++++
 arch/arm/plat-omap/include/mach/clock.h |    1 
 2 files changed, 82 insertions(+), 0 deletions(-)

diff --git a/arch/arm/plat-omap/clock.c b/arch/arm/plat-omap/clock.c
index 4c2ed56..73bef03 100644
--- a/arch/arm/plat-omap/clock.c
+++ b/arch/arm/plat-omap/clock.c
@@ -173,6 +173,64 @@ void omap_clk_del_child(struct clk *clk, struct clk *clk2)
 	}
 }
 
+/**
+ * omap_clk_notify - call clk notifier chain
+ * @clk: struct clk * that is changing rate
+ * @msg: clk notifier type (i.e., CLK_POST_RATE_CHANGE; see mach/clock.h)
+ * @old_rate: old rate
+ * @new_rate: new rate
+ *
+ * Triggers a notifier call chain on the post-clk-rate-change notifier
+ * for clock 'clk'.  Passes a pointer to the struct clk and the
+ * previous and current rates to the notifier callback.  Intended to be
+ * called by internal clock code only.  No return value.
+ */
+static int omap_clk_notify(struct clk *clk, unsigned long msg,
+			   unsigned long old_rate, unsigned long new_rate)
+{
+	struct clk_notifier *cn;
+	struct clk_notifier_data cnd;
+	int ret = NOTIFY_DONE;
+
+	cnd.clk = clk;
+	cnd.old_rate = old_rate;
+	cnd.new_rate = new_rate;
+
+	list_for_each_entry(cn, &clk_notifier_list, node) {
+		if (cn->clk == clk) {
+			ret = atomic_notifier_call_chain(&cn->notifier_head,
+							 msg, &cnd);
+			break;
+		}
+	}
+
+	return ret;
+}
+
+/**
+ * _do_clk_notifier - callback function for clock change notification
+ * @clk: struct clk * to start the notifications with
+ * @msg: notifier msg - see "Clk notifier callback types"
+ * @param2: unused
+ *
+ * Notify callbacks associated with @clk that a clock change will or has
+ * occurred.  If @msg is CLK_PREPARE_RATE_CHANGE, then actually pay attention
+ * to the notifier return value.
+ */
+static int _do_clk_notifier(struct clk *clk, unsigned long msg, u8 param2)
+{
+	int ret;
+
+	ret = omap_clk_notify(clk, msg, clk->rate, clk->temp_rate);
+	if (ret && msg == CLK_PREPARE_RATE_CHANGE)
+		return ret;
+
+	if (omap_clk_has_children(clk))
+		return omap_clk_notify_downstream(clk, msg);
+	else
+		return 0;
+}
+
 /*-------------------------------------------------------------------------
  * Standard clock functions defined in include/linux/clk.h
  *-------------------------------------------------------------------------*/
@@ -440,6 +498,27 @@ void recalculate_root_clocks(void)
 			_do_propagate_rate(clkp, 0, CURRENT_RATE);
 }
 
+/**
+ * omap_clk_notify_downstream - trigger clock change notifications
+ * @clk: struct clk * to start the notifications with
+ * @msg: notifier msg - see "Clk notifier callback types"
+ *
+ * Call clock change notifiers on clocks starting with @clk and including
+ * all of @clk's downstream children clocks.  Returns NOTIFY_DONE if
+ * the notifiers ran successfully, or when msg is CLK_PREPARE_RATE_CHANGE,
+ * NOTIFY_BAD if one of the notifiers denied the change.
+ */
+int omap_clk_notify_downstream(struct clk *clk, unsigned long msg)
+{
+	if (clk == NULL || IS_ERR(clk))
+		return -EINVAL;
+
+	if (!clk->notifier_count)
+		return 0;
+
+	return omap_clk_for_each_child(clk, msg, 0, _do_clk_notifier);
+}
+
 int clk_register(struct clk *clk)
 {
 	if (clk == NULL || IS_ERR(clk))
@@ -529,6 +608,8 @@ void clk_init_cpufreq_table(struct cpufreq_frequency_table **table)
 EXPORT_SYMBOL(clk_init_cpufreq_table);
 #endif
 
+/* Clk notifier implementations */
+
 /**
  * clk_notifier_register - add a clock parameter change notifier
  * @clk: struct clk * to watch
diff --git a/arch/arm/plat-omap/include/mach/clock.h b/arch/arm/plat-omap/include/mach/clock.h
index d08f16c..2faf4c4 100644
--- a/arch/arm/plat-omap/include/mach/clock.h
+++ b/arch/arm/plat-omap/include/mach/clock.h
@@ -183,6 +183,7 @@ extern void clk_init_cpufreq_table(struct cpufreq_frequency_table **table);
 #endif
 void omap_clk_add_child(struct clk *clk, struct clk *clk2);
 void omap_clk_del_child(struct clk *clk, struct clk *clk2);
+extern int omap_clk_notify_downstream(struct clk *clk, unsigned long msg);
 
 /* Clock flags */
 #define RATE_CKCTL		(1 << 0)	/* Main fixed ratio clocks */



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

* [PATCH 3/7] OMAP2/3 clock: store planned clock rates into temporary rate storage
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
  2008-12-23 10:13 ` [PATCH 1/7] OMAP2/3 clock: implement clock notifier infrastructure Paul Walmsley
  2008-12-23 10:13 ` [PATCH 2/7] OMAP clock: add " Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2008-12-23 10:13 ` [PATCH 4/7] OMAP2/3 clock: add clk post-rate-change notifiers Paul Walmsley
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

In omap2_clk_set_rate() and omap2_clk_set_parent(), if a notifier
exists on this clock, precompute the rate that each clock will be set
to, and store that into the clk->temp_rate variable for each struct clk.
Also improve some debug while here.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/clock.c |   36 +++++++++++++++++++++++++-----------
 1 files changed, 25 insertions(+), 11 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index c21767a..c990a17 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -790,10 +790,18 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 {
 	int ret = -EINVAL;
 
-	pr_debug("clock: set_rate for clock %s to rate %ld\n", clk->name, rate);
+	if (!clk->set_rate)
+		return -EINVAL;
+
+	if (clk->notifier_count) {
+		clk->temp_rate = rate;
+		propagate_rate(clk, TEMP_RATE);
+	}
+
+	pr_debug("clock: %s: set_rate from %ld Hz to %ld Hz\n", clk->name,
+		 clk->rate, rate);
 
-	if (clk->set_rate != NULL)
-		ret = clk->set_rate(clk, rate);
+	ret = clk->set_rate(clk, rate);
 
 	return ret;
 }
@@ -835,6 +843,7 @@ static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
 int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 {
 	u32 field_val, v, parent_div;
+	unsigned long orig_rate, new_rate;
 
 	if (!clk->clksel)
 		return -EINVAL;
@@ -843,6 +852,16 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 	if (!parent_div)
 		return -EINVAL;
 
+	orig_rate = clk->rate;
+	new_rate = new_parent->rate;
+	if (parent_div > 0)
+		new_rate /= parent_div;
+
+	if (clk->notifier_count) {
+		clk->temp_rate = new_rate;
+		propagate_rate(clk, TEMP_RATE);
+	}
+
 	if (clk->usecount > 0)
 		_omap2_clk_disable(clk);
 
@@ -859,15 +878,10 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 		_omap2_clk_enable(clk);
 
 	clk->parent = new_parent;
+	clk->rate = new_rate;
 
-	/* CLKSEL clocks follow their parents' rates, divided by a divisor */
-	clk->rate = new_parent->rate;
-
-	if (parent_div > 0)
-		clk->rate /= parent_div;
-
-	pr_debug("clock: set parent of %s to %s (new rate %ld)\n",
-		 clk->name, clk->parent->name, clk->rate);
+	pr_debug("clock: %s: set parent to %s (orig rate %ld, new rate %ld)\n",
+		 clk->name, clk->parent->name, orig_rate, new_rate);
 
 	return 0;
 }



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

* [PATCH 4/7] OMAP2/3 clock: add clk post-rate-change notifiers
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
                   ` (2 preceding siblings ...)
  2008-12-23 10:13 ` [PATCH 3/7] OMAP2/3 clock: store planned clock rates into temporary rate storage Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2008-12-23 10:13 ` [PATCH 5/7] OMAP2/3 clock: add clock pre-rate-change notification Paul Walmsley
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

Calls the  clock post-rate-change notifier whenever clock rates are changed
in the OMAP clock framework.  This is primarily in *_recalc() functions.

One item to note is that the post-rate-change notifier is called even
if the new clock rate is identical to the old rate.  This is because
the process of changing the rate may have temporarily disabled or
glitched the clock or one of its parents, and some devices may be
sensitive to such changes.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/clock.c |   11 ++++++++++-
 1 files changed, 10 insertions(+), 1 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index c990a17..843d1b5 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -789,6 +789,7 @@ int omap2_clksel_set_rate(struct clk *clk, unsigned long rate)
 int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 {
 	int ret = -EINVAL;
+	unsigned long temp_rate;
 
 	if (!clk->set_rate)
 		return -EINVAL;
@@ -803,6 +804,11 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 
 	ret = clk->set_rate(clk, rate);
 
+	temp_rate = clk->rate;
+	clk->rate = clk->temp_rate;
+	omap_clk_notify_downstream(clk, CLK_POST_RATE_CHANGE);
+	clk->rate = temp_rate;
+
 	return ret;
 }
 
@@ -878,11 +884,14 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 		_omap2_clk_enable(clk);
 
 	clk->parent = new_parent;
-	clk->rate = new_rate;
 
 	pr_debug("clock: %s: set parent to %s (orig rate %ld, new rate %ld)\n",
 		 clk->name, clk->parent->name, orig_rate, new_rate);
 
+	omap_clk_notify_downstream(clk, CLK_POST_RATE_CHANGE);
+
+	clk->rate = new_rate;
+
 	return 0;
 }
 



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

* [PATCH 6/7] OMAP2/3 clock: add clock prepare-rate-change notifications
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
                   ` (4 preceding siblings ...)
  2008-12-23 10:13 ` [PATCH 5/7] OMAP2/3 clock: add clock pre-rate-change notification Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2008-12-23 10:13 ` [PATCH 7/7] OMAP2/3 clock: add clock abort-rate-change notifications Paul Walmsley
  2009-01-13 17:01 ` [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Kevin Hilman
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

Call the clock notifiers before rate or parent changes.  If a callback
refuses the change, it should return NOTIFY_BAD; otherwise, NOTIFY_DONE.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/clock.c |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 2a024bf..50d8e06 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -790,6 +790,7 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 {
 	int ret = -EINVAL;
 	unsigned long temp_rate;
+	int r;
 
 	if (!clk->set_rate)
 		return -EINVAL;
@@ -799,6 +800,13 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 		propagate_rate(clk, TEMP_RATE);
 	}
 
+	r = omap_clk_notify_downstream(clk, CLK_PREPARE_RATE_CHANGE);
+	if (r == NOTIFY_BAD) {
+		pr_debug("clock: %s: clk_set_rate() aborted by notifier\n",
+			 clk->name);
+		return -EAGAIN;
+	}
+
 	pr_debug("clock: %s: set_rate from %ld Hz to %ld Hz\n", clk->name,
 		 clk->rate, rate);
 
@@ -851,6 +859,7 @@ static u32 _omap2_clksel_get_src_field(struct clk *src_clk, struct clk *clk,
 int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 {
 	u32 field_val, v, parent_div;
+	int r;
 	unsigned long orig_rate, new_rate;
 
 	if (!clk->clksel)
@@ -870,6 +879,13 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 		propagate_rate(clk, TEMP_RATE);
 	}
 
+	r = omap_clk_notify_downstream(clk, CLK_PREPARE_RATE_CHANGE);
+	if (r == NOTIFY_BAD) {
+		pr_debug("clock: %s: clk_set_parent() aborted by notifier\n",
+			 clk->name);
+		return -EAGAIN;
+	}
+
 	omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE);
 
 	if (clk->usecount > 0)



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

* [PATCH 5/7] OMAP2/3 clock: add clock pre-rate-change notification
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
                   ` (3 preceding siblings ...)
  2008-12-23 10:13 ` [PATCH 4/7] OMAP2/3 clock: add clk post-rate-change notifiers Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2008-12-23 10:13 ` [PATCH 6/7] OMAP2/3 clock: add clock prepare-rate-change notifications Paul Walmsley
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

Call the clock notifiers immediately before changing the clock's rate or
parent.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/clock.c |    4 ++++
 1 files changed, 4 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 843d1b5..2a024bf 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -802,6 +802,8 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 	pr_debug("clock: %s: set_rate from %ld Hz to %ld Hz\n", clk->name,
 		 clk->rate, rate);
 
+	omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE);
+
 	ret = clk->set_rate(clk, rate);
 
 	temp_rate = clk->rate;
@@ -868,6 +870,8 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 		propagate_rate(clk, TEMP_RATE);
 	}
 
+	omap_clk_notify_downstream(clk, CLK_PRE_RATE_CHANGE);
+
 	if (clk->usecount > 0)
 		_omap2_clk_disable(clk);
 



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

* [PATCH 7/7] OMAP2/3 clock: add clock abort-rate-change notifications
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
                   ` (5 preceding siblings ...)
  2008-12-23 10:13 ` [PATCH 6/7] OMAP2/3 clock: add clock prepare-rate-change notifications Paul Walmsley
@ 2008-12-23 10:13 ` Paul Walmsley
  2009-01-13 17:01 ` [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Kevin Hilman
  7 siblings, 0 replies; 9+ messages in thread
From: Paul Walmsley @ 2008-12-23 10:13 UTC (permalink / raw)
  To: linux-omap; +Cc: Paul Walmsley

Call clock notifiers with the CLK_ABORT_RATE_CHANGE message in two
conditions:

- in the PREPARE phase when another callback has denied the rate/parent
  change; or

- in the PRE phase if the set_rate() or set_parent() function fails.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
---
 arch/arm/mach-omap2/clock.c |   16 ++++++++++++----
 1 files changed, 12 insertions(+), 4 deletions(-)

diff --git a/arch/arm/mach-omap2/clock.c b/arch/arm/mach-omap2/clock.c
index 50d8e06..1600119 100644
--- a/arch/arm/mach-omap2/clock.c
+++ b/arch/arm/mach-omap2/clock.c
@@ -804,6 +804,7 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 	if (r == NOTIFY_BAD) {
 		pr_debug("clock: %s: clk_set_rate() aborted by notifier\n",
 			 clk->name);
+		omap_clk_notify_downstream(clk, CLK_ABORT_RATE_CHANGE);
 		return -EAGAIN;
 	}
 
@@ -814,10 +815,16 @@ int omap2_clk_set_rate(struct clk *clk, unsigned long rate)
 
 	ret = clk->set_rate(clk, rate);
 
-	temp_rate = clk->rate;
-	clk->rate = clk->temp_rate;
-	omap_clk_notify_downstream(clk, CLK_POST_RATE_CHANGE);
-	clk->rate = temp_rate;
+	if (ret == 0) {
+		temp_rate = clk->rate;
+		clk->rate = clk->temp_rate;
+		omap_clk_notify_downstream(clk, CLK_POST_RATE_CHANGE);
+		clk->rate = temp_rate;
+	} else {
+		pr_debug("clock: %s: clk_set_rate() aborted by failed "
+			 "set_rate(): %d\n", clk->name, ret);
+		omap_clk_notify_downstream(clk, CLK_ABORT_RATE_CHANGE);
+	}
 
 	return ret;
 }
@@ -883,6 +890,7 @@ int omap2_clk_set_parent(struct clk *clk, struct clk *new_parent)
 	if (r == NOTIFY_BAD) {
 		pr_debug("clock: %s: clk_set_parent() aborted by notifier\n",
 			 clk->name);
+		omap_clk_notify_downstream(clk, CLK_ABORT_RATE_CHANGE);
 		return -EAGAIN;
 	}
 



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

* Re: [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers
  2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
                   ` (6 preceding siblings ...)
  2008-12-23 10:13 ` [PATCH 7/7] OMAP2/3 clock: add clock abort-rate-change notifications Paul Walmsley
@ 2009-01-13 17:01 ` Kevin Hilman
  7 siblings, 0 replies; 9+ messages in thread
From: Kevin Hilman @ 2009-01-13 17:01 UTC (permalink / raw)
  To: Paul Walmsley; +Cc: linux-omap

Paul Walmsley <paul@pwsan.com> writes:

> Hello,
>
> This series implements a clock rate change notifier for OMAP2/3 [1].
> It applies on top of the "OMAP clock: bug fixes, cleanup,
> optimization" series posted to linux-omap on Mon, 22 Dec 2008.

Now that the 'buf fixes, cleanup' series is now in linux-omap, I'll pull 
this series into the PM branch for more testing.

Kevin

> (The notes below assume that the notifiers are used by device drivers;
> however, they are also usable by core code.)
>
> The clock notifier is called for both rate and parent changes, and
> is called even when the current rate is the same as the future rate,
> in the event that the reprogramming causes a glitch.
>
> A single notifier is defined, with four types of messages:
>
> CLK_PREPARE_RATE_CHANGE: Used to ask the driver whether a rate
>    change is OK to complete.  When drivers receive this message,
>    they should start any actions that must complete before a
>    rate change can succeed.
>
> CLK_ABORT_RATE_CHANGE: Indicates that a notifier callback
>    has aborted the rate change, or that the clock rate/parent
>    change function has failed.  Upon receipt of this message,
>    drivers can 
>
> CLK_PRE_RATE_CHANGE: Indicates that the rate/parent change is about
>    to take place. The callback must not return until the driver
>    is ready for the change.
>
> CLK_POST_RATE_CHANGE: Indicates that the rate/parent change has
>    successfully completed.
>
> ...
>
> There are three possible sequences of notifier messages that a driver
> can receive:
>
> PREPARE -> PRE -> POST: 
>    Successful rate/parent change.
>
> PREPARE -> ABORT: 
>    Rate/parent change aborted by one of the callbacks.
>
> PREPARE -> PRE -> ABORT
>    Rate/parent change aborted by the clock's set_rate()/set_parent()
>    functions.
>
> ...
>
> If clock notifier callbacks are expected to abort clock changes, they
> probably should not be used as the only method for aborting changes.
> The clk_set_rate() or clk_set_parent() functions will return -EAGAIN
> if a driver aborts the change.  Higher-level code such as CPUFreq may
> attempt to call the function repeatedly, pointlessly burning CPU
> cycles.  In these situations, application code or system daemons
> must also be used to prevent the frequency change from reaching the
> clock code when it is expected to fail for long durations.
>
> Callbacks are called with the clock framework spinlock held.  Callback
> code should be extremely fast and lightweight and must not call back
> into the clock framework.
>
> Since support for clock notifiers currently does not exist in
> include/linux/clk.h, drivers that wish to use them should pass
> function pointers for clk_notifier_register() and
> clk_notifier_unregister() via their platform_data structs.
>
> Please note that these patches have only been lightly tested.  Further
> testing is ongoing; help always appreciated.
>
> Compile-tested on OMAP1.  Boot-tested on N800.  Notifier callback
> tested on 3430SDP GP ES2.1.
>
> - Paul
>
> --
>
> 1, There's no technical reason why these can't also be implemented for
> OMAP1.  I just don't have a convenient way to test OMAP1 builds at the
> moment.
>
>
> size:
>    text    data     bss     dec     hex filename
> 3582501  190928  108824 3882253  3b3d0d vmlinux.3430sdp.orig
> 3583605  191824  108824 3884253  3b44dd vmlinux.3430sdp.patched
>
> diffstat:
>  arch/arm/mach-omap2/clock.c             |   69 +++++++++--
>  arch/arm/plat-omap/clock.c              |  203 +++++++++++++++++++++++++++++++
>  arch/arm/plat-omap/include/mach/clock.h |   83 +++++++++++++
>  3 files changed, 346 insertions(+), 9 deletions(-)
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-omap" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

end of thread, other threads:[~2009-01-13 17:01 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-12-23 10:13 [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Paul Walmsley
2008-12-23 10:13 ` [PATCH 1/7] OMAP2/3 clock: implement clock notifier infrastructure Paul Walmsley
2008-12-23 10:13 ` [PATCH 2/7] OMAP clock: add " Paul Walmsley
2008-12-23 10:13 ` [PATCH 3/7] OMAP2/3 clock: store planned clock rates into temporary rate storage Paul Walmsley
2008-12-23 10:13 ` [PATCH 4/7] OMAP2/3 clock: add clk post-rate-change notifiers Paul Walmsley
2008-12-23 10:13 ` [PATCH 5/7] OMAP2/3 clock: add clock pre-rate-change notification Paul Walmsley
2008-12-23 10:13 ` [PATCH 6/7] OMAP2/3 clock: add clock prepare-rate-change notifications Paul Walmsley
2008-12-23 10:13 ` [PATCH 7/7] OMAP2/3 clock: add clock abort-rate-change notifications Paul Walmsley
2009-01-13 17:01 ` [PATCH 0/7] OMAP2/3 clock: implement clock rate change notifiers Kevin Hilman

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.