From c95714d0ba915e422ad676f68630fcd513f34295 Mon Sep 17 00:00:00 2001
From: Colin Cross <ccross@android.com>
Date: Mon, 1 Nov 2010 17:25:30 -0700
Subject: [PATCH] ARM: tegra: clock: Redo clock locking

Give each clock its own lock, and remove all lock traversals from
parent to child clocks to prevent AB-BA deadlocks.

Signed-off-by: Colin Cross <ccross@android.com>
Change-Id: I0afb7d1bca956439b1a4f17bbc6748aaec706b49
---
 arch/arm/mach-tegra/clock.c            | 584 ++++++++++++++++---------
 arch/arm/mach-tegra/clock.h            |  16 +-
 arch/arm/mach-tegra/include/mach/clk.h |   1 +
 arch/arm/mach-tegra/tegra2_clocks.c    |  75 ++--
 4 files changed, 439 insertions(+), 237 deletions(-)

diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index d34f0efbdd37..4486214c52f5 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -18,119 +18,205 @@
 
 #include <linux/kernel.h>
 #include <linux/clk.h>
-#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
 #include <linux/init.h>
+#include <linux/list.h>
 #include <linux/module.h>
-#include <linux/debugfs.h>
-#include <linux/slab.h>
 #include <linux/seq_file.h>
+#include <linux/slab.h>
+
 #include <asm/clkdev.h>
+
 #include <mach/clk.h>
 
 #include "board.h"
 #include "clock.h"
 #include "dvfs.h"
 
-static LIST_HEAD(clocks);
-
 /*
- * clock_lock must be held when:
- *   Accessing any clock register non-atomically
- *     or
- *   Relying on any state of a clk struct not to change, unless clk_is_dvfs
- *   returns true on that clk struct, and dvfs_lock is held instead.
+ * Locking:
+ *
+ * Each struct clk has a lock.  Depending on the cansleep flag, that lock
+ * may be a spinlock or a mutex.  For most clocks, the spinlock is sufficient,
+ * and using the spinlock allows the clock to be manipulated from an interrupt
+ * or while holding a spinlock.  Some clocks may need to adjust a regulator
+ * in order to maintain the required voltage for a new frequency.  Those
+ * clocks set the cansleep flag, and take a mutex so that the regulator api
+ * can be used while holding the lock.
+ *
+ * To avoid AB-BA locking problems, locks must always be traversed from child
+ * clock to parent clock.  For example, when enabling a clock, the clock's lock
+ * is taken, and then clk_enable is called on the parent, which take's the
+ * parent clock's lock.  There are two exceptions to this ordering:
+ *  1. When setting a clock as cansleep, in which case the entire list of clocks
+ *     is traversed to set the children as cansleep as well.  This must occur
+ *     during init, before any calls to clk_get, so no other clock locks can
+ *     get taken.
+ *  2. When dumping the clock tree through debugfs.  In this case, clk_lock_all
+ *     is called, which attemps to iterate through the entire list of clocks
+ *     and take every clock lock.  If any call to clk_trylock fails, a locked
+ *     clocks are unlocked, and the process is retried.  When all the locks
+ *     are held, the only clock operation that can be called is
+ *     clk_get_rate_all_locked.
  *
- *   Any function that changes the state of a clk struct must hold
- *   the dvfs_lock if clk_is_auto_dvfs(clk) is true, and the clock_lock.
+ * Within a single clock, no clock operation can call another clock operation
+ * on itself, except for clk_get_rate_locked.  Any clock operation can call
+ * any other clock operation on any of it's possible parents.
  *
- *   When taking dvfs_lock and clock_lock, dvfs_lock must be taken first.
+ * clk_set_cansleep is used to mark a clock as sleeping.  It is called during
+ * dvfs (Dynamic Voltage and Frequency Scaling) init on any clock that has a
+ * dvfs requirement.  It can only be called on clocks that are the sole parent
+ * of all of their child clocks, meaning the child clock can not be reparented
+ * onto a different, possibly non-sleeping, clock.  This is inherently true
+ * of all leaf clocks in the clock tree
+ *
+ * An additional lock, clock_list_lock, is used to protect the list of all
+ * clocks.
+ *
+ * The clock operations must lock internally to protect against
+ * read-modify-write on registers that are shared by multiple clocks
  */
