[PATCH 7/9] [ARM] tegra: clock: Add dvfs support, bug fixes, and cleanups

Colin Cross ccross at google.com
Thu Jul 29 20:29:28 EDT 2010


From: Colin Cross <ccross at android.com>

- Add drivers to clock lookup table
- Add new pll_m entries
- Support I2C U16 divider
- Fix rate reporting on 32.768kHz clock
- Call propagate rate only if set_rate succeeds
- Add support for audio_sync clock
- Add 24MHz to PLLA frequency list
- Correct i2s1/2/spdifout mux
- Add suspend support
- Fix enable/disable parent clocks in set_parent
- Add max_rate parameter to all clocks
- DVFS support
- Add virtual cpu clock with dvfs
- Support clk_round_rate
- Fix requesting very high periph frequencies
- Add quirks for PLLU:
   PLLU is slightly different from the rest of the PLLs.  The
   lock enable bit is at bit 22 instead of 18 in the MISC
   register, and the post divider field is a single bit with
   reversed values from other PLLs.
- Simplify recalculating clock rates
- Fix UART divider flags
- Remove unused clock ops

Signed-off-by: Colin Cross <ccross at android.com>
---
 arch/arm/mach-tegra/Makefile           |    1 +
 arch/arm/mach-tegra/clock.c            |  267 ++++++++++-
 arch/arm/mach-tegra/clock.h            |   58 ++-
 arch/arm/mach-tegra/include/mach/clk.h |    5 +
 arch/arm/mach-tegra/tegra2_clocks.c    |  778 +++++++++++++++++++++++++-------
 arch/arm/mach-tegra/tegra2_dvfs.c      |   86 ++++
 arch/arm/mach-tegra/tegra2_dvfs.h      |   20 +
 7 files changed, 1005 insertions(+), 210 deletions(-)
 create mode 100644 arch/arm/mach-tegra/tegra2_dvfs.c
 create mode 100644 arch/arm/mach-tegra/tegra2_dvfs.h

diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 6203475..3642e58 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -8,6 +8,7 @@ obj-y                                   += pinmux.o
 obj-y					+= fuse.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clock.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
+obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)		+= pinmux-t2-tables.o
 obj-$(CONFIG_SMP)                       += platsmp.o localtimer.o headsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)               += hotplug.o
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 03ad578..ae19f95 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -24,13 +24,80 @@
 #include <linux/debugfs.h>
 #include <linux/slab.h>
 #include <linux/seq_file.h>
+#include <linux/regulator/consumer.h>
 #include <asm/clkdev.h>
 
 #include "clock.h"
+#include "board.h"
+#include "fuse.h"
 
 static LIST_HEAD(clocks);
 
 static DEFINE_SPINLOCK(clock_lock);
+static DEFINE_MUTEX(dvfs_lock);
+
+static int clk_is_dvfs(struct clk *c)
+{
+	return (c->dvfs != NULL);
+};
+
+static int dvfs_set_rate(struct dvfs *d, unsigned long rate)
+{
+	struct dvfs_table *t;
+
+	if (d->table == NULL)
+		return -ENODEV;
+
+	for (t = d->table; t->rate != 0; t++) {
+		if (rate <= t->rate) {
+			if (!d->reg)
+				return 0;
+
+			return regulator_set_voltage(d->reg,
+				t->millivolts * 1000,
+				d->max_millivolts * 1000);
+		}
+	}
+
+	return -EINVAL;
+}
+
+static void dvfs_init(struct clk *c)
+{
+	int process_id;
+	int i;
+	struct dvfs_table *table;
+
+	process_id = c->dvfs->cpu ? tegra_core_process_id() :
+		tegra_cpu_process_id();
+
+	for (i = 0; i < c->dvfs->process_id_table_length; i++)
+		if (process_id == c->dvfs->process_id_table[i].process_id)
+			c->dvfs->table = c->dvfs->process_id_table[i].table;
+
+	if (c->dvfs->table == NULL) {
+		pr_err("Failed to find dvfs table for clock %s process %d\n",
+			c->name, process_id);
+		return;
+	}
+
+	c->dvfs->max_millivolts = 0;
+	for (table = c->dvfs->table; table->rate != 0; table++)
+		if (c->dvfs->max_millivolts < table->millivolts)
+			c->dvfs->max_millivolts = table->millivolts;
+
+	c->dvfs->reg = regulator_get(NULL, c->dvfs->reg_id);
+
+	if (IS_ERR(c->dvfs->reg)) {
+		pr_err("Failed to get regulator %s for clock %s\n",
+			c->dvfs->reg_id, c->name);
+		c->dvfs->reg = NULL;
+		return;
+	}
+
+	if (c->refcnt > 0)
+		dvfs_set_rate(c->dvfs, c->rate);
+}
 
 struct clk *tegra_get_clock_by_name(const char *name)
 {
@@ -48,14 +115,31 @@ struct clk *tegra_get_clock_by_name(const char *name)
 	return ret;
 }
 
+static void clk_recalculate_rate(struct clk *c)
+{
+	u64 rate;
+
+	if (!c->parent)
+		return;
+
+	rate = c->parent->rate;
+
+	if (c->mul != 0 && c->div != 0) {
+		rate = rate * c->mul;
+		do_div(rate, c->div);
+	}
+
+	if (rate > c->max_rate)
+		pr_warn("clocks: Set clock %s to rate %llu, max is %lu\n",
+			c->name, rate, c->max_rate);
+
+	c->rate = rate;
+}
+
 int clk_reparent(struct clk *c, struct clk *parent)
 {
 	pr_debug("%s: %s\n", __func__, c->name);
-	if (c->refcnt && c->parent)
-		clk_disable_locked(c->parent);
 	c->parent = parent;
-	if (c->refcnt && c->parent)
-		clk_enable_locked(c->parent);
 	list_del(&c->sibling);
 	list_add_tail(&c->sibling, &parent->children);
 	return 0;
@@ -67,8 +151,7 @@ static void propagate_rate(struct clk *c)
 	pr_debug("%s: %s\n", __func__, c->name);
 	list_for_each_entry(clkp, &c->children, sibling) {
 		pr_debug("   %s\n", clkp->name);
-		if (clkp->ops->recalculate_rate)
-			clkp->ops->recalculate_rate(clkp);
+		clk_recalculate_rate(clkp);
 		propagate_rate(clkp);
 	}
 }
@@ -77,6 +160,8 @@ void clk_init(struct clk *c)
 {
 	unsigned long flags;
 
+	pr_debug("%s: %s\n", __func__, c->name);
+
 	spin_lock_irqsave(&clock_lock, flags);
 
 	INIT_LIST_HEAD(&c->children);
@@ -85,6 +170,8 @@ void clk_init(struct clk *c)
 	if (c->ops && c->ops->init)
 		c->ops->init(c);
 
+	clk_recalculate_rate(c);
+
 	list_add(&c->node, &clocks);
 
 	if (c->parent)
@@ -122,13 +209,38 @@ int clk_enable_locked(struct clk *c)
 	return 0;
 }
 