-static DEFINE_SPINLOCK(clock_lock);
+static DEFINE_MUTEX(clock_list_lock);
+static LIST_HEAD(clocks);
 
 static inline bool clk_is_auto_dvfs(struct clk *c)
 {
-	smp_rmb();
 	return c->auto_dvfs;
-};
+}
 
 static inline bool clk_is_dvfs(struct clk *c)
 {
-	smp_rmb();
 	return c->is_dvfs;
-};
+}
+
+static inline bool clk_cansleep(struct clk *c)
+{
+	return c->cansleep;
+}
+
+#define clk_lock_save(c, flags)						\
+	do {								\
+		if (clk_cansleep(c)) {					\
+			flags = 0;					\
+			mutex_lock(&c->mutex);				\
+		} else {						\
+			spin_lock_irqsave(&c->spinlock, flags);		\
+		}							\
+	} while (0)
+
+#define clk_unlock_restore(c, flags)					\
+	do {								\
+		if (clk_cansleep(c))					\
+			mutex_unlock(&c->mutex);			\
+		else							\
+			spin_unlock_irqrestore(&c->spinlock, flags);	\
+	} while (0)
+
+static inline void clk_lock_init(struct clk *c)
+{
+	mutex_init(&c->mutex);
+	spin_lock_init(&c->spinlock);
+}
 
 struct clk *tegra_get_clock_by_name(const char *name)
 {
 	struct clk *c;
 	struct clk *ret = NULL;
-	unsigned long flags;
-	spin_lock_irqsave(&clock_lock, flags);
+	mutex_lock(&clock_list_lock);
 	list_for_each_entry(c, &clocks, node) {
 		if (strcmp(c->name, name) == 0) {
 			ret = c;
 			break;
 		}
 	}
-	spin_unlock_irqrestore(&clock_lock, flags);
+	mutex_unlock(&clock_list_lock);
 	return ret;
 }
 
+/* Must be called with clk_lock(c) held */
 static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
 {
 	u64 rate;
 
-	rate = p->rate;
+	rate = clk_get_rate(p);
 
 	if (c->mul != 0 && c->div != 0) {
-		rate = rate * c->mul;
+		rate *= c->mul;
 		do_div(rate, c->div);
 	}
 
 	return rate;
 }
 
-static void clk_recalculate_rate(struct clk *c)
+/* Must be called with clk_lock(c) held */
+unsigned long clk_get_rate_locked(struct clk *c)
 {
 	unsigned long rate;
 
-	if (!c->parent)
-		return;
+	if (c->parent)
+		rate = clk_predict_rate_from_parent(c, c->parent);
+	else
+		rate = c->rate;
 
-	rate = clk_predict_rate_from_parent(c, c->parent);
+	return rate;
+}
 
-	if (rate > c->max_rate)
-		pr_warn("clocks: Set clock %s to rate %lu, max is %lu\n",
-			c->name, rate, c->max_rate);
+unsigned long clk_get_rate(struct clk *c)
+{
+	unsigned long flags;
+	unsigned long rate;
+
+	clk_lock_save(c, flags);
+
+	rate = clk_get_rate_locked(c);
 
-	c->rate = rate;
+	clk_unlock_restore(c, flags);
+
+	return rate;
 }
+EXPORT_SYMBOL(clk_get_rate);
 
-int clk_reparent(struct clk *c, struct clk *parent)
+static void __clk_set_cansleep(struct clk *c)
 {
-	c->parent = parent;
-	list_del(&c->sibling);
-	list_add_tail(&c->sibling, &parent->children);
-	return 0;
+	struct clk *child;
+	BUG_ON(mutex_is_locked(&c->mutex));
+	BUG_ON(spin_is_locked(&c->spinlock));
+
+	list_for_each_entry(child, &clocks, node) {
+		if (child->parent != c)
+			continue;
+
+		WARN(child->ops && child->ops->set_parent,
+			"can't make child clock %s of %s "
+			"sleepable if it's parent could change",
+			child->name, c->name);
+
+		__clk_set_cansleep(child);
+	}
+
+	c->cansleep = true;
 }
 
-static void propagate_rate(struct clk *c)
+/* Must be called before any clk_get calls */
+void clk_set_cansleep(struct clk *c)
 {
-	struct clk *clkp;
 
-	list_for_each_entry(clkp, &c->children, sibling) {
-		clk_recalculate_rate(clkp);
-		propagate_rate(clkp);
-	}
+	mutex_lock(&clock_list_lock);
+	__clk_set_cansleep(c);
+	mutex_unlock(&clock_list_lock);
 }
 
-void clk_init(struct clk *c)
+int clk_reparent(struct clk *c, struct clk *parent)
 {
-	unsigned long flags;
+	c->parent = parent;
+	return 0;
+}
 
-	spin_lock_irqsave(&clock_lock, flags);
+void clk_init(struct clk *c)
+{
+	clk_lock_init(c);
 
-	INIT_LIST_HEAD(&c->children);
-	INIT_LIST_HEAD(&c->sibling);
 	INIT_LIST_HEAD(&c->dvfs);
 
 	if (c->ops && c->ops->init)
@@ -145,71 +231,58 @@ void clk_init(struct clk *c)
 			c->state = ON;
 	}
 
-	clk_recalculate_rate(c);
-
+	mutex_lock(&clock_list_lock);
 	list_add(&c->node, &clocks);
-
-	if (c->parent)
-		list_add_tail(&c->sibling, &c->parent->children);
-
-	spin_unlock_irqrestore(&clock_lock, flags);
+	mutex_unlock(&clock_list_lock);
 }
 
-int clk_enable_locked(struct clk *c)
+int clk_enable(struct clk *c)
 {
-	int ret;
+	int ret = 0;
+	unsigned long flags;
+
+	clk_lock_save(c, flags);
+
+	if (clk_is_auto_dvfs(c)) {
+		ret = tegra_dvfs_set_rate(c, clk_get_rate_locked(c));
+		if (ret)
+			goto out;
+	}
 
 	if (c->refcnt == 0) {
 		if (c->parent) {
-			ret = clk_enable_locked(c->parent);
+			ret = clk_enable(c->parent);
 			if (ret)
-				return ret;
+				goto out;
 		}
 
 		if (c->ops && c->ops->enable) {
 			ret = c->ops->enable(c);
 			if (ret) {
 				if (c->parent)
-					clk_disable_locked(c->parent);
-				return ret;
+					clk_disable(c->parent);
+				goto out;
 			}
 			c->state = ON;
 			c->set = true;
 		}
 	}
 	c->refcnt++;
-
-	return 0;
-}
-
-int clk_enable(struct clk *c)
-{
-	int ret;
-	unsigned long flags;
-
-	if (clk_is_auto_dvfs(c)) {
-		lock_dvfs();
-		ret = tegra_dvfs_set_rate(c, c->rate);
-		if (ret)
-			goto out;
-	}
-
-	spin_lock_irqsave(&clock_lock, flags);
-	ret = clk_enable_locked(c);
-	spin_unlock_irqrestore(&clock_lock, flags);
-
 out:
-	if (clk_is_auto_dvfs(c))
-		unlock_dvfs();
-
+	clk_unlock_restore(c, flags);
 	return ret;
 }
 EXPORT_SYMBOL(clk_enable);
 
-void clk_disable_locked(struct clk *c)
+void clk_disable(struct clk *c)
 {
+	unsigned long flags;
+
+	clk_lock_save(c, flags);
+
 	if (c->refcnt == 0) {
 		WARN(1, "Attempting to disable clock %s with refcnt 0", c->name);
+		clk_unlock_restore(c, flags);
 		return;
 	}
 	if (c->refcnt == 1) {
@@ -217,79 +290,53 @@ void clk_disable_locked(struct clk *c)
 			c->ops->disable(c);
 
 		if (c->parent)
-			clk_disable_locked(c->parent);
+			clk_disable(c->parent);
 
 		c->state = OFF;
 	}
 	c->refcnt--;
-}
-
-void clk_disable(struct clk *c)
-{
-	unsigned long flags;
-
-	if (clk_is_auto_dvfs(c))
-		lock_dvfs();
 
-	spin_lock_irqsave(&clock_lock, flags);
-	clk_disable_locked(c);
-	spin_unlock_irqrestore(&clock_lock, flags);
+	if (clk_is_auto_dvfs(c) && c->refcnt == 0)
+		tegra_dvfs_set_rate(c, 0);
 
-	if (clk_is_auto_dvfs(c)) {
-		if (c->refcnt == 0)
-			tegra_dvfs_set_rate(c, 0);
-		unlock_dvfs();
-	}
+	clk_unlock_restore(c, flags);
 }
 EXPORT_SYMBOL(clk_disable);
 
-int clk_set_parent_locked(struct clk *c, struct clk *parent)
-{
-	int ret;
-
-	if (!c->ops || !c->ops->set_parent)
-		return -ENOSYS;
-
-	ret = c->ops->set_parent(c, parent);
-
-	if (ret)
-		return ret;
-
-	clk_recalculate_rate(c);
-
-	propagate_rate(c);
-
-	return 0;
-}
-
 int clk_set_parent(struct clk *c, struct clk *parent)
 {
 	int ret = 0;
 	unsigned long flags;
-	unsigned long new_rate = clk_predict_rate_from_parent(c, parent);
+	unsigned long new_rate;
+	unsigned long old_rate;
 
+	clk_lock_save(c, flags);
 
-	if (clk_is_auto_dvfs(c)) {
-		lock_dvfs();
-		if (c->refcnt > 0 && (!c->parent || new_rate > c->rate))
-			ret = tegra_dvfs_set_rate(c, new_rate);
-		if (!ret)
+	if (!c->ops || !c->ops->set_parent) {
+		ret = -ENOSYS;
+		goto out;
+	}
+
+	new_rate = clk_predict_rate_from_parent(c, parent);
+	old_rate = clk_get_rate_locked(c);
+
+	if (clk_is_auto_dvfs(c) && c->refcnt > 0 &&
+			(!c->parent || new_rate > old_rate)) {
+		ret = tegra_dvfs_set_rate(c, new_rate);
+		if (ret)
 			goto out;
 	}
 
-	spin_lock_irqsave(&clock_lock, flags);
-	ret = clk_set_parent_locked(c, parent);
-	spin_unlock_irqrestore(&clock_lock, flags);
-	if (!ret)
+	ret = c->ops->set_parent(c, parent);
+	if (ret)
 		goto out;
 
-	if (clk_is_auto_dvfs(c) && c->refcnt > 0)
+	if (clk_is_auto_dvfs(c) && c->refcnt > 0 &&
+			new_rate < old_rate)
 		ret = tegra_dvfs_set_rate(c, new_rate);
 
 out:
-	if (clk_is_auto_dvfs(c))
-		unlock_dvfs();
-
+	clk_unlock_restore(c, flags);
 	return ret;
 }
 EXPORT_SYMBOL(clk_set_parent);
@@ -300,86 +347,87 @@ struct clk *clk_get_parent(struct clk *c)
 }
 EXPORT_SYMBOL(clk_get_parent);
 