+int clk_enable_cansleep(struct clk *c)
+{
+	int ret;
+	unsigned long flags;
+
+	mutex_lock(&dvfs_lock);
+
+	if (clk_is_dvfs(c) && c->refcnt > 0)
+		dvfs_set_rate(c->dvfs, c->rate);
+
+	spin_lock_irqsave(&clock_lock, flags);
+	ret = clk_enable_locked(c);
+	spin_unlock_irqrestore(&clock_lock, flags);
+
+	mutex_unlock(&dvfs_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(clk_enable_cansleep);
+
 int clk_enable(struct clk *c)
 {
 	int ret;
 	unsigned long flags;
+
+	if (clk_is_dvfs(c))
+		BUG();
+
 	spin_lock_irqsave(&clock_lock, flags);
 	ret = clk_enable_locked(c);
 	spin_unlock_irqrestore(&clock_lock, flags);
+
 	return ret;
 }
 EXPORT_SYMBOL(clk_enable);
@@ -152,9 +264,30 @@ void clk_disable_locked(struct clk *c)
 	c->refcnt--;
 }
 
+void clk_disable_cansleep(struct clk *c)
+{
+	unsigned long flags;
+
+	mutex_lock(&dvfs_lock);
+
+	spin_lock_irqsave(&clock_lock, flags);
+	clk_disable_locked(c);
+	spin_unlock_irqrestore(&clock_lock, flags);
+
+	if (clk_is_dvfs(c) && c->refcnt == 0)
+		dvfs_set_rate(c->dvfs, c->rate);
+
+	mutex_unlock(&dvfs_lock);
+}
+EXPORT_SYMBOL(clk_disable_cansleep);
+
 void clk_disable(struct clk *c)
 {
 	unsigned long flags;
+
+	if (clk_is_dvfs(c))
+		BUG();
+
 	spin_lock_irqsave(&clock_lock, flags);
 	clk_disable_locked(c);
 	spin_unlock_irqrestore(&clock_lock, flags);
@@ -175,6 +308,8 @@ int clk_set_parent_locked(struct clk *c, struct clk *parent)
 	if (ret)
 		return ret;
 
+	clk_recalculate_rate(c);
+
 	propagate_rate(c);
 
 	return 0;
@@ -197,22 +332,69 @@ struct clk *clk_get_parent(struct clk *c)
 }
 EXPORT_SYMBOL(clk_get_parent);
 
-int clk_set_rate(struct clk *c, unsigned long rate)
+int clk_set_rate_locked(struct clk *c, unsigned long rate)
+{
+	int ret;
+
+	if (rate > c->max_rate)
+		rate = c->max_rate;
+
+	if (!c->ops || !c->ops->set_rate)
+		return -ENOSYS;
+
+	ret = c->ops->set_rate(c, rate);
+
+	if (ret)
+		return ret;
+
+	clk_recalculate_rate(c);
+
+	propagate_rate(c);
+
+	return 0;
+}
+
+int clk_set_rate_cansleep(struct clk *c, unsigned long rate)
 {
 	int ret = 0;
 	unsigned long flags;
 
+	pr_debug("%s: %s\n", __func__, c->name);
+
+	mutex_lock(&dvfs_lock);
+
+	if (rate > c->rate)
+		ret = dvfs_set_rate(c->dvfs, rate);
+	if (ret)
+		goto out;
+
 	spin_lock_irqsave(&clock_lock, flags);
+	ret = clk_set_rate_locked(c, rate);
+	spin_unlock_irqrestore(&clock_lock, flags);
 
-	pr_debug("%s: %s\n", __func__, c->name);
+	if (ret)
+		goto out;
 
-	if (c->ops && c->ops->set_rate)
-		ret = c->ops->set_rate(c, rate);
-	else
-		ret = -ENOSYS;
+	ret = dvfs_set_rate(c->dvfs, rate);
 
-	propagate_rate(c);
+out:
+	mutex_unlock(&dvfs_lock);
+	return ret;
+}
+EXPORT_SYMBOL(clk_set_rate_cansleep);
+
+int clk_set_rate(struct clk *c, unsigned long rate)
+{
+	int ret = 0;
+	unsigned long flags;
+
+	pr_debug("%s: %s\n", __func__, c->name);
+
+	if (clk_is_dvfs(c))
+		BUG();
 
+	spin_lock_irqsave(&clock_lock, flags);
+	ret = clk_set_rate_locked(c, rate);
 	spin_unlock_irqrestore(&clock_lock, flags);
 
 	return ret;
@@ -235,6 +417,20 @@ unsigned long clk_get_rate(struct clk *c)
 }
 EXPORT_SYMBOL(clk_get_rate);
 
+long clk_round_rate(struct clk *c, unsigned long rate)
+{
+	pr_debug("%s: %s\n", __func__, c->name);
+
+	if (!c->ops || !c->ops->round_rate)
+		return -ENOSYS;
+
+	if (rate > c->max_rate)
+		rate = c->max_rate;
+
+	return c->ops->round_rate(c, rate);
+}
+EXPORT_SYMBOL(clk_round_rate);
+
 static int tegra_clk_init_one_from_table(struct tegra_clk_init_table *table)
 {
 	struct clk *c;
@@ -308,13 +504,28 @@ void tegra_periph_reset_assert(struct clk *c)
 }
 EXPORT_SYMBOL(tegra_periph_reset_assert);
 
-int __init tegra_init_clock(void)
+void __init tegra_init_clock(void)
 {
 	tegra2_init_clocks();
+}
+
+int __init tegra_init_dvfs(void)
+{
+	struct clk *c, *safe;
+
+	mutex_lock(&dvfs_lock);
+
+	list_for_each_entry_safe(c, safe, &clocks, node)
+		if (c->dvfs)
+			dvfs_init(c);
+
+	mutex_unlock(&dvfs_lock);
 
 	return 0;
 }
 
+late_initcall(tegra_init_dvfs);
+
 #ifdef CONFIG_DEBUG_FS
 static struct dentry *clk_debugfs_root;
 
@@ -324,7 +535,7 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 	struct clk *child;
 	struct clk *safe;
 	const char *state = "uninit";
-	char div[5] = {0};
+	char div[8] = {0};
 
 	if (c->state == ON)
 		state = "on";
@@ -332,16 +543,26 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 		state = "off";
 
 	if (c->mul != 0 && c->div != 0) {
-		BUG_ON(c->mul > 2);
-		if (c->mul > c->div)
-			snprintf(div, sizeof(div), "x%d", c->mul / c->div);
-		else
+		if (c->mul > c->div) {
+			int mul = c->mul / c->div;
+			int mul2 = (c->mul * 10 / c->div) % 10;
+			int mul3 = (c->mul * 10) % c->div;
+			if (mul2 == 0 && mul3 == 0)
+				snprintf(div, sizeof(div), "x%d", mul);
+			else if (mul3 == 0)
+				snprintf(div, sizeof(div), "x%d.%d", mul, mul2);
+			else
+				snprintf(div, sizeof(div), "x%d.%d..", mul, mul2);
+		} else {
 			snprintf(div, sizeof(div), "%d%s", c->div / c->mul,
 				(c->div % c->mul) ? ".5" : "");
+		}
 	}
 
-	seq_printf(s, "%*s%-*s %-6s %-3d %-5s %-10lu\n",
-		level * 3 + 1, c->set ? "" : "*",
+	seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n",
+		level * 3 + 1, "",
+		c->rate > c->max_rate ? '!' : ' ',
+		!c->set ? '*' : ' ',
 		30 - level * 3, c->name,
 		state, c->refcnt, div, c->rate);
 	list_for_each_entry_safe(child, safe, &c->children, sibling) {
@@ -353,8 +574,8 @@ 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");
+	seq_printf(s, "   clock                          state  ref div      rate\n");
+	seq_printf(s, "--------------------------------------------------------------\n");
 	spin_lock_irqsave(&clock_lock, flags);
 	list_for_each_entry(c, &clocks, node)
 		if (c->parent == NULL)
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index af7c70e..94fd859 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -27,18 +27,43 @@
 #define DIV_U71			(1 << 1)
 #define DIV_U71_FIXED		(1 << 2)
 #define DIV_2			(1 << 3)
-#define PLL_FIXED		(1 << 4)
-#define PLL_HAS_CPCON		(1 << 5)
-#define MUX			(1 << 6)
-#define PLLD			(1 << 7)
-#define PERIPH_NO_RESET		(1 << 8)
-#define PERIPH_NO_ENB		(1 << 9)
-#define PERIPH_EMC_ENB		(1 << 10)
-#define PERIPH_MANUAL_RESET	(1 << 11)
-#define PLL_ALT_MISC_REG	(1 << 12)
+#define DIV_U16			(1 << 4)
+#define PLL_FIXED		(1 << 5)
+#define PLL_HAS_CPCON		(1 << 6)
+#define MUX			(1 << 7)
+#define PLLD			(1 << 8)
+#define PERIPH_NO_RESET		(1 << 9)
+#define PERIPH_NO_ENB		(1 << 10)
+#define PERIPH_EMC_ENB		(1 << 11)
+#define PERIPH_MANUAL_RESET	(1 << 12)
+#define PLL_ALT_MISC_REG	(1 << 13)
+#define PLLU			(1 << 14)
 #define ENABLE_ON_INIT		(1 << 28)
 
 struct clk;
+struct regulator;
+
+struct dvfs_table {
+	unsigned long rate;
+	int millivolts;
+};
+
+struct dvfs_process_id_table {
+	int process_id;
+	struct dvfs_table *table;
+};
+
+
+struct dvfs {
+	struct regulator *reg;
+	struct dvfs_table *table;
+	int max_millivolts;
+
+	int process_id_table_length;
+	const char *reg_id;
+	bool cpu;
+	struct dvfs_process_id_table process_id_table[];
+};
 
 struct clk_mux_sel {
 	struct clk	*input;
@@ -58,12 +83,9 @@ struct clk_ops {
 	void		(*init)(struct clk *);
 	int		(*enable)(struct clk *);
 	void		(*disable)(struct clk *);
-	void		(*recalc)(struct clk *);
 	int		(*set_parent)(struct clk *, struct clk *);
 	int		(*set_rate)(struct clk *, unsigned long);
-	unsigned long	(*get_rate)(struct clk *);
 	long		(*round_rate)(struct clk *, unsigned long);
-	unsigned long	(*recalculate_rate)(struct clk *);
 };
 
 enum clk_state {
@@ -85,6 +107,7 @@ struct clk {
 	struct clk			*parent;
 	struct clk_lookup		lookup;
 	unsigned long			rate;
+	unsigned long			max_rate;
 	u32				flags;
 	u32				refcnt;
 	const char			*name;
@@ -103,10 +126,6 @@ struct clk {
 	unsigned long			cf_max;
 	unsigned long			vco_min;
 	unsigned long			vco_max;
-	u32				m;
-	u32				n;
-	u32				p;
-	u32				cpcon;
 	const struct clk_pll_table	*pll_table;
 
 	/* DIV */
@@ -117,6 +136,12 @@ struct clk {
 	const struct clk_mux_sel	*inputs;
 	u32				sel;
 	u32				reg_mask;
+
+	/* Virtual cpu clock */
+	struct clk			*main;
+	struct clk			*backup;
+
+	struct dvfs			*dvfs;
 };
 
 
@@ -141,6 +166,7 @@ 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);
 
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index 2896f25..d772395 100644
--- a/arch/arm/mach-tegra/include/mach/clk.h
+++ b/arch/arm/mach-tegra/include/mach/clk.h
@@ -23,4 +23,9 @@
 void tegra_periph_reset_deassert(struct clk *c);
 void tegra_periph_reset_assert(struct clk *c);
 
+int clk_enable_cansleep(struct clk *clk);
+void clk_disable_cansleep(struct clk *clk);
+int clk_set_rate_cansleep(struct clk *clk, unsigned long rate);
+int clk_set_parent_cansleep(struct clk *clk, struct clk *parent);
+
 #endif
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 4261632..cf1c9d0 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -30,14 +30,21 @@
 #include <mach/iomap.h>
 
 #include "clock.h"
+#include "fuse.h"
+#include "tegra2_dvfs.h"
 
 #define RST_DEVICES			0x004
 #define RST_DEVICES_SET			0x300
 #define RST_DEVICES_CLR			0x304
+#define RST_DEVICES_NUM			3
 
 #define CLK_OUT_ENB			0x010
 #define CLK_OUT_ENB_SET			0x320
 #define CLK_OUT_ENB_CLR			0x324
+#define CLK_OUT_ENB_NUM			3
+
+#define CLK_MASK_ARM			0x44
+#define MISC_CLK_ENB			0x48
 
 #define OSC_CTRL			0x50
 #define OSC_CTRL_OSC_FREQ_MASK		(3<<30)
@@ -45,6 +52,7 @@
 #define OSC_CTRL_OSC_FREQ_19_2MHZ	(1<<30)
 #define OSC_CTRL_OSC_FREQ_12MHZ		(2<<30)
 #define OSC_CTRL_OSC_FREQ_26MHZ		(3<<30)
+#define OSC_CTRL_MASK			0x3f2
 
 #define OSC_FREQ_DET			0x58
 #define OSC_FREQ_DET_TRIG		(1<<31)
@@ -53,10 +61,17 @@
 #define OSC_FREQ_DET_BUSY		(1<<31)
 #define OSC_FREQ_DET_CNT_MASK		0xFFFF
 
+#define PERIPH_CLK_SOURCE_I2S1		0x100
+#define PERIPH_CLK_SOURCE_EMC		0x19c
+#define PERIPH_CLK_SOURCE_OSC		0x1fc
+#define PERIPH_CLK_SOURCE_NUM \
+	((PERIPH_CLK_SOURCE_OSC - PERIPH_CLK_SOURCE_I2S1) / 4)
+
 #define PERIPH_CLK_SOURCE_MASK		(3<<30)
 #define PERIPH_CLK_SOURCE_SHIFT		30
 #define PERIPH_CLK_SOURCE_ENABLE	(1<<28)
-#define PERIPH_CLK_SOURCE_DIV_MASK	0xFF
+#define PERIPH_CLK_SOURCE_DIVU71_MASK	0xFF
+#define PERIPH_CLK_SOURCE_DIVU16_MASK	0xFFFF
 #define PERIPH_CLK_SOURCE_DIV_SHIFT	0
 
 #define PLL_BASE			0x0
@@ -79,8 +94,9 @@
 #define PLL_OUT_RESET_DISABLE		(1<<0)
 
 #define PLL_MISC(c)			(((c)->flags & PLL_ALT_MISC_REG) ? 0x4 : 0xc)
+#define PLL_MISC_LOCK_ENABLE(c)		(((c)->flags & PLLU) ? (1<<22) : (1<<18))
+
 #define PLL_MISC_DCCON_SHIFT		20
-#define PLL_MISC_LOCK_ENABLE		(1<<18)
 #define PLL_MISC_CPCON_SHIFT		8
 #define PLL_MISC_CPCON_MASK		(0xF<<PLL_MISC_CPCON_SHIFT)
 #define PLL_MISC_LFCON_SHIFT		4
@@ -88,6 +104,8 @@
 #define PLL_MISC_VCOCON_SHIFT		0
 #define PLL_MISC_VCOCON_MASK		(0xF<<PLL_MISC_VCOCON_SHIFT)
 
+#define PLLU_BASE_POST_DIV		(1<<20)
+
 #define PLLD_MISC_CLKENABLE		(1<<30)
 #define PLLD_MISC_DIV_RST		(1<<23)
 #define PLLD_MISC_DCCON_SHIFT		12
@@ -143,30 +161,37 @@ unsigned long clk_measure_input_freq(void)
 	}
 }
 
-static int clk_div71_get_divider(struct clk *c, unsigned long rate)
+static int clk_div71_get_divider(unsigned long parent_rate, unsigned long rate)
 {
-	unsigned long divider_u71;
+	s64 divider_u71 = parent_rate * 2;
+	divider_u71 += rate - 1;
+	do_div(divider_u71, rate);
 
-	divider_u71 = DIV_ROUND_UP(c->rate * 2, rate);
+	if (divider_u71 - 2 < 0)
+		return 0;
 
-	if (divider_u71 - 2 > 255 || divider_u71 - 2 < 0)
+	if (divider_u71 - 2 > 255)
 		return -EINVAL;
 
 	return divider_u71 - 2;
 }
 
-static unsigned long tegra2_clk_recalculate_rate(struct clk *c)
+static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
 {
-	unsigned long rate;
-	rate = c->parent->rate;
+	s64 divider_u16;
 
-	if (c->mul != 0 && c->div != 0)
-		c->rate = rate * c->mul / c->div;
-	else
-		c->rate = rate;
-	return c->rate;
-}
+	divider_u16 = parent_rate;
+	divider_u16 += rate - 1;
+	do_div(divider_u16, rate);
 
+	if (divider_u16 - 1 < 0)
+		return 0;
+
+	if (divider_u16 - 1 > 255)
+		return -EINVAL;
+
+	return divider_u16 - 1;
+}
 
 /* clk_m functions */
 static unsigned long tegra2_clk_m_autodetect_rate(struct clk *c)
@@ -244,7 +269,6 @@ static void tegra2_super_clk_init(struct clk *c)
 	}
 	BUG_ON(sel->input == NULL);
 	c->parent = sel->input;
-	tegra2_clk_recalculate_rate(c);
 }
 
 static int tegra2_super_clk_enable(struct clk *c)