-int clk_set_rate_locked(struct clk *c, unsigned long rate)
+int clk_set_rate(struct clk *c, unsigned long rate)
 {
-	int ret;
-
-	if (rate == c->requested_rate)
-		return 0;
-
-	if (rate > c->max_rate)
-		rate = c->max_rate;
-
-	if (!c->ops || !c->ops->set_rate)
-		return -ENOSYS;
-
-	c->requested_rate = rate;
-
-	ret = c->ops->set_rate(c, rate);
-
-	if (ret)
-		return ret;
+	int ret = 0;
+	unsigned long flags;
+	unsigned long old_rate;
 
-	clk_recalculate_rate(c);
+	clk_lock_save(c, flags);
 
-	propagate_rate(c);
+	if (!c->ops || !c->ops->set_rate) {
+		ret = -ENOSYS;
+		goto out;
+	}
 
-	return 0;
-}
+	old_rate = clk_get_rate_locked(c);
 
-int clk_set_rate(struct clk *c, unsigned long rate)
-{
-	int ret = 0;
-	unsigned long flags;
+	if (rate > c->max_rate)
+		rate = c->max_rate;
 
-	if (clk_is_auto_dvfs(c)) {
-		lock_dvfs();
-		if (rate > c->rate && c->refcnt > 0)
-			ret = tegra_dvfs_set_rate(c, rate);
+	if (clk_is_auto_dvfs(c) && rate > old_rate && c->refcnt > 0) {
+		ret = tegra_dvfs_set_rate(c, rate);
 		if (ret)
 			goto out;
 	}
 
-	spin_lock_irqsave(&clock_lock, flags);
-	ret = clk_set_rate_locked(c, rate);
-	spin_unlock_irqrestore(&clock_lock, flags);
-
+	ret = c->ops->set_rate(c, rate);
 	if (ret)
 		goto out;
 
-	if (clk_is_auto_dvfs(c) && c->refcnt > 0)
+	if (clk_is_auto_dvfs(c) && rate < old_rate && c->refcnt > 0)
 		ret = tegra_dvfs_set_rate(c, rate);
 
 out:
-	if (clk_is_auto_dvfs(c))
-		unlock_dvfs();
+	clk_unlock_restore(c, flags);
 	return ret;
 }
 EXPORT_SYMBOL(clk_set_rate);
 
-unsigned long clk_get_rate(struct clk *c)
+/* Must be called with clocks lock and all indvidual clock locks held */
+unsigned long clk_get_rate_all_locked(struct clk *c)
 {
-	unsigned long flags;
-	unsigned long ret;
-
-	spin_lock_irqsave(&clock_lock, flags);
+	u64 rate;
+	int mul = 1;
+	int div = 1;
+	struct clk *p = c;
+
+	while (p) {
+		c = p;
+		if (c->mul != 0 && c->div != 0) {
+			mul *= c->mul;
+			div *= c->div;
+		}
+		p = c->parent;
+	}
 
-	ret = c->rate;
+	rate = c->rate;
+	rate *= mul;
+	do_div(rate, div);
 
-	spin_unlock_irqrestore(&clock_lock, flags);
-	return ret;
+	return rate;
 }
-EXPORT_SYMBOL(clk_get_rate);
 
 long clk_round_rate(struct clk *c, unsigned long rate)
 {
-	if (!c->ops || !c->ops->round_rate)
-		return -ENOSYS;
+	unsigned long flags;
+	long ret;
+
+	clk_lock_save(c, flags);
+
+	if (!c->ops || !c->ops->round_rate) {
+		ret = -ENOSYS;
+		goto out;
+	}
 
 	if (rate > c->max_rate)
 		rate = c->max_rate;
 
-	return c->ops->round_rate(c, rate);
+	ret = c->ops->round_rate(c, rate);
+
+out:
+	clk_unlock_restore(c, flags);
+	return ret;
 }
 EXPORT_SYMBOL(clk_round_rate);
 
@@ -459,32 +507,51 @@ EXPORT_SYMBOL(tegra_periph_reset_assert);
 void __init tegra_init_clock(void)
 {
 	tegra2_init_clocks();
+	tegra2_init_dvfs();
 }
 
+/*
+ * Iterate through all clocks, setting the dvfs rate to the current clock
+ * rate on all auto dvfs clocks, and to the saved dvfs rate on all manual
+ * dvfs clocks.  Used to enable dvfs during late init, after the regulators
+ * are available.
+ */
 void __init tegra_clk_set_dvfs_rates(void)
 {
+	unsigned long flags;
 	struct clk *c;
+
+	mutex_lock(&clock_list_lock);
+
 	list_for_each_entry(c, &clocks, node) {
+		clk_lock_save(c, flags);
 		if (clk_is_auto_dvfs(c)) {
 			if (c->refcnt > 0)
-				tegra_dvfs_set_rate(c, c->rate);
+				tegra_dvfs_set_rate(c, clk_get_rate_locked(c));
 			else
 				tegra_dvfs_set_rate(c, 0);
 		} else if (clk_is_dvfs(c)) {
 			tegra_dvfs_set_rate(c, c->dvfs_rate);
 		}
+		clk_unlock_restore(c, flags);
 	}
+
+	mutex_unlock(&clock_list_lock);
 }
 
+/*
+ * Iterate through all clocks, disabling any for which the refcount is 0
+ * but the clock init detected the bootloader left the clock on.
+ */
 int __init tegra_disable_boot_clocks(void)
 {
 	unsigned long flags;
 	struct clk *c;
 
-	lock_dvfs();
-	spin_lock_irqsave(&clock_lock, flags);
+	mutex_lock(&clock_list_lock);
 
 	list_for_each_entry(c, &clocks, node) {
+		clk_lock_save(c, flags);
 		if (c->refcnt == 0 && c->state == ON &&
 				c->ops && c->ops->disable) {
 			pr_warning("Disabling clock %s left on by bootloader\n",
@@ -492,15 +559,134 @@ int __init tegra_disable_boot_clocks(void)
 			c->ops->disable(c);
 			c->state = OFF;
 		}
+		clk_unlock_restore(c, flags);
 	}
 
-	spin_unlock_irqrestore(&clock_lock, flags);
-	unlock_dvfs();
+	mutex_unlock(&clock_list_lock);
 	return 0;
 }
-late_initcall(tegra_disable_boot_clocks);
+
+int __init tegra_late_init_clock(void)
+{
+	tegra_disable_boot_clocks();
+	tegra_clk_set_dvfs_rates();
+	return 0;
+}
+late_initcall(tegra_late_init_clock);
 
 #ifdef CONFIG_DEBUG_FS
+
+/*
+ * Attempt to lock all the clocks that are marked cansleep
+ * Must be called with irqs enabled
+ */
+static int __clk_lock_all_mutexes(void)
+{
+	struct clk *c;
+
+	might_sleep();
+
+	list_for_each_entry(c, &clocks, node)
+		if (clk_cansleep(c))
+			if (!mutex_trylock(&c->mutex))
+				goto unlock_mutexes;
+
+	return 0;
+
+unlock_mutexes:
+	list_for_each_entry_continue_reverse(c, &clocks, node)
+		if (clk_cansleep(c))
+			mutex_unlock(&c->mutex);
+
+	return -EAGAIN;
+}
+
+/*
+ * Attempt to lock all the clocks that are not marked cansleep
+ * Must be called with irqs disabled
+ */
+static int __clk_lock_all_spinlocks(void)
+{
+	struct clk *c;
+
+	list_for_each_entry(c, &clocks, node)
+		if (!clk_cansleep(c))
+			if (!spin_trylock(&c->spinlock))
+				goto unlock_spinlocks;
+
+	return 0;
+
+unlock_spinlocks:
+	list_for_each_entry_continue_reverse(c, &clocks, node)
+		if (!clk_cansleep(c))
+			spin_unlock(&c->spinlock);
+
+	return -EAGAIN;
+}
+
+static void __clk_unlock_all_mutexes(void)
+{
+	struct clk *c;
+
+	list_for_each_entry_reverse(c, &clocks, node)
+		if (clk_cansleep(c))
+			mutex_unlock(&c->mutex);
+}
+
+static void __clk_unlock_all_spinlocks(void)
+{
+	struct clk *c;
+
+	list_for_each_entry_reverse(c, &clocks, node)
+		if (!clk_cansleep(c))
+			spin_unlock(&c->spinlock);
+}
+
+/*
+ * This function retries until it can take all locks, and may take
+ * an arbitrarily long time to complete.
+ * Must be called with irqs enabled, returns with irqs disabled
+ * Must be called with clock_list_lock held
+ */
+static void clk_lock_all(void)
+{
+	int ret;
+retry:
+	ret = __clk_lock_all_mutexes();
+	if (ret)
+		goto failed_mutexes;
+
+	local_irq_disable();
+
+	ret = __clk_lock_all_spinlocks();
+	if (ret)
+		goto failed_spinlocks;
+
+	/* All locks taken successfully, return */
+	return;
+
+failed_spinlocks:
+	local_irq_enable();
+	__clk_unlock_all_mutexes();
+failed_mutexes:
+	msleep(1);
+	goto retry;
+}
+
+/*
+ * Unlocks all clocks after a clk_lock_all
+ * Must be called with irqs disabled, returns with irqs enabled
+ * Must be called with clock_list_lock held
+ */
+static void clk_unlock_all(void)
+{
+	__clk_unlock_all_spinlocks();
+
+	local_irq_enable();
+
+	__clk_unlock_all_mutexes();
+}
+
 static struct dentry *clk_debugfs_root;
 
 static void dvfs_show_one(struct seq_file *s, struct dvfs *d, int level)
@@ -515,7 +701,6 @@ static void dvfs_show_one(struct seq_file *s, struct dvfs *d, int level)
 static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 {
 	struct clk *child;
-	struct clk *safe;
 	struct dvfs *d;
 	const char *state = "uninit";
 	char div[8] = {0};
@@ -547,12 +732,15 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 		c->rate > c->max_rate ? '!' : ' ',
 		!c->set ? '*' : ' ',
 		30 - level * 3, c->name,
-		state, c->refcnt, div, c->rate);
+		state, c->refcnt, div, clk_get_rate_all_locked(c));
 
 	list_for_each_entry(d, &c->dvfs, node)
 		dvfs_show_one(s, d, level + 1);
 
-	list_for_each_entry_safe(child, safe, &c->children, sibling) {
+	list_for_each_entry(child, &clocks, node) {
+		if (child->parent != c)
+			continue;
+
 		clock_tree_show_one(s, child, level + 1);
 	}
 }
@@ -560,14 +748,20 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 static int clock_tree_show(struct seq_file *s, void *data)
 {
 	struct clk *c;
-	unsigned long flags;
 	seq_printf(s, "   clock                          state  ref div      rate\n");
 	seq_printf(s, "--------------------------------------------------------------\n");
-	spin_lock_irqsave(&clock_lock, flags);
+
+	mutex_lock(&clock_list_lock);
+
+	clk_lock_all();
+
 	list_for_each_entry(c, &clocks, node)
 		if (c->parent == NULL)
 			clock_tree_show_one(s, c, 0);
-	spin_unlock_irqrestore(&clock_lock, flags);
+
+	clk_unlock_all();
+
+	mutex_unlock(&clock_list_lock);
 	return 0;
 }
 
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index b13aab47083e..0bcf475b3c82 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -21,6 +21,8 @@
 #define __MACH_TEGRA_CLOCK_H
 
 #include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
 #include <asm/clkdev.h>
 
 #define DIV_BUS			(1 << 0)
@@ -75,8 +77,6 @@ enum clk_state {
 struct clk {
 	/* node for master clocks list */
 	struct list_head	node;		/* node for list of all clocks */
-	struct list_head	children;	/* list of children */
-	struct list_head	sibling;	/* node for children */
 	struct list_head	dvfs;		/* list of dvfs dependencies */
 	struct clk_lookup	lookup;
 
@@ -91,11 +91,11 @@ struct clk {
 	unsigned long		max_rate;
 	bool			is_dvfs;
 	bool			auto_dvfs;
+	bool			cansleep;
 	u32			flags;
 	const char		*name;
 
 	u32			refcnt;
-	unsigned long		requested_rate;
 	enum clk_state		state;
 	struct clk		*parent;
 	u32			div;
@@ -137,8 +137,10 @@ struct clk {
 			unsigned long			rate;
 		} shared_bus_user;
 	} u;
-};
 
+	struct mutex mutex;
+	spinlock_t spinlock;
+};
 
 struct clk_duplicate {
 	const char *name;
@@ -158,12 +160,10 @@ void tegra2_periph_reset_assert(struct clk *c);
 void clk_init(struct clk *clk);
 struct clk *tegra_get_clock_by_name(const char *name);
 unsigned long clk_measure_input_freq(void);
-void clk_disable_locked(struct clk *c);
-int clk_enable_locked(struct clk *c);
-int clk_set_parent_locked(struct clk *c, struct clk *parent);
-int clk_set_rate_locked(struct clk *c, unsigned long rate);
 int clk_reparent(struct clk *c, struct clk *parent);
 void tegra_clk_init_from_table(struct tegra_clk_init_table *table);
 void tegra_clk_set_dvfs_rates(void);
+void clk_set_cansleep(struct clk *c);
+unsigned long clk_get_rate_locked(struct clk *c);
 
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index 04ff7b672ad8..f96f8c7c53ee 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -26,5 +26,6 @@ void tegra_periph_reset_deassert(struct clk *c);
 void tegra_periph_reset_assert(struct clk *c);
 
 int tegra_dvfs_set_rate(struct clk *c, unsigned long rate);
+unsigned long clk_get_rate_all_locked(struct clk *c);
 
 #endif
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 7f5ed03fa958..8bc46e7e0988 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -23,7 +23,7 @@
 #include <linux/spinlock.h>
 #include <linux/delay.h>
 #include <linux/io.h>
-#include <linux/hrtimer.h>
+#include <linux/clk.h>
 
 #include <asm/clkdev.h>
 
@@ -337,12 +337,12 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
 			val |= sel->value << shift;
 
 			if (c->refcnt)
-				clk_enable_locked(p);
+				clk_enable(p);
 
 			clk_writel(val, c->reg);
 
 			if (c->refcnt && c->parent)
-				clk_disable_locked(c->parent);
+				clk_disable(c->parent);
 
 			clk_reparent(c, p);
 			return 0;
@@ -389,31 +389,31 @@ static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
 	 * Take an extra reference to the main pll so it doesn't turn
 	 * off when we move the cpu off of it
 	 */
-	clk_enable_locked(c->u.cpu.main);
+	clk_enable(c->u.cpu.main);
 
-	ret = clk_set_parent_locked(c->parent, c->u.cpu.backup);
+	ret = clk_set_parent(c->parent, c->u.cpu.backup);
 	if (ret) {
 		pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
 		goto out;
 	}
 
-	if (rate == c->u.cpu.backup->rate)
+	if (rate == clk_get_rate(c->u.cpu.backup))
 		goto out;
 
-	ret = clk_set_rate_locked(c->u.cpu.main, rate);
+	ret = clk_set_rate(c->u.cpu.main, rate);
 	if (ret) {
 		pr_err("Failed to change cpu pll to %lu\n", rate);
 		goto out;
 	}
 
-	ret = clk_set_parent_locked(c->parent, c->u.cpu.main);
+	ret = clk_set_parent(c->parent, c->u.cpu.main);
 	if (ret) {
 		pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
 		goto out;
 	}
 
 out:
-	clk_disable_locked(c->u.cpu.main);
+	clk_disable(c->u.cpu.main);
 	return ret;
 }
 
@@ -465,7 +465,7 @@ static void tegra2_bus_clk_disable(struct clk *c)
 static int tegra2_bus_clk_set_rate(struct clk *c, unsigned long rate)
 {
 	u32 val = clk_readl(c->reg);
-	unsigned long parent_rate = c->parent->rate;
+	unsigned long parent_rate = clk_get_rate(c->parent);
 	int i;
 	for (i = 1; i <= 4; i++) {
 		if (rate == parent_rate / i) {
@@ -539,14 +539,15 @@ static void tegra2_blink_clk_disable(struct clk *c)
 
 static int tegra2_blink_clk_set_rate(struct clk *c, unsigned long rate)
 {
-	if (rate >= c->parent->rate) {
+	unsigned long parent_rate = clk_get_rate(c->parent);
+	if (rate >= parent_rate) {
 		c->div = 1;
 		pmc_writel(0, c->reg);
 	} else {
 		unsigned int on_off;
 		u32 val;
 
-		on_off = DIV_ROUND_UP(c->parent->rate / 8, rate);
+		on_off = DIV_ROUND_UP(parent_rate / 8, rate);
 		c->div = on_off * 8;
 
 		val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
@@ -632,7 +633,7 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
 
 	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
 
-	input_rate = c->parent->rate;
+	input_rate = clk_get_rate(c->parent);
 	for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
 		if (sel->input_rate == input_rate && sel->output_rate == rate) {
 			c->mul = sel->n;
@@ -772,9 +773,11 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
 	u32 val;
 	u32 new_val;
 	int divider_u71;
+	unsigned long parent_rate = clk_get_rate(c->parent);
+
 	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
 	if (c->flags & DIV_U71) {
-		divider_u71 = clk_div71_get_divider(c->parent->rate, rate);
+		divider_u71 = clk_div71_get_divider(parent_rate, rate);
 		if (divider_u71 >= 0) {
 			val = clk_readl(c->reg);
 			new_val = val >> c->reg_shift;
@@ -792,7 +795,7 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
 			return 0;
 		}
 	} else if (c->flags & DIV_2) {
-		if (c->parent->rate == rate * 2)
+		if (parent_rate == rate * 2)
 			return 0;
 	}
 	return -EINVAL;
@@ -801,15 +804,16 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
 static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
 {
 	int divider;
+	unsigned long parent_rate = clk_get_rate(c->parent);
 	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
 
 	if (c->flags & DIV_U71) {
-		divider = clk_div71_get_divider(c->parent->rate, rate);
+		divider = clk_div71_get_divider(parent_rate, rate);
 		if (divider < 0)
 			return divider;
-		return c->parent->rate * 2 / (divider + 2);
+		return parent_rate * 2 / (divider + 2);
 	} else if (c->flags & DIV_2) {
-		return c->parent->rate / 2;
+		return parent_rate / 2;
 	}
 	return -EINVAL;
 }
@@ -923,12 +927,12 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
 			val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
 
 			if (c->refcnt)
-				clk_enable_locked(p);
+				clk_enable(p);
 
 			clk_writel(val, c->reg);
 
 			if (c->refcnt && c->parent)
-				clk_disable_locked(c->parent);
+				clk_disable(c->parent);
 
 			clk_reparent(c, p);
 			return 0;
@@ -942,9 +946,10 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
 {
 	u32 val;
 	int divider;
-	pr_debug("%s: %lu\n", __func__, rate);
+	unsigned long parent_rate = clk_get_rate(c->parent);
+
 	if (c->flags & DIV_U71) {
-		divider = clk_div71_get_divider(c->parent->rate, rate);
+		divider = clk_div71_get_divider(parent_rate, rate);
 		if (divider >= 0) {
 			val = clk_readl(c->reg);
 			val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
@@ -955,7 +960,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
 			return 0;
 		}
 	} else if (c->flags & DIV_U16) {
-		divider = clk_div16_get_divider(c->parent->rate, rate);
+		divider = clk_div16_get_divider(parent_rate, rate);
 		if (divider >= 0) {
 			val = clk_readl(c->reg);
 			val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
@@ -965,7 +970,7 @@ static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
 			c->mul = 1;
 			return 0;
 		}
-	} else if (c->parent->rate <= rate) {
+	} else if (parent_rate <= rate) {
 		c->div = 1;
 		c->mul = 1;
 		return 0;
@@ -977,19 +982,20 @@ static long tegra2_periph_clk_round_rate(struct clk *c,
 	unsigned long rate)
 {
 	int divider;
+	unsigned long parent_rate = clk_get_rate(c->parent);
 	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
 
 	if (c->flags & DIV_U71) {
-		divider = clk_div71_get_divider(c->parent->rate, rate);
+		divider = clk_div71_get_divider(parent_rate, rate);
 		if (divider < 0)
 			return divider;
 
-		return c->parent->rate * 2 / (divider + 2);
+		return parent_rate * 2 / (divider + 2);
 	} else if (c->flags & DIV_U16) {
-		divider = clk_div16_get_divider(c->parent->rate, rate);
+		divider = clk_div16_get_divider(parent_rate, rate);
 		if (divider < 0)
 			return divider;
-		return c->parent->rate / (divider + 1);
+		return parent_rate / (divider + 1);
 	}
 	return -EINVAL;
 }
@@ -1017,7 +1023,7 @@ static void tegra2_clk_double_init(struct clk *c)
 
 static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate)
 {
-	if (rate != 2 * c->parent->rate)
+	if (rate != 2 * clk_get_rate(c->parent))
 		return -EINVAL;
 	c->mul = 2;
 	c->div = 1;
@@ -1068,12 +1074,12 @@ static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
 			val |= sel->value;
 
 			if (c->refcnt)
-				clk_enable_locked(p);
+				clk_enable(p);
 
 			clk_writel(val, c->reg);
 
 			if (c->refcnt && c->parent)
-				clk_disable_locked(c->parent);
+				clk_disable(c->parent);
 
 			clk_reparent(c, p);
 			return 0;
@@ -1134,12 +1140,13 @@ static void tegra_clk_shared_bus_update(struct clk *bus)
 	struct clk *c;
 	unsigned long rate = bus->u.shared_bus.min_rate;
 
-	list_for_each_entry(c, &bus->u.shared_bus.list, u.shared_bus_user.node)
+	list_for_each_entry(c, &bus->u.shared_bus.list,
+			u.shared_bus_user.node) {
 		if (c->u.shared_bus_user.enabled)
 			rate = max(c->u.shared_bus_user.rate, rate);
+	}
 
-	if (rate != bus->rate)
-		clk_set_rate_locked(bus, rate);
+	clk_set_rate(bus, rate);
 };
 
 static void tegra_clk_shared_bus_init(struct clk *c)
-- 
2.34.1