@@ -266,6 +290,7 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
 	u32 val;
 	const struct clk_mux_sel *sel;
 	int shift;
+
 	val = clk_readl(c->reg + SUPER_CLK_MUX);;
 	BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
 		((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
@@ -273,11 +298,18 @@ static int tegra2_super_clk_set_parent(struct clk *c, struct clk *p)
 		SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
 	for (sel = c->inputs; sel->input != NULL; sel++) {
 		if (sel->input == p) {
-			clk_reparent(c, p);
 			val &= ~(SUPER_SOURCE_MASK << shift);
 			val |= sel->value << shift;
+
+			if (c->refcnt)
+				clk_enable_locked(p);
+
 			clk_writel(val, c->reg);
-			c->rate = c->parent->rate;
+
+			if (c->refcnt && c->parent)
+				clk_disable_locked(c->parent);
+
+			clk_reparent(c, p);
 			return 0;
 		}
 	}
@@ -289,7 +321,61 @@ static struct clk_ops tegra_super_ops = {
 	.enable			= tegra2_super_clk_enable,
 	.disable		= tegra2_super_clk_disable,
 	.set_parent		= tegra2_super_clk_set_parent,
-	.recalculate_rate	= tegra2_clk_recalculate_rate,
+};
+
+/* virtual cpu clock functions */
+/* some clocks can not be stopped (cpu, memory bus) while the SoC is running.
+   To change the frequency of these clocks, the parent pll may need to be
+   reprogrammed, so the clock must be moved off the pll, the pll reprogrammed,
+   and then the clock moved back to the pll.  To hide this sequence, a virtual
+   clock handles it.
+ */
+static void tegra2_cpu_clk_init(struct clk *c)
+{
+}
+
+static int tegra2_cpu_clk_enable(struct clk *c)
+{
+	return 0;
+}
+
+static void tegra2_cpu_clk_disable(struct clk *c)
+{
+	pr_debug("%s on clock %s\n", __func__, c->name);
+
+	/* oops - don't disable the CPU clock! */
+	BUG();
+}
+
+static int tegra2_cpu_clk_set_rate(struct clk *c, unsigned long rate)
+{
+	int ret;
+	ret = clk_set_parent_locked(c->parent, c->backup);
+	if (ret) {
+		pr_err("Failed to switch cpu to clock %s\n", c->backup->name);
+		return ret;
+	}
+
+	ret = clk_set_rate_locked(c->main, rate);
+	if (ret) {
+		pr_err("Failed to change cpu pll to %lu\n", rate);
+		return ret;
+	}
+
+	ret = clk_set_parent_locked(c->parent, c->main);
+	if (ret) {
+		pr_err("Failed to switch cpu to clock %s\n", c->main->name);
+		return ret;
+	}
+
+	return 0;
+}
+
+static struct clk_ops tegra_cpu_ops = {
+	.init     = tegra2_cpu_clk_init,
+	.enable   = tegra2_cpu_clk_enable,
+	.disable  = tegra2_cpu_clk_disable,
+	.set_rate = tegra2_cpu_clk_set_rate,
 };
 
 /* bus clock functions */
@@ -299,7 +385,6 @@ static void tegra2_bus_clk_init(struct clk *c)
 	c->state = ((val >> c->reg_shift) & BUS_CLK_DISABLE) ? OFF : ON;
 	c->div = ((val >> c->reg_shift) & BUS_CLK_DIV_MASK) + 1;
 	c->mul = 1;
-	tegra2_clk_recalculate_rate(c);
 }
 
 static int tegra2_bus_clk_enable(struct clk *c)
@@ -340,27 +425,15 @@ static struct clk_ops tegra_bus_ops = {
 	.enable			= tegra2_bus_clk_enable,
 	.disable		= tegra2_bus_clk_disable,
 	.set_rate		= tegra2_bus_clk_set_rate,
-	.recalculate_rate	= tegra2_clk_recalculate_rate,
 };
 
 /* PLL Functions */
-static unsigned long tegra2_pll_clk_recalculate_rate(struct clk *c)
-{
-	u64 rate;
-	rate = c->parent->rate;
-	rate *= c->n;
-	do_div(rate, c->m);
-	if (c->p == 2)
-		rate >>= 1;
-	c->rate = rate;
-	return c->rate;
-}
-
 static int tegra2_pll_clk_wait_for_lock(struct clk *c)
 {
 	ktime_t before;
 
 	before = ktime_get();
+
 	while (!(clk_readl(c->reg + PLL_BASE) & PLL_BASE_LOCK)) {
 		if (ktime_us_delta(ktime_get(), before) > 5000) {
 			pr_err("Timed out waiting for lock bit on pll %s",
@@ -380,24 +453,19 @@ static void tegra2_pll_clk_init(struct clk *c)
 
 	if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
 		pr_warning("Clock %s has unknown fixed frequency\n", c->name);
-		c->n = 1;
-		c->m = 0;
-		c->p = 1;
+		c->mul = 1;
+		c->div = 1;
 	} else if (val & PLL_BASE_BYPASS) {
-		c->n = 1;
-		c->m = 1;
-		c->p = 1;
+		c->mul = 1;
+		c->div = 1;
 	} else {
-		c->n = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT;
-		c->m = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT;
-		c->p = (val & PLL_BASE_DIVP_MASK) ? 2 : 1;
+		c->mul = (val & PLL_BASE_DIVN_MASK) >> PLL_BASE_DIVN_SHIFT;
+		c->div = (val & PLL_BASE_DIVM_MASK) >> PLL_BASE_DIVM_SHIFT;
+		if (c->flags & PLLU)
+			c->div *= (val & PLLU_BASE_POST_DIV) ? 1 : 2;
+		else
+			c->div *= (val & PLL_BASE_DIVP_MASK) ? 2 : 1;
 	}
-
-	val = clk_readl(c->reg + PLL_MISC(c));
-	if (c->flags & PLL_HAS_CPCON)
-		c->cpcon = (val & PLL_MISC_CPCON_MASK) >> PLL_MISC_CPCON_SHIFT;
-
-	tegra2_pll_clk_recalculate_rate(c);
 }
 
 static int tegra2_pll_clk_enable(struct clk *c)
@@ -411,7 +479,7 @@ static int tegra2_pll_clk_enable(struct clk *c)
 	clk_writel(val, c->reg + PLL_BASE);
 
 	val = clk_readl(c->reg + PLL_MISC(c));
-	val |= PLL_MISC_LOCK_ENABLE;
+	val |= PLL_MISC_LOCK_ENABLE(c);
 	clk_writel(val, c->reg + PLL_MISC(c));
 
 	tegra2_pll_clk_wait_for_lock(c);
@@ -441,33 +509,36 @@ static int tegra2_pll_clk_set_rate(struct clk *c, unsigned long rate)
 	input_rate = c->parent->rate;
 	for (sel = c->pll_table; sel->input_rate != 0; sel++) {
 		if (sel->input_rate == input_rate && sel->output_rate == rate) {
-			c->n = sel->n;
-			c->m = sel->m;
-			c->p = sel->p;
-			c->cpcon = sel->cpcon;
+			c->mul = sel->n;
+			c->div = sel->m * sel->p;
 
 			val = clk_readl(c->reg + PLL_BASE);
 			if (c->flags & PLL_FIXED)
 				val |= PLL_BASE_OVERRIDE;
 			val &= ~(PLL_BASE_DIVP_MASK | PLL_BASE_DIVN_MASK |
 				 PLL_BASE_DIVM_MASK);
-			val |= (c->m << PLL_BASE_DIVM_SHIFT) |
-				(c->n << PLL_BASE_DIVN_SHIFT);
-			BUG_ON(c->p > 2);
-			if (c->p == 2)
-				val |= 1 << PLL_BASE_DIVP_SHIFT;
+			val |= (sel->m << PLL_BASE_DIVM_SHIFT) |
+				(sel->n << PLL_BASE_DIVN_SHIFT);
+			BUG_ON(sel->p < 1 || sel->p > 2);
+			if (c->flags & PLLU) {
+				if (sel->p == 1)
+					val |= PLLU_BASE_POST_DIV;
+			} else {
+				if (sel->p == 2)
+					val |= 1 << PLL_BASE_DIVP_SHIFT;
+			}
 			clk_writel(val, c->reg + PLL_BASE);
 
 			if (c->flags & PLL_HAS_CPCON) {
-				val = c->cpcon << PLL_MISC_CPCON_SHIFT;
-				val |= PLL_MISC_LOCK_ENABLE;
+				val = clk_readl(c->reg + PLL_MISC(c));
+				val &= ~PLL_MISC_CPCON_MASK;
+				val |= sel->cpcon << PLL_MISC_CPCON_SHIFT;
 				clk_writel(val, c->reg + PLL_MISC(c));
 			}
 
 			if (c->state == ON)
 				tegra2_pll_clk_enable(c);
 
-			c->rate = rate;
 			return 0;
 		}
 	}
@@ -479,7 +550,21 @@ static struct clk_ops tegra_pll_ops = {
 	.enable			= tegra2_pll_clk_enable,
 	.disable		= tegra2_pll_clk_disable,
 	.set_rate		= tegra2_pll_clk_set_rate,
-	.recalculate_rate	= tegra2_pll_clk_recalculate_rate,
+};
+
+static void tegra2_pllx_clk_init(struct clk *c)
+{
+	tegra2_pll_clk_init(c);
+
+	if (tegra_sku_id() == 7)
+		c->max_rate = 750000000;
+}
+
+static struct clk_ops tegra_pllx_ops = {
+	.init     = tegra2_pllx_clk_init,
+	.enable   = tegra2_pll_clk_enable,
+	.disable  = tegra2_pll_clk_disable,
+	.set_rate = tegra2_pll_clk_set_rate,
 };
 
 /* Clock divider ops */
@@ -503,8 +588,6 @@ static void tegra2_pll_div_clk_init(struct clk *c)
 		c->div = 1;
 		c->mul = 1;
 	}
-
-	tegra2_clk_recalculate_rate(c);
 }
 
 static int tegra2_pll_div_clk_enable(struct clk *c)
@@ -565,7 +648,7 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
 	int divider_u71;
 	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
 	if (c->flags & DIV_U71) {
-		divider_u71 = clk_div71_get_divider(c->parent, rate);
+		divider_u71 = clk_div71_get_divider(c->parent->rate, rate);
 		if (divider_u71 >= 0) {
 			val = clk_readl(c->reg);
 			new_val = val >> c->reg_shift;
@@ -580,25 +663,37 @@ static int tegra2_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
 			clk_writel(val, c->reg);
 			c->div = divider_u71 + 2;
 			c->mul = 2;
-			tegra2_clk_recalculate_rate(c);
 			return 0;
 		}
 	} else if (c->flags & DIV_2) {
-		if (c->parent->rate == rate * 2) {
-			c->rate = rate;
+		if (c->parent->rate == rate * 2)
 			return 0;
-		}
 	}
 	return -EINVAL;
 }
 
+static long tegra2_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
+{
+	int divider;
+	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+	if (c->flags & DIV_U71) {
+		divider = clk_div71_get_divider(c->parent->rate, rate);
+		if (divider < 0)
+			return divider;
+		return c->parent->rate * 2 / (divider + 2);
+	} else if (c->flags & DIV_2) {
+		return c->parent->rate / 2;
+	}
+	return -EINVAL;
+}
 
 static struct clk_ops tegra_pll_div_ops = {
 	.init			= tegra2_pll_div_clk_init,
 	.enable			= tegra2_pll_div_clk_enable,
 	.disable		= tegra2_pll_div_clk_disable,
 	.set_rate		= tegra2_pll_div_clk_set_rate,
-	.recalculate_rate	= tegra2_clk_recalculate_rate,
+	.round_rate		= tegra2_pll_div_clk_round_rate,
 };
 
 /* Periph clk ops */
@@ -621,9 +716,13 @@ static void tegra2_periph_clk_init(struct clk *c)
 	}
 
 	if (c->flags & DIV_U71) {
-		u32 divu71 = val & PERIPH_CLK_SOURCE_DIV_MASK;
+		u32 divu71 = val & PERIPH_CLK_SOURCE_DIVU71_MASK;
 		c->div = divu71 + 2;
 		c->mul = 2;
+	} else if (c->flags & DIV_U16) {
+		u32 divu16 = val & PERIPH_CLK_SOURCE_DIVU16_MASK;
+		c->div = divu16 + 1;
+		c->mul = 1;
 	} else {
 		c->div = 1;
 		c->mul = 1;
@@ -637,7 +736,6 @@ static void tegra2_periph_clk_init(struct clk *c)
 		if (clk_readl(RST_DEVICES + PERIPH_CLK_TO_ENB_REG(c)) &
 				PERIPH_CLK_TO_ENB_BIT(c))
 			c->state = OFF;
-	tegra2_clk_recalculate_rate(c);
 }
 
 static int tegra2_periph_clk_enable(struct clk *c)
@@ -692,12 +790,19 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
 	pr_debug("%s: %s %s\n", __func__, c->name, p->name);
 	for (sel = c->inputs; sel->input != NULL; sel++) {
 		if (sel->input == p) {
-			clk_reparent(c, p);
 			val = clk_readl(c->reg);
 			val &= ~PERIPH_CLK_SOURCE_MASK;
 			val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT;
+
+			if (c->refcnt)
+				clk_enable_locked(p);
+
 			clk_writel(val, c->reg);
-			c->rate = c->parent->rate;
+
+			if (c->refcnt && c->parent)
+				clk_disable_locked(c->parent);
+
+			clk_reparent(c, p);
 			return 0;
 		}
 	}
@@ -708,20 +813,55 @@ static int tegra2_periph_clk_set_parent(struct clk *c, struct clk *p)
 static int tegra2_periph_clk_set_rate(struct clk *c, unsigned long rate)
 {
 	u32 val;
-	int divider_u71;
+	int divider;
 	pr_debug("%s: %lu\n", __func__, rate);
 	if (c->flags & DIV_U71) {
-		divider_u71 = clk_div71_get_divider(c->parent, rate);
-		if (divider_u71 >= 0) {
+		divider = clk_div71_get_divider(c->parent->rate, rate);
+		if (divider >= 0) {
 			val = clk_readl(c->reg);
-			val &= ~PERIPH_CLK_SOURCE_DIV_MASK;
-			val |= divider_u71;
+			val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
+			val |= divider;
 			clk_writel(val, c->reg);
-			c->div = divider_u71 + 2;
+			c->div = divider + 2;
 			c->mul = 2;
-			tegra2_clk_recalculate_rate(c);
 			return 0;
 		}
+	} else if (c->flags & DIV_U16) {
+		divider = clk_div16_get_divider(c->parent->rate, rate);
+		if (divider >= 0) {
+			val = clk_readl(c->reg);
+			val &= ~PERIPH_CLK_SOURCE_DIVU16_MASK;
+			val |= divider;
+			clk_writel(val, c->reg);
+			c->div = divider + 1;
+			c->mul = 1;
+			return 0;
+		}
+	} else if (c->parent->rate <= rate) {
+		c->div = 1;
+		c->mul = 1;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static long tegra2_periph_clk_round_rate(struct clk *c,
+	unsigned long rate)
+{
+	int divider;
+	pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+	if (c->flags & DIV_U71) {
+		divider = clk_div71_get_divider(c->parent->rate, rate);
+		if (divider < 0)
+			return divider;
+
+		return c->parent->rate * 2 / (divider + 2);
+	} else if (c->flags & DIV_U16) {
+		divider = clk_div16_get_divider(c->parent->rate, rate);
+		if (divider < 0)
+			return divider;
+		return c->parent->rate / (divider + 1);
 	}
 	return -EINVAL;
 }
@@ -732,7 +872,7 @@ static struct clk_ops tegra_periph_clk_ops = {
 	.disable		= &tegra2_periph_clk_disable,
 	.set_parent		= &tegra2_periph_clk_set_parent,
 	.set_rate		= &tegra2_periph_clk_set_rate,
-	.recalculate_rate	= &tegra2_clk_recalculate_rate,
+	.round_rate		= &tegra2_periph_clk_round_rate,
 };
 
 /* Clock doubler ops */
@@ -744,21 +884,108 @@ static void tegra2_clk_double_init(struct clk *c)
 	if (!(clk_readl(CLK_OUT_ENB + PERIPH_CLK_TO_ENB_REG(c)) &
 			PERIPH_CLK_TO_ENB_BIT(c)))
 		c->state = OFF;
-	tegra2_clk_recalculate_rate(c);
 };
 
+static int tegra2_clk_double_set_rate(struct clk *c, unsigned long rate)
+{
+	if (rate != 2 * c->parent->rate)
+		return -EINVAL;
+	c->mul = 2;
+	c->div = 1;
+	return 0;
+}
+
 static struct clk_ops tegra_clk_double_ops = {
 	.init			= &tegra2_clk_double_init,
 	.enable			= &tegra2_periph_clk_enable,
 	.disable		= &tegra2_periph_clk_disable,
-	.recalculate_rate	= &tegra2_clk_recalculate_rate,
+	.set_rate		= &tegra2_clk_double_set_rate,
+};
+
+static void tegra2_audio_sync_clk_init(struct clk *c)
+{
+	int source;
+	const struct clk_mux_sel *sel;
+	u32 val = clk_readl(c->reg);
+	c->state = (val & (1<<4)) ? OFF : ON;
+	source = val & 0xf;
+	for (sel = c->inputs; sel->input != NULL; sel++)
+		if (sel->value == source)
+			break;
+	BUG_ON(sel->input == NULL);
+	c->parent = sel->input;
+}
+
+static int tegra2_audio_sync_clk_enable(struct clk *c)
+{
+	clk_writel(0, c->reg);
+	return 0;
+}
+
+static void tegra2_audio_sync_clk_disable(struct clk *c)
+{
+	clk_writel(1, c->reg);
+}
+
+static int tegra2_audio_sync_clk_set_parent(struct clk *c, struct clk *p)
+{
+	u32 val;
+	const struct clk_mux_sel *sel;
+	for (sel = c->inputs; sel->input != NULL; sel++) {
+		if (sel->input == p) {
+			val = clk_readl(c->reg);
+			val &= ~0xf;
+			val |= sel->value;
+
+			if (c->refcnt)
+				clk_enable_locked(p);
+
+			clk_writel(val, c->reg);
+
+			if (c->refcnt && c->parent)
+				clk_disable_locked(c->parent);
+
+			clk_reparent(c, p);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int tegra2_audio_sync_clk_set_rate(struct clk *c, unsigned long rate)
+{
+	unsigned long parent_rate;
+	if (!c->parent) {
+		pr_err("%s: clock has no parent\n", __func__);
+		return -EINVAL;
+	}
+	parent_rate = c->parent->rate;
+	if (rate != parent_rate) {
+		pr_err("%s: %s/%ld differs from parent %s/%ld\n",
+			__func__,
+			c->name, rate,
+			c->parent->name, parent_rate);
+		return -EINVAL;
+	}
+	c->rate = parent_rate;
+	return 0;
+}
+
+static struct clk_ops tegra_audio_sync_clk_ops = {
+	.init       = tegra2_audio_sync_clk_init,
+	.enable     = tegra2_audio_sync_clk_enable,
+	.disable    = tegra2_audio_sync_clk_disable,
+	.set_rate   = tegra2_audio_sync_clk_set_rate,
+	.set_parent = tegra2_audio_sync_clk_set_parent,
 };
 
 /* Clock definitions */
 static struct clk tegra_clk_32k = {
 	.name = "clk_32k",
-	.rate = 32678,
+	.rate = 32768,
 	.ops  = NULL,
+	.max_rate = 32768,
 };
 
 static struct clk_pll_table tegra_pll_s_table[] = {
@@ -782,6 +1009,7 @@ static struct clk tegra_pll_s = {
 	.vco_min   = 12000000,
 	.vco_max   = 26000000,
 	.pll_table = tegra_pll_s_table,
+	.max_rate  = 26000000,
 };
 
 static struct clk_mux_sel tegra_clk_m_sel[] = {
@@ -797,6 +1025,7 @@ static struct clk tegra_clk_m = {
 	.reg       = 0x1fc,
 	.reg_mask  = (1<<28),
 	.reg_shift = 28,
+	.max_rate  = 26000000,
 };
 
 static struct clk_pll_table tegra_pll_c_table[] = {
@@ -816,6 +1045,7 @@ static struct clk tegra_pll_c = {
 	.vco_min   = 20000000,
 	.vco_max   = 1400000000,
 	.pll_table = tegra_pll_c_table,
+	.max_rate  = 600000000,
 };
 
 static struct clk tegra_pll_c_out1 = {
@@ -825,9 +1055,18 @@ static struct clk tegra_pll_c_out1 = {
 	.parent    = &tegra_pll_c,
 	.reg       = 0x84,
 	.reg_shift = 0,
+	.max_rate  = 600000000,
 };
 
 static struct clk_pll_table tegra_pll_m_table[] = {
+	{ 12000000, 666000000, 666, 12, 1, 8},
+	{ 13000000, 666000000, 666, 13, 1, 8},
+	{ 19200000, 666000000, 555, 16, 1, 8},
+	{ 26000000, 666000000, 666, 26, 1, 8},
+	{ 12000000, 600000000, 600, 12, 1, 8},
+	{ 13000000, 600000000, 600, 13, 1, 8},
+	{ 19200000, 600000000, 375, 12, 1, 6},
+	{ 26000000, 600000000, 600, 26, 1, 8},
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
@@ -844,6 +1083,7 @@ static struct clk tegra_pll_m = {
 	.vco_min   = 20000000,
 	.vco_max   = 1200000000,
 	.pll_table = tegra_pll_m_table,
+	.max_rate  = 800000000,
 };
 
 static struct clk tegra_pll_m_out1 = {
@@ -853,6 +1093,7 @@ static struct clk tegra_pll_m_out1 = {
 	.parent    = &tegra_pll_m,
 	.reg       = 0x94,
 	.reg_shift = 0,
+	.max_rate  = 600000000,
 };
 
 static struct clk_pll_table tegra_pll_p_table[] = {
@@ -880,6 +1121,7 @@ static struct clk tegra_pll_p = {
 	.vco_min   = 20000000,
 	.vco_max   = 1400000000,
 	.pll_table = tegra_pll_p_table,
+	.max_rate  = 432000000,
 };
 
 static struct clk tegra_pll_p_out1 = {
@@ -889,6 +1131,7 @@ static struct clk tegra_pll_p_out1 = {
 	.parent    = &tegra_pll_p,
 	.reg       = 0xa4,
 	.reg_shift = 0,
+	.max_rate  = 432000000,
 };
 
 static struct clk tegra_pll_p_out2 = {
@@ -898,6 +1141,7 @@ static struct clk tegra_pll_p_out2 = {
 	.parent    = &tegra_pll_p,
 	.reg       = 0xa4,
 	.reg_shift = 16,
+	.max_rate  = 432000000,
 };
 
 static struct clk tegra_pll_p_out3 = {
@@ -907,6 +1151,7 @@ static struct clk tegra_pll_p_out3 = {
 	.parent    = &tegra_pll_p,
 	.reg       = 0xa8,
 	.reg_shift = 0,
+	.max_rate  = 432000000,
 };
 
 static struct clk tegra_pll_p_out4 = {
@@ -916,6 +1161,7 @@ static struct clk tegra_pll_p_out4 = {
 	.parent    = &tegra_pll_p,
 	.reg       = 0xa8,
 	.reg_shift = 16,
+	.max_rate  = 432000000,
 };
 
 static struct clk_pll_table tegra_pll_a_table[] = {
@@ -923,6 +1169,7 @@ static struct clk_pll_table tegra_pll_a_table[] = {
 	{ 28800000, 73728000, 64, 25, 1, 1},
 	{ 28800000, 11289600, 49, 25, 1, 1},
 	{ 28800000, 12288000, 64, 25, 1, 1},
+	{ 28800000, 24000000,  5,  6, 1, 1},
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
@@ -939,6 +1186,7 @@ static struct clk tegra_pll_a = {
 	.vco_min   = 20000000,
 	.vco_max   = 1400000000,
 	.pll_table = tegra_pll_a_table,
+	.max_rate  = 56448000,
 };
 
 static struct clk tegra_pll_a_out0 = {
@@ -948,6 +1196,7 @@ static struct clk tegra_pll_a_out0 = {
 	.parent    = &tegra_pll_a,
 	.reg       = 0xb4,
 	.reg_shift = 0,
+	.max_rate  = 56448000,
 };
 
 static struct clk_pll_table tegra_pll_d_table[] = {
@@ -971,6 +1220,7 @@ static struct clk tegra_pll_d = {
 	.vco_min   = 40000000,
 	.vco_max   = 1000000000,
 	.pll_table = tegra_pll_d_table,
+	.max_rate  = 1000000000,
 };
 
 static struct clk tegra_pll_d_out0 = {
@@ -978,19 +1228,20 @@ static struct clk tegra_pll_d_out0 = {
 	.ops       = &tegra_pll_div_ops,
 	.flags     = DIV_2 | PLLD,
 	.parent    = &tegra_pll_d,
+	.max_rate  = 500000000,
 };
 
 static struct clk_pll_table tegra_pll_u_table[] = {
-	{ 12000000, 480000000, 960, 12, 1, 0},
-	{ 13000000, 480000000, 960, 13, 1, 0},
-	{ 19200000, 480000000, 200, 4,  1, 0},
-	{ 26000000, 480000000, 960, 26, 1, 0},
+	{ 12000000, 480000000, 960, 12, 2, 0},
+	{ 13000000, 480000000, 960, 13, 2, 0},
+	{ 19200000, 480000000, 200, 4,  2, 0},
+	{ 26000000, 480000000, 960, 26, 2, 0},
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
 static struct clk tegra_pll_u = {
 	.name      = "pll_u",
-	.flags     = 0,
+	.flags     = PLLU,
 	.ops       = &tegra_pll_ops,
 	.reg       = 0xc0,
 	.input_min = 2000000,
@@ -1001,24 +1252,59 @@ static struct clk tegra_pll_u = {
 	.vco_min   = 480000000,
 	.vco_max   = 960000000,
 	.pll_table = tegra_pll_u_table,
+	.max_rate  = 480000000,
 };
 
 static struct clk_pll_table tegra_pll_x_table[] = {
+	/* 1 GHz */
 	{ 12000000, 1000000000, 1000, 12, 1, 12},
 	{ 13000000, 1000000000, 1000, 13, 1, 12},
 	{ 19200000, 1000000000, 625,  12, 1, 8},
 	{ 26000000, 1000000000, 1000, 26, 1, 12},
-	{ 12000000, 750000000,  750,  12, 1, 12},
-	{ 13000000, 750000000,  750,  13, 1, 12},
-	{ 19200000, 750000000,  625,  16, 1, 8},
-	{ 26000000, 750000000,  750,  26, 1, 12},
+
+	/* 912 MHz */
+	{ 12000000, 912000000,  912,  12, 1, 12},
+	{ 13000000, 912000000,  912,  13, 1, 12},
+	{ 19200000, 912000000,  760,  16, 1, 8},
+	{ 26000000, 912000000,  912,  26, 1, 12},
+
+	/* 816 MHz */
+	{ 12000000, 816000000,  816,  12, 1, 12},
+	{ 13000000, 816000000,  816,  13, 1, 12},
+	{ 19200000, 816000000,  680,  16, 1, 8},
+	{ 26000000, 816000000,  816,  26, 1, 12},
+
+	/* 760 MHz */
+	{ 12000000, 760000000,  760,  12, 1, 12},
+	{ 13000000, 760000000,  760,  13, 1, 12},
+	{ 19200000, 760000000,  950,  24, 1, 8},
+	{ 26000000, 760000000,  760,  26, 1, 12},
+
+	/* 608 MHz */
+	{ 12000000, 608000000,  760,  12, 1, 12},
+	{ 13000000, 608000000,  760,  13, 1, 12},
+	{ 19200000, 608000000,  380,  12, 1, 8},
+	{ 26000000, 608000000,  760,  26, 1, 12},
+
+	/* 456 MHz */
+	{ 12000000, 456000000,  456,  12, 1, 12},
+	{ 13000000, 456000000,  456,  13, 1, 12},
+	{ 19200000, 456000000,  380,  16, 1, 8},
+	{ 26000000, 456000000,  456,  26, 1, 12},
+
+	/* 312 MHz */
+	{ 12000000, 312000000,  312,  12, 1, 12},
+	{ 13000000, 312000000,  312,  13, 1, 12},
+	{ 19200000, 312000000,  260,  16, 1, 8},
+	{ 26000000, 312000000,  312,  26, 1, 12},
+
 	{ 0, 0, 0, 0, 0, 0 },
 };
 
 static struct clk tegra_pll_x = {
 	.name      = "pll_x",
 	.flags     = PLL_HAS_CPCON | PLL_ALT_MISC_REG,
-	.ops       = &tegra_pll_ops,
+	.ops       = &tegra_pllx_ops,
 	.reg       = 0xe0,
 	.input_min = 2000000,
 	.input_max = 31000000,
@@ -1028,6 +1314,7 @@ static struct clk tegra_pll_x = {
 	.vco_min   = 20000000,
 	.vco_max   = 1200000000,
 	.pll_table = tegra_pll_x_table,
+	.max_rate  = 1000000000,
 };
 
 static struct clk tegra_clk_d = {
@@ -1038,19 +1325,77 @@ static struct clk tegra_clk_d = {
 	.reg       = 0x34,
 	.reg_shift = 12,
 	.parent    = &tegra_clk_m,
+	.max_rate  = 52000000,
+};
+
+/* initialized before peripheral clocks */
+static struct clk_mux_sel mux_audio_sync_clk[8+1];
+static const struct audio_sources {
+	const char *name;
+	int value;
+} mux_audio_sync_clk_sources[] = {
+	{ .name = "spdif_in", .value = 0 },
+	{ .name = "i2s1", .value = 1 },
+	{ .name = "i2s2", .value = 2 },
+	{ .name = "pll_a_out0", .value = 4 },
+#if 0 /* FIXME: not implemented */
+	{ .name = "ac97", .value = 3 },
+	{ .name = "ext_audio_clk2", .value = 5 },
+	{ .name = "ext_audio_clk1", .value = 6 },
+	{ .name = "ext_vimclk", .value = 7 },
+#endif
+	{ 0, 0 }
+};
+
+static struct clk tegra_clk_audio = {
+	.name      = "audio",
+	.inputs    = mux_audio_sync_clk,
+	.reg       = 0x38,
+	.max_rate  = 24000000,
+	.ops       = &tegra_audio_sync_clk_ops
 };
 
-/* FIXME: need tegra_audio
 static struct clk tegra_clk_audio_2x = {
-	.name      = "clk_d",
+	.name      = "audio_2x",
 	.flags     = PERIPH_NO_RESET,
+	.max_rate  = 48000000,
 	.ops       = &tegra_clk_double_ops,
 	.clk_num   = 89,
 	.reg       = 0x34,
 	.reg_shift = 8,
-	.parent    = &tegra_audio,
+	.parent    = &tegra_clk_audio,
+};
+
+struct clk_lookup tegra_audio_clk_lookups[] = {
+	{ .con_id = "audio", .clk = &tegra_clk_audio },
+	{ .con_id = "audio_2x", .clk = &tegra_clk_audio_2x }
+};
+
+/* This is called after peripheral clocks are initialized, as the
+ * audio_sync clock depends on some of the peripheral clocks.
+ */
+
+static void init_audio_sync_clock_mux(void)
+{
+	int i;
+	struct clk_mux_sel *sel = mux_audio_sync_clk;
+	const struct audio_sources *src = mux_audio_sync_clk_sources;
+	struct clk_lookup *lookup;
+
+	for (i = 0; src->name; i++, sel++, src++) {
+		sel->input = tegra_get_clock_by_name(src->name);
+		if (!sel->input)
+			pr_err("%s: could not find clk %s\n", __func__,
+				src->name);
+		sel->value = src->value;
+	}
+
+	lookup = tegra_audio_clk_lookups;
+	for (i = 0; i < ARRAY_SIZE(tegra_audio_clk_lookups); i++, lookup++) {
+		clk_init(lookup->clk);
+		clkdev_add(lookup);
+	}
 }
-*/
 
 static struct clk_mux_sel mux_cclk[] = {
 	{ .input = &tegra_clk_m,	.value = 0},
@@ -1077,27 +1422,40 @@ static struct clk_mux_sel mux_sclk[] = {
 	{ 0, 0},
 };
 
-static struct clk tegra_clk_cpu = {
-	.name	= "cpu",
+static struct clk tegra_clk_cclk = {
+	.name	= "cclk",
 	.inputs	= mux_cclk,
 	.reg	= 0x20,
 	.ops	= &tegra_super_ops,
+	.max_rate = 1000000000,
 };
 
-static struct clk tegra_clk_sys = {
-	.name	= "sys",
+static struct clk tegra_clk_sclk = {
+	.name	= "sclk",
 	.inputs	= mux_sclk,
 	.reg	= 0x28,
 	.ops	= &tegra_super_ops,
+	.max_rate = 600000000,
+};
+
+static struct clk tegra_clk_virtual_cpu = {
+	.name      = "cpu",
+	.parent    = &tegra_clk_cclk,
+	.main      = &tegra_pll_x,
+	.backup    = &tegra_clk_m,
+	.ops       = &tegra_cpu_ops,
+	.max_rate  = 1000000000,
+	.dvfs      = &tegra_dvfs_virtual_cpu_dvfs,
 };
 
 static struct clk tegra_clk_hclk = {
 	.name		= "hclk",
 	.flags		= DIV_BUS,
-	.parent		= &tegra_clk_sys,
+	.parent		= &tegra_clk_sclk,
 	.reg		= 0x30,
 	.reg_shift	= 4,
 	.ops		= &tegra_bus_ops,
+	.max_rate       = 240000000,
 };
 
 static struct clk tegra_clk_pclk = {
@@ -1107,6 +1465,7 @@ static struct clk tegra_clk_pclk = {
 	.reg		= 0x30,
 	.reg_shift	= 0,
 	.ops		= &tegra_bus_ops,
+	.max_rate       = 108000000,
 };
 
 static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
@@ -1133,10 +1492,9 @@ static struct clk_mux_sel mux_pllp_pllc_pllm_clkm[] = {
 	{ 0, 0},
 };
 
-static struct clk_mux_sel mux_plla_audio_pllp_clkm[] = {
-	{.input = &tegra_pll_a, .value = 0},
-	/* FIXME: no mux defined for tegra_audio
-	{.input = &tegra_audio, .value = 1},*/
+static struct clk_mux_sel mux_pllaout0_audio2x_pllp_clkm[] = {
+	{.input = &tegra_pll_a_out0, .value = 0},
+	{.input = &tegra_clk_audio_2x, .value = 1},
 	{.input = &tegra_pll_p, .value = 2},
 	{.input = &tegra_clk_m, .value = 3},
 	{ 0, 0},
@@ -1153,8 +1511,7 @@ static struct clk_mux_sel mux_pllp_plld_pllc_clkm[] = {
 static struct clk_mux_sel mux_pllp_pllc_audio_clkm_clk32[] = {
 	{.input = &tegra_pll_p,     .value = 0},
 	{.input = &tegra_pll_c,     .value = 1},
-	/* FIXME: no mux defined for tegra_audio
-	{.input = &tegra_audio,     .value = 2},*/
+	{.input = &tegra_clk_audio,     .value = 2},
 	{.input = &tegra_clk_m,     .value = 3},
 	{.input = &tegra_clk_32k,   .value = 4},
 	{ 0, 0},
@@ -1187,7 +1544,7 @@ static struct clk_mux_sel mux_clk_32k[] = {
 	{ 0, 0},
 };
 
-#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _inputs, _flags) \
+#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
 	{						\
 		.name      = _name,			\
 		.lookup    = {				\
@@ -1199,72 +1556,76 @@ static struct clk_mux_sel mux_clk_32k[] = {
 		.reg       = _reg,			\
 		.inputs    = _inputs,			\
 		.flags     = _flags,			\
+		.max_rate  = _max,			\
 	}
 
 struct clk tegra_periph_clks[] = {
-	PERIPH_CLK("rtc",	"rtc-tegra",		NULL,	4,	0,	mux_clk_32k,			PERIPH_NO_RESET),
-	PERIPH_CLK("timer",	"timer",		NULL,	5,	0,	mux_clk_m,			0),
-	PERIPH_CLK("i2s1",	"i2s.0",		NULL,	11,	0x100,	mux_plla_audio_pllp_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("i2s2",	"i2s.1",		NULL,	18,	0x104,	mux_plla_audio_pllp_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("rtc",	"rtc-tegra",		NULL,	4,	0,	32768,     mux_clk_32k,			PERIPH_NO_RESET),
+	PERIPH_CLK("timer",	"timer",		NULL,	5,	0,	26000000,  mux_clk_m,			0),
+	PERIPH_CLK("i2s1",	"i2s.0",		NULL,	11,	0x100,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("i2s2",	"i2s.1",		NULL,	18,	0x104,	26000000,  mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
 	/* FIXME: spdif has 2 clocks but 1 enable */
-	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	mux_plla_audio_pllp_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	mux_pllp_pllc_pllm,		MUX | DIV_U71),
-	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71),
-	PERIPH_CLK("spi",	"spi",			NULL,	43,	0x114,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("xio",	"xio",			NULL,	45,	0x120,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("twc",	"twc",			NULL,	16,	0x12c,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sbc1",	"spi_tegra.0",		NULL,	41,	0x134,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sbc2",	"spi_tegra.1",		NULL,	44,	0x118,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sbc3",	"spi_tegra.2",		NULL,	46,	0x11c,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sbc4",	"spi_tegra.3",		NULL,	68,	0x1b4,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("ide",	"ide",			NULL,	25,	0x144,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("ndflash",	"tegra_nand",		NULL,	13,	0x160,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("spdif_out",	"spdif_out",		NULL,	10,	0x108,	100000000, mux_pllaout0_audio2x_pllp_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("spdif_in",	"spdif_in",		NULL,	10,	0x10c,	100000000, mux_pllp_pllc_pllm,		MUX | DIV_U71),
+	PERIPH_CLK("pwm",	"pwm",			NULL,	17,	0x110,	432000000, mux_pllp_pllc_audio_clkm_clk32,	MUX | DIV_U71),
+	PERIPH_CLK("spi",	"spi",			NULL,	43,	0x114,	40000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("xio",	"xio",			NULL,	45,	0x120,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("twc",	"twc",			NULL,	16,	0x12c,	150000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("sbc1",	"spi_tegra.0",		NULL,	41,	0x134,	160000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("sbc2",	"spi_tegra.1",		NULL,	44,	0x118,	160000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("sbc3",	"spi_tegra.2",		NULL,	46,	0x11c,	160000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("sbc4",	"spi_tegra.3",		NULL,	68,	0x1b4,	160000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("ide",	"ide",			NULL,	25,	0x144,	100000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("ndflash",	"tegra_nand",		NULL,	13,	0x160,	164000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
 	/* FIXME: vfir shares an enable with uartb */
-	PERIPH_CLK("vfir",	"vfir",			NULL,	7,	0x168,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sdmmc1",	"sdhci-tegra.0",	NULL,	14,	0x150,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sdmmc2",	"sdhci-tegra.1",	NULL,	9,	0x154,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sdmmc3",	"sdhci-tegra.2",	NULL,	69,	0x1bc,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("sdmmc4",	"sdhci-tegra.3",	NULL,	15,	0x160,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("vde",	"vde",			NULL,	61,	0x1c8,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("csite",	"csite",		NULL,	73,	0x1d4,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("vfir",	"vfir",			NULL,	7,	0x168,	72000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("sdmmc1",	"sdhci-tegra.0",	NULL,	14,	0x150,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("sdmmc2",	"sdhci-tegra.1",	NULL,	9,	0x154,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("sdmmc3",	"sdhci-tegra.2",	NULL,	69,	0x1bc,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("sdmmc4",	"sdhci-tegra.3",	NULL,	15,	0x160,	52000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("vde",	"vde",			NULL,	61,	0x1c8,	250000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("csite",	"csite",		NULL,	73,	0x1d4,	144000000, mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* max rate ??? */
 	/* FIXME: what is la? */
-	PERIPH_CLK("la",	"la",			NULL,	76,	0x1f8,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("owr",	"owr",			NULL,	71,	0x1cc,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("nor",	"nor",			NULL,	42,	0x1d0,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("mipi",	"mipi",			NULL,	50,	0x174,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("i2c1",	"tegra-i2c.0",		NULL,	12,	0x124,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("i2c2",	"tegra-i2c.1",		NULL,	54,	0x198,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("i2c3",	"tegra-i2c.2",		NULL,	67,	0x1b8,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("dvc",	"tegra-i2c.3",		NULL,	47,	0x128,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("i2c1_i2c",	"tegra-i2c.0",		"i2c",	0,	0,	mux_pllp_out3,			0),
-	PERIPH_CLK("i2c2_i2c",	"tegra-i2c.1",		"i2c",	0,	0,	mux_pllp_out3,			0),
-	PERIPH_CLK("i2c3_i2c",	"tegra-i2c.2",		"i2c",	0,	0,	mux_pllp_out3,			0),
-	PERIPH_CLK("dvc_i2c",	"tegra-i2c.3",		"i2c",	0,	0,	mux_pllp_out3,			0),
-	PERIPH_CLK("uarta",	"uart.0",		NULL,	6,	0x178,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("uartb",	"uart.1",		NULL,	7,	0x17c,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("uartc",	"uart.2",		NULL,	55,	0x1a0,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("uartd",	"uart.3",		NULL,	65,	0x1c0,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("uarte",	"uart.4",		NULL,	66,	0x1c4,	mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("3d",	"3d",			NULL,	24,	0x158,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_MANUAL_RESET),
-	PERIPH_CLK("2d",	"2d",			NULL,	21,	0x15c,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71),
+	PERIPH_CLK("la",	"la",			NULL,	76,	0x1f8,	26000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("owr",	"tegra_w1",		NULL,	71,	0x1cc,	26000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71),
+	PERIPH_CLK("nor",	"nor",			NULL,	42,	0x1d0,	92000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("mipi",	"mipi",			NULL,	50,	0x174,	60000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U71), /* scales with voltage */
+	PERIPH_CLK("i2c1",	"tegra-i2c.0",		NULL,	12,	0x124,	26000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U16),
+	PERIPH_CLK("i2c2",	"tegra-i2c.1",		NULL,	54,	0x198,	26000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U16),
+	PERIPH_CLK("i2c3",	"tegra-i2c.2",		NULL,	67,	0x1b8,	26000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U16),
+	PERIPH_CLK("dvc",	"tegra-i2c.3",		NULL,	47,	0x128,	26000000,  mux_pllp_pllc_pllm_clkm,	MUX | DIV_U16),
+	PERIPH_CLK("i2c1_i2c",	"tegra-i2c.0",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
+	PERIPH_CLK("i2c2_i2c",	"tegra-i2c.1",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
+	PERIPH_CLK("i2c3_i2c",	"tegra-i2c.2",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
+	PERIPH_CLK("dvc_i2c",	"tegra-i2c.3",		"i2c",	0,	0,	72000000,  mux_pllp_out3,			0),
+	PERIPH_CLK("uarta",	"uart.0",		NULL,	6,	0x178,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartb",	"uart.1",		NULL,	7,	0x17c,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartc",	"uart.2",		NULL,	55,	0x1a0,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uartd",	"uart.3",		NULL,	65,	0x1c0,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("uarte",	"uart.4",		NULL,	66,	0x1c4,	216000000, mux_pllp_pllc_pllm_clkm,	MUX),
+	PERIPH_CLK("3d",	"3d",			NULL,	24,	0x158,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
+	PERIPH_CLK("2d",	"2d",			NULL,	21,	0x15c,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	/* FIXME: vi and vi_sensor share an enable */
-	PERIPH_CLK("vi",	"vi",			NULL,	20,	0x148,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71),
-	PERIPH_CLK("vi_sensor",	"vi_sensor",		NULL,	20,	0x1a8,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71),
-	PERIPH_CLK("epp",	"epp",			NULL,	19,	0x16c,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71),
-	PERIPH_CLK("mpe",	"mpe",			NULL,	60,	0x170,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71),
-	PERIPH_CLK("host1x",	"host1x",		NULL,	28,	0x180,	mux_pllm_pllc_pllp_plla,	MUX | DIV_U71),
+	PERIPH_CLK("vi",	"vi",			NULL,	20,	0x148,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("vi_sensor",	"vi_sensor",		NULL,	20,	0x1a8,	150000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
+	PERIPH_CLK("epp",	"epp",			NULL,	19,	0x16c,	300000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("mpe",	"mpe",			NULL,	60,	0x170,	250000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("host1x",	"host1x",		NULL,	28,	0x180,	166000000, mux_pllm_pllc_pllp_plla,	MUX | DIV_U71), /* scales with voltage and process_id */
 	/* FIXME: cve and tvo share an enable	*/
-	PERIPH_CLK("cve",	"cve",			NULL,	49,	0x140,	mux_pllp_plld_pllc_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("tvo",	"tvo",			NULL,	49,	0x188,	mux_pllp_plld_pllc_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	mux_pllp_plld_pllc_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("tvdac",	"tvdac",		NULL,	53,	0x194,	mux_pllp_plld_pllc_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("disp1",	"tegrafb.0",		NULL,	27,	0x138,	mux_pllp_plld_pllc_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("disp2",	"tegrafb.1",		NULL,	26,	0x13c,	mux_pllp_plld_pllc_clkm,	MUX | DIV_U71),
-	PERIPH_CLK("usbd",	"fsl-tegra-udc",	NULL,	22,	0,	mux_clk_m,			0),
-	PERIPH_CLK("usb2",	"usb.1",		NULL,	58,	0,	mux_clk_m,			0),
-	PERIPH_CLK("usb3",	"usb.2",		NULL,	59,	0,	mux_clk_m,			0),
-	PERIPH_CLK("emc",	"emc",			NULL,	57,	0x19c,	mux_pllm_pllc_pllp_clkm,	MUX | DIV_U71 | PERIPH_EMC_ENB),
-	PERIPH_CLK("dsi",	"dsi",			NULL,	48,	0,	mux_plld,			0),
+	PERIPH_CLK("cve",	"cve",			NULL,	49,	0x140,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("tvo",	"tvo",			NULL,	49,	0x188,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("hdmi",	"hdmi",			NULL,	51,	0x18c,	148500000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("tvdac",	"tvdac",		NULL,	53,	0x194,	250000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* requires min voltage */
+	PERIPH_CLK("disp1",	"tegrafb.0",		NULL,	27,	0x138,	190000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("disp2",	"tegrafb.1",		NULL,	26,	0x13c,	190000000, mux_pllp_plld_pllc_clkm,	MUX | DIV_U71), /* scales with voltage and process_id */
+	PERIPH_CLK("usbd",	"fsl-tegra-udc",	NULL,	22,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
+	PERIPH_CLK("usb2",	"tegra-ehci.1",		NULL,	58,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
+	PERIPH_CLK("usb3",	"tegra-ehci.2",		NULL,	59,	0,	480000000, mux_clk_m,			0), /* requires min voltage */
+	PERIPH_CLK("emc",	"emc",			NULL,	57,	0x19c,	800000000, mux_pllm_pllc_pllp_clkm,	MUX | DIV_U71 | PERIPH_EMC_ENB),
+	PERIPH_CLK("dsi",	"dsi",			NULL,	48,	0,	500000000, mux_plld,			0), /* scales with voltage */
+	PERIPH_CLK("csi",	"csi",			NULL,	52,	0,	72000000,  mux_pllp_out3,		0),
+	PERIPH_CLK("isp",	"isp",			NULL,	23,	0,	150000000, mux_clk_m,			0), /* same frequency as VI */
+	PERIPH_CLK("csus",	"csus",			NULL,	92,	0,	150000000, mux_clk_m,			PERIPH_NO_RESET),
 };
 
 #define CLK_DUPLICATE(_name, _dev, _con)		\
@@ -1286,6 +1647,9 @@ struct clk_duplicate tegra_clk_duplicates[] = {
 	CLK_DUPLICATE("uartc",	"tegra_uart.2",	NULL),
 	CLK_DUPLICATE("uartd",	"tegra_uart.3",	NULL),
 	CLK_DUPLICATE("uarte",	"tegra_uart.4",	NULL),
+	CLK_DUPLICATE("host1x", "tegrafb.0", "host1x"),
+	CLK_DUPLICATE("host1x", "tegrafb.1", "host1x"),
+	CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
 };
 
 #define CLK(dev, con, ck)	\
@@ -1315,11 +1679,12 @@ struct clk_lookup tegra_clk_lookups[] = {
 	CLK(NULL,	"pll_d_out0",	&tegra_pll_d_out0),
 	CLK(NULL,	"pll_u",	&tegra_pll_u),
 	CLK(NULL,	"pll_x",	&tegra_pll_x),
-	CLK(NULL,	"cpu",		&tegra_clk_cpu),
-	CLK(NULL,	"sys",		&tegra_clk_sys),
+	CLK(NULL,	"cclk",		&tegra_clk_cclk),
+	CLK(NULL,	"sclk",		&tegra_clk_sclk),
 	CLK(NULL,	"hclk",		&tegra_clk_hclk),
 	CLK(NULL,	"pclk",		&tegra_clk_pclk),
 	CLK(NULL,	"clk_d",	&tegra_clk_d),
+	CLK(NULL,	"cpu",		&tegra_clk_virtual_cpu),
 };
 
 void __init tegra2_init_clocks(void)
@@ -1356,4 +1721,75 @@ void __init tegra2_init_clocks(void)
 				cd->name);
 		}
 	}
+
+	init_audio_sync_clock_mux();
+}
+
+#ifdef CONFIG_PM
+static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
+			   PERIPH_CLK_SOURCE_NUM + 3];
+
+void tegra_clk_suspend(void)
+{
+	unsigned long off, i;
+	u32 *ctx = clk_rst_suspend;
+
+	*ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
+
+	for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
+			off += 4) {
+		if (off == PERIPH_CLK_SOURCE_EMC)
+			continue;
+		*ctx++ = clk_readl(off);
+	}
+
+	off = RST_DEVICES;
+	for (i = 0; i < RST_DEVICES_NUM; i++, off += 4)
+		*ctx++ = clk_readl(off);
+
+	off = CLK_OUT_ENB;
+	for (i = 0; i < CLK_OUT_ENB_NUM; i++, off += 4)
+		*ctx++ = clk_readl(off);
+
+	*ctx++ = clk_readl(MISC_CLK_ENB);
+	*ctx++ = clk_readl(CLK_MASK_ARM);
+}
+
+void tegra_clk_resume(void)
+{
+	unsigned long off, i;
+	const u32 *ctx = clk_rst_suspend;
+	u32 val;
+
+	val = clk_readl(OSC_CTRL) & ~OSC_CTRL_MASK;
+	val |= *ctx++;
+	clk_writel(val, OSC_CTRL);
+
+	/* enable all clocks before configuring clock sources */
+	clk_writel(0xbffffff9ul, CLK_OUT_ENB);
+	clk_writel(0xfefffff7ul, CLK_OUT_ENB + 4);
+	clk_writel(0x77f01bfful, CLK_OUT_ENB + 8);
+	wmb();
+
+	for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
+			off += 4) {
+		if (off == PERIPH_CLK_SOURCE_EMC)
+			continue;
+		clk_writel(*ctx++, off);
+	}
+	wmb();
+
+	off = RST_DEVICES;
+	for (i = 0; i < RST_DEVICES_NUM; i++, off += 4)
+		clk_writel(*ctx++, off);
+	wmb();
+
+	off = CLK_OUT_ENB;
+	for (i = 0; i < CLK_OUT_ENB_NUM; i++, off += 4)
+		clk_writel(*ctx++, off);
+	wmb();
+
+	clk_writel(*ctx++, MISC_CLK_ENB);
+	clk_writel(*ctx++, CLK_MASK_ARM);
 }
+#endif
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c
new file mode 100644
index 0000000..5529c23
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_dvfs.c
@@ -0,0 +1,86 @@
+/*
+ * arch/arm/mach-tegra/tegra2_dvfs.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross at google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+
+#include "clock.h"
+#include "tegra2_dvfs.h"
+
+static struct dvfs_table virtual_cpu_process_0[] = {
+	{314000000,  750},
+	{456000000,  825},
+	{608000000,  900},
+	{760000000,  975},
+	{817000000,  1000},
+	{912000000,  1050},
+	{1000000000, 1100},
+	{0, 0},
+};
+
+static struct dvfs_table virtual_cpu_process_1[] = {
+	{314000000,  750},
+	{456000000,  825},
+	{618000000,  900},
+	{770000000,  975},
+	{827000000,  1000},
+	{922000000,  1050},
+	{1000000000, 1100},
+	{0, 0},
+};
+
+static struct dvfs_table virtual_cpu_process_2[] = {
+	{494000000,  750},
+	{675000000,  825},
+	{817000000,  875},
+	{922000000,  925},
+	{1000000000, 975},
+	{0, 0},
+};
+
+static struct dvfs_table virtual_cpu_process_3[] = {
+	{730000000,  750},
+	{760000000,  775},
+	{845000000,  800},
+	{1000000000, 875},
+	{0, 0},
+};
+
+struct dvfs tegra_dvfs_virtual_cpu_dvfs = {
+	.reg_id = "vdd_cpu",
+	.process_id_table = {
+		{
+			.process_id = 0,
+			.table = virtual_cpu_process_0,
+		},
+		{
+			.process_id = 1,
+			.table = virtual_cpu_process_1,
+		},
+		{
+			.process_id = 2,
+			.table = virtual_cpu_process_2,
+		},
+		{
+			.process_id = 3,
+			.table = virtual_cpu_process_3,
+		},
+	},
+	.process_id_table_length = 4,
+	.cpu = 1,
+};
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.h b/arch/arm/mach-tegra/tegra2_dvfs.h
new file mode 100644
index 0000000..f8c1adb
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra2_dvfs.h
@@ -0,0 +1,20 @@
+/*
+ * arch/arm/mach-tegra/tegra2_dvfs.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross at google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+extern struct dvfs tegra_dvfs_virtual_cpu_dvfs;
-- 
1.7.1




More information about the linux-arm-kernel mailing list