diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index f401b92a99f5f0e1428878d7bee87a5a8aec8ca9..bf7273f3dc64efa359ece26eed3a7e19192e7bf2 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -573,6 +573,7 @@ config ARCH_TEGRA
 	select HAVE_CLK
 	select COMMON_CLKDEV
 	select ARCH_HAS_BARRIERS if CACHE_L2X0
+	select ARCH_HAS_CPUFREQ
 	help
 	  This enables support for NVIDIA Tegra based systems (Tegra APX,
 	  Tegra 6xx and Tegra 2 series).
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index a57713c1954abe0ca329648d7fc398c8ac3d64f7..acd9552f8ada7bb6fd10d2b5cc5377d14632ca74 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -16,6 +16,10 @@ config ARCH_TEGRA_2x_SOC
 
 endchoice
 
+config TEGRA_PCI
+	bool "PCI Express support"
+	select PCI
+
 comment "Tegra board type"
 
 config MACH_HARMONY
@@ -47,4 +51,11 @@ config TEGRA_DEBUG_UARTE
 
 endchoice
 
+config TEGRA_SYSTEM_DMA
+	bool "Enable system DMA driver for NVIDIA Tegra SoCs"
+	default y
+	help
+	  Adds system DMA functionality for NVIDIA Tegra SoCs, used by
+	  several Tegra device drivers
+
 endif
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 51e9370eed99bb4ffb23323610fff4e5f3cd9787..cdbc68e4c0ca2738492edc665e31df47354fa6fe 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -1,14 +1,21 @@
 obj-y                                   += common.o
 obj-y                                   += io.o
-obj-y                                   += irq.o
+obj-y                                   += irq.o legacy_irq.o
 obj-y                                   += clock.o
 obj-y                                   += timer.o
 obj-y                                   += gpio.o
 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
+obj-$(CONFIG_TEGRA_SYSTEM_DMA)		+= dma.o
+obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
+obj-$(CONFIG_TEGRA_PCI)			+= pcie.o
 
 obj-${CONFIG_MACH_HARMONY}              += board-harmony.o
 obj-${CONFIG_MACH_HARMONY}              += board-harmony-pinmux.o
+obj-${CONFIG_MACH_HARMONY}              += board-harmony-pcie.o
diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c
new file mode 100644
index 0000000000000000000000000000000000000000..f7e7d4514b6ad7ffc28ba86bcf5377c2749b604f
--- /dev/null
+++ b/arch/arm/mach-tegra/board-harmony-pcie.c
@@ -0,0 +1,57 @@
+/*
+ * arch/arm/mach-tegra/board-harmony-pcie.c
+ *
+ * Copyright (C) 2010 CompuLab, Ltd.
+ * Mike Rapoport <mike@compulab.co.il>
+ *
+ * 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 <linux/gpio.h>
+#include <linux/err.h>
+#include <linux/regulator/consumer.h>
+
+#include <asm/mach-types.h>
+
+#include <mach/pinmux.h>
+#include "board.h"
+
+#ifdef CONFIG_TEGRA_PCI
+
+static int __init harmony_pcie_init(void)
+{
+	int err;
+
+	if (!machine_is_harmony())
+		return 0;
+
+	tegra_pinmux_set_tristate(TEGRA_PINGROUP_GPV, TEGRA_TRI_NORMAL);
+	tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXA, TEGRA_TRI_NORMAL);
+	tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_NORMAL);
+
+	err = tegra_pcie_init(true, true);
+	if (err)
+		goto err_pcie;
+
+	return 0;
+
+err_pcie:
+	tegra_pinmux_set_tristate(TEGRA_PINGROUP_GPV, TEGRA_TRI_TRISTATE);
+	tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXA, TEGRA_TRI_TRISTATE);
+	tegra_pinmux_set_tristate(TEGRA_PINGROUP_SLXK, TEGRA_TRI_TRISTATE);
+
+	return err;
+}
+
+subsys_initcall(harmony_pcie_init);
+
+#endif
diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h
index 3d06354136f2663ff2f80cdbfa9b441671dc4146..0de565ca37c550709fd077278a398eccac7ace5c 100644
--- a/arch/arm/mach-tegra/board.h
+++ b/arch/arm/mach-tegra/board.h
@@ -27,6 +27,7 @@ void __init tegra_common_init(void);
 void __init tegra_map_common_io(void);
 void __init tegra_init_irq(void);
 void __init tegra_init_clock(void);
+int __init tegra_pcie_init(bool init_port0, bool init_port1);
 
 extern struct sys_timer tegra_timer;
 #endif
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 03ad578349b98aa55e89f8639ff49663dd0f8d6d..ae19f95585be119c51456a9d2d75183fc3b1ff4c 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 af7c70e2a3baf4a29c8860ccc0ca51cd026cb1cd..94fd859770f1a992a0bd001e265a781c3bf7448b 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/common.c b/arch/arm/mach-tegra/common.c
index 039a514b61ef1e5f0c9142a4aa81ccf021c96c95..7c91e2b9d643fa5c18e07a606a1b5a543645c383 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -19,13 +19,17 @@
 
 #include <linux/init.h>
 #include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
 
 #include <asm/hardware/cache-l2x0.h>
 
 #include <mach/iomap.h>
+#include <mach/dma.h>
 
 #include "board.h"
 #include "clock.h"
+#include "fuse.h"
 
 static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	/* name		parent		rate		enabled */
@@ -35,8 +39,8 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
 	{ "pll_p_out2",	"pll_p",	48000000,	true },
 	{ "pll_p_out3",	"pll_p",	72000000,	true },
 	{ "pll_p_out4",	"pll_p",	108000000,	true },
-	{ "sys",	"pll_p_out4",	108000000,	true },
-	{ "hclk",	"sys",		108000000,	true },
+	{ "sclk",	"pll_p_out4",	108000000,	true },
+	{ "hclk",	"sclk",		108000000,	true },
 	{ "pclk",	"hclk",		54000000,	true },
 	{ NULL,		NULL,		0,		0},
 };
@@ -51,11 +55,16 @@ void __init tegra_init_cache(void)
 
 	l2x0_init(p, 0x6C080001, 0x8200c3fe);
 #endif
+
 }
 
 void __init tegra_common_init(void)
 {
+	tegra_init_fuse();
 	tegra_init_clock();
 	tegra_clk_init_from_table(common_clk_init_table);
 	tegra_init_cache();
+#ifdef CONFIG_TEGRA_SYSTEM_DMA
+	tegra_dma_init();
+#endif
 }
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
new file mode 100644
index 0000000000000000000000000000000000000000..fea5719c7072fc32cfaf58647d09054cd7c576ba
--- /dev/null
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -0,0 +1,185 @@
+/*
+ * arch/arm/mach-tegra/cpu-tegra.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@google.com>
+ *	Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation
+ *
+ * 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 <linux/module.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/cpufreq.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <asm/system.h>
+
+#include <mach/hardware.h>
+#include <mach/clk.h>
+
+/* Frequency table index must be sequential starting at 0 */
+static struct cpufreq_frequency_table freq_table[] = {
+	{ 0, 312000 },
+	{ 1, 456000 },
+	{ 2, 608000 },
+	{ 3, 760000 },
+	{ 4, 816000 },
+	{ 5, 912000 },
+	{ 6, 1000000 },
+	{ 7, CPUFREQ_TABLE_END },
+};
+
+#define NUM_CPUS	2
+
+static struct clk *cpu_clk;
+
+static unsigned long target_cpu_speed[NUM_CPUS];
+
+int tegra_verify_speed(struct cpufreq_policy *policy)
+{
+	return cpufreq_frequency_table_verify(policy, freq_table);
+}
+
+unsigned int tegra_getspeed(unsigned int cpu)
+{
+	unsigned long rate;
+
+	if (cpu >= NUM_CPUS)
+		return 0;
+
+	rate = clk_get_rate(cpu_clk) / 1000;
+	return rate;
+}
+
+static int tegra_update_cpu_speed(void)
+{
+	int i;
+	unsigned long rate = 0;
+	int ret = 0;
+	struct cpufreq_freqs freqs;
+
+	for_each_online_cpu(i)
+		rate = max(rate, target_cpu_speed[i]);
+
+	freqs.old = tegra_getspeed(0);
+	freqs.new = rate;
+
+	if (freqs.old == freqs.new)
+		return ret;
+
+	for_each_online_cpu(freqs.cpu)
+		cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+
+#ifdef CONFIG_CPU_FREQ_DEBUG
+	printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n",
+	       freqs.old, freqs.new);
+#endif
+
+	ret = clk_set_rate_cansleep(cpu_clk, freqs.new * 1000);
+	if (ret) {
+		pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n",
+			freqs.new);
+		return ret;
+	}
+
+	for_each_online_cpu(freqs.cpu)
+		cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+
+	return 0;
+}
+
+static int tegra_target(struct cpufreq_policy *policy,
+		       unsigned int target_freq,
+		       unsigned int relation)
+{
+	int idx;
+	unsigned int freq;
+
+	cpufreq_frequency_table_target(policy, freq_table, target_freq,
+		relation, &idx);
+
+	freq = freq_table[idx].frequency;
+
+	target_cpu_speed[policy->cpu] = freq;
+
+	return tegra_update_cpu_speed();
+}
+
+static int tegra_cpu_init(struct cpufreq_policy *policy)
+{
+	if (policy->cpu >= NUM_CPUS)
+		return -EINVAL;
+
+	cpu_clk = clk_get_sys(NULL, "cpu");
+	if (IS_ERR(cpu_clk))
+		return PTR_ERR(cpu_clk);
+
+	cpufreq_frequency_table_cpuinfo(policy, freq_table);
+	cpufreq_frequency_table_get_attr(freq_table, policy->cpu);
+	policy->cur = tegra_getspeed(policy->cpu);
+	target_cpu_speed[policy->cpu] = policy->cur;
+
+	/* FIXME: what's the actual transition time? */
+	policy->cpuinfo.transition_latency = 300 * 1000;
+
+	policy->shared_type = CPUFREQ_SHARED_TYPE_ALL;
+	cpumask_copy(policy->related_cpus, cpu_possible_mask);
+
+	return 0;
+}
+
+static int tegra_cpu_exit(struct cpufreq_policy *policy)
+{
+	cpufreq_frequency_table_cpuinfo(policy, freq_table);
+	clk_put(cpu_clk);
+	return 0;
+}
+
+static struct freq_attr *tegra_cpufreq_attr[] = {
+	&cpufreq_freq_attr_scaling_available_freqs,
+	NULL,
+};
+
+static struct cpufreq_driver tegra_cpufreq_driver = {
+	.verify		= tegra_verify_speed,
+	.target		= tegra_target,
+	.get		= tegra_getspeed,
+	.init		= tegra_cpu_init,
+	.exit		= tegra_cpu_exit,
+	.name		= "tegra",
+	.attr		= tegra_cpufreq_attr,
+};
+
+static int __init tegra_cpufreq_init(void)
+{
+	return cpufreq_register_driver(&tegra_cpufreq_driver);
+}
+
+static void __exit tegra_cpufreq_exit(void)
+{
+        cpufreq_unregister_driver(&tegra_cpufreq_driver);
+}
+
+
+MODULE_AUTHOR("Colin Cross <ccross@android.com>");
+MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2");
+MODULE_LICENSE("GPL");
+module_init(tegra_cpufreq_init);
+module_exit(tegra_cpufreq_exit);
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
new file mode 100644
index 0000000000000000000000000000000000000000..edda6ec5e925b9dd1d75dc8b776573cae5200d1c
--- /dev/null
+++ b/arch/arm/mach-tegra/dma.c
@@ -0,0 +1,752 @@
+/*
+ * arch/arm/mach-tegra/dma.c
+ *
+ * System DMA driver for NVIDIA Tegra SoCs
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/io.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/err.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <mach/dma.h>
+#include <mach/irqs.h>
+#include <mach/iomap.h>
+
+#define APB_DMA_GEN				0x000
+#define GEN_ENABLE				(1<<31)
+
+#define APB_DMA_CNTRL				0x010
+
+#define APB_DMA_IRQ_MASK			0x01c
+
+#define APB_DMA_IRQ_MASK_SET			0x020
+
+#define APB_DMA_CHAN_CSR			0x000
+#define CSR_ENB					(1<<31)
+#define CSR_IE_EOC				(1<<30)
+#define CSR_HOLD				(1<<29)
+#define CSR_DIR					(1<<28)
+#define CSR_ONCE				(1<<27)
+#define CSR_FLOW				(1<<21)
+#define CSR_REQ_SEL_SHIFT			16
+#define CSR_REQ_SEL_MASK			(0x1F<<CSR_REQ_SEL_SHIFT)
+#define CSR_REQ_SEL_INVALID			(31<<CSR_REQ_SEL_SHIFT)
+#define CSR_WCOUNT_SHIFT			2
+#define CSR_WCOUNT_MASK				0xFFFC
+
+#define APB_DMA_CHAN_STA				0x004
+#define STA_BUSY				(1<<31)
+#define STA_ISE_EOC				(1<<30)
+#define STA_HALT				(1<<29)
+#define STA_PING_PONG				(1<<28)
+#define STA_COUNT_SHIFT				2
+#define STA_COUNT_MASK				0xFFFC
+
+#define APB_DMA_CHAN_AHB_PTR				0x010
+
+#define APB_DMA_CHAN_AHB_SEQ				0x014
+#define AHB_SEQ_INTR_ENB			(1<<31)
+#define AHB_SEQ_BUS_WIDTH_SHIFT			28
+#define AHB_SEQ_BUS_WIDTH_MASK			(0x7<<AHB_SEQ_BUS_WIDTH_SHIFT)
+#define AHB_SEQ_BUS_WIDTH_8			(0<<AHB_SEQ_BUS_WIDTH_SHIFT)
+#define AHB_SEQ_BUS_WIDTH_16			(1<<AHB_SEQ_BUS_WIDTH_SHIFT)
+#define AHB_SEQ_BUS_WIDTH_32			(2<<AHB_SEQ_BUS_WIDTH_SHIFT)
+#define AHB_SEQ_BUS_WIDTH_64			(3<<AHB_SEQ_BUS_WIDTH_SHIFT)
+#define AHB_SEQ_BUS_WIDTH_128			(4<<AHB_SEQ_BUS_WIDTH_SHIFT)
+#define AHB_SEQ_DATA_SWAP			(1<<27)
+#define AHB_SEQ_BURST_MASK			(0x7<<24)
+#define AHB_SEQ_BURST_1				(4<<24)
+#define AHB_SEQ_BURST_4				(5<<24)
+#define AHB_SEQ_BURST_8				(6<<24)
+#define AHB_SEQ_DBL_BUF				(1<<19)
+#define AHB_SEQ_WRAP_SHIFT			16
+#define AHB_SEQ_WRAP_MASK			(0x7<<AHB_SEQ_WRAP_SHIFT)
+
+#define APB_DMA_CHAN_APB_PTR				0x018
+
+#define APB_DMA_CHAN_APB_SEQ				0x01c
+#define APB_SEQ_BUS_WIDTH_SHIFT			28
+#define APB_SEQ_BUS_WIDTH_MASK			(0x7<<APB_SEQ_BUS_WIDTH_SHIFT)
+#define APB_SEQ_BUS_WIDTH_8			(0<<APB_SEQ_BUS_WIDTH_SHIFT)
+#define APB_SEQ_BUS_WIDTH_16			(1<<APB_SEQ_BUS_WIDTH_SHIFT)
+#define APB_SEQ_BUS_WIDTH_32			(2<<APB_SEQ_BUS_WIDTH_SHIFT)
+#define APB_SEQ_BUS_WIDTH_64			(3<<APB_SEQ_BUS_WIDTH_SHIFT)
+#define APB_SEQ_BUS_WIDTH_128			(4<<APB_SEQ_BUS_WIDTH_SHIFT)
+#define APB_SEQ_DATA_SWAP			(1<<27)
+#define APB_SEQ_WRAP_SHIFT			16
+#define APB_SEQ_WRAP_MASK			(0x7<<APB_SEQ_WRAP_SHIFT)
+
+#define TEGRA_SYSTEM_DMA_CH_NR			16
+#define TEGRA_SYSTEM_DMA_AVP_CH_NUM		4
+#define TEGRA_SYSTEM_DMA_CH_MIN			0
+#define TEGRA_SYSTEM_DMA_CH_MAX	\
+	(TEGRA_SYSTEM_DMA_CH_NR - TEGRA_SYSTEM_DMA_AVP_CH_NUM - 1)
+
+#define NV_DMA_MAX_TRASFER_SIZE 0x10000
+
+const unsigned int ahb_addr_wrap_table[8] = {
+	0, 32, 64, 128, 256, 512, 1024, 2048
+};
+
+const unsigned int apb_addr_wrap_table[8] = {0, 1, 2, 4, 8, 16, 32, 64};
+
+const unsigned int bus_width_table[5] = {8, 16, 32, 64, 128};
+
+#define TEGRA_DMA_NAME_SIZE 16
+struct tegra_dma_channel {
+	struct list_head	list;
+	int			id;
+	spinlock_t		lock;
+	char			name[TEGRA_DMA_NAME_SIZE];
+	void  __iomem		*addr;
+	int			mode;
+	int			irq;
+
+	/* Register shadow */
+	u32			csr;
+	u32			ahb_seq;
+	u32			ahb_ptr;
+	u32			apb_seq;
+	u32			apb_ptr;
+};
+
+#define  NV_DMA_MAX_CHANNELS  32
+
+static DECLARE_BITMAP(channel_usage, NV_DMA_MAX_CHANNELS);
+static struct tegra_dma_channel dma_channels[NV_DMA_MAX_CHANNELS];
+
+static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req);
+static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req);
+static void tegra_dma_init_hw(struct tegra_dma_channel *ch);
+static void tegra_dma_stop(struct tegra_dma_channel *ch);
+
+void tegra_dma_flush(struct tegra_dma_channel *ch)
+{
+}
+EXPORT_SYMBOL(tegra_dma_flush);
+
+void tegra_dma_dequeue(struct tegra_dma_channel *ch)
+{
+	struct tegra_dma_req *req;
+
+	req = list_entry(ch->list.next, typeof(*req), node);
+
+	tegra_dma_dequeue_req(ch, req);
+	return;
+}
+
+void tegra_dma_stop(struct tegra_dma_channel *ch)
+{
+	unsigned int csr;
+	unsigned int status;
+
+	csr = ch->csr;
+	csr &= ~CSR_IE_EOC;
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+
+	csr &= ~CSR_ENB;
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+
+	status = readl(ch->addr + APB_DMA_CHAN_STA);
+	if (status & STA_ISE_EOC)
+		writel(status, ch->addr + APB_DMA_CHAN_STA);
+}
+
+int tegra_dma_cancel(struct tegra_dma_channel *ch)
+{
+	unsigned int csr;
+	unsigned long irq_flags;
+
+	spin_lock_irqsave(&ch->lock, irq_flags);
+	while (!list_empty(&ch->list))
+		list_del(ch->list.next);
+
+	csr = ch->csr;
+	csr &= ~CSR_REQ_SEL_MASK;
+	csr |= CSR_REQ_SEL_INVALID;
+
+	/* Set the enable as that is not shadowed */
+	csr |= CSR_ENB;
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+
+	tegra_dma_stop(ch);
+
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
+	return 0;
+}
+
+int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *_req)
+{
+	unsigned int csr;
+	unsigned int status;
+	struct tegra_dma_req *req = NULL;
+	int found = 0;
+	unsigned long irq_flags;
+	int to_transfer;
+	int req_transfer_count;
+
+	spin_lock_irqsave(&ch->lock, irq_flags);
+	list_for_each_entry(req, &ch->list, node) {
+		if (req == _req) {
+			list_del(&req->node);
+			found = 1;
+			break;
+		}
+	}
+	if (!found) {
+		spin_unlock_irqrestore(&ch->lock, irq_flags);
+		return 0;
+	}
+
+	/* STOP the DMA and get the transfer count.
+	 * Getting the transfer count is tricky.
+	 *  - Change the source selector to invalid to stop the DMA from
+	 *    FIFO to memory.
+	 *  - Read the status register to know the number of pending
+	 *    bytes to be transfered.
+	 *  - Finally stop or program the DMA to the next buffer in the
+	 *    list.
+	 */
+	csr = ch->csr;
+	csr &= ~CSR_REQ_SEL_MASK;
+	csr |= CSR_REQ_SEL_INVALID;
+
+	/* Set the enable as that is not shadowed */
+	csr |= CSR_ENB;
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+
+	/* Get the transfer count */
+	status = readl(ch->addr + APB_DMA_CHAN_STA);
+	to_transfer = (status & STA_COUNT_MASK) >> STA_COUNT_SHIFT;
+	req_transfer_count = (ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+	req_transfer_count += 1;
+	to_transfer += 1;
+
+	req->bytes_transferred = req_transfer_count;
+
+	if (status & STA_BUSY)
+		req->bytes_transferred -= to_transfer;
+
+	/* In continous transfer mode, DMA only tracks the count of the
+	 * half DMA buffer. So, if the DMA already finished half the DMA
+	 * then add the half buffer to the completed count.
+	 *
+	 *	FIXME: There can be a race here. What if the req to
+	 *	dequue happens at the same time as the DMA just moved to
+	 *	the new buffer and SW didn't yet received the interrupt?
+	 */
+	if (ch->mode & TEGRA_DMA_MODE_CONTINOUS)
+		if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL)
+			req->bytes_transferred += req_transfer_count;
+
+	req->bytes_transferred *= 4;
+
+	tegra_dma_stop(ch);
+	if (!list_empty(&ch->list)) {
+		/* if the list is not empty, queue the next request */
+		struct tegra_dma_req *next_req;
+		next_req = list_entry(ch->list.next,
+			typeof(*next_req), node);
+		tegra_dma_update_hw(ch, next_req);
+	}
+	req->status = -TEGRA_DMA_REQ_ERROR_ABORTED;
+
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+	/* Callback should be called without any lock */
+	req->complete(req);
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dma_dequeue_req);
+
+bool tegra_dma_is_empty(struct tegra_dma_channel *ch)
+{
+	unsigned long irq_flags;
+	bool is_empty;
+
+	spin_lock_irqsave(&ch->lock, irq_flags);
+	if (list_empty(&ch->list))
+		is_empty = true;
+	else
+		is_empty = false;
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
+	return is_empty;
+}
+EXPORT_SYMBOL(tegra_dma_is_empty);
+
+bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *_req)
+{
+	unsigned long irq_flags;
+	struct tegra_dma_req *req;
+
+	spin_lock_irqsave(&ch->lock, irq_flags);
+	list_for_each_entry(req, &ch->list, node) {
+		if (req == _req) {
+			spin_unlock_irqrestore(&ch->lock, irq_flags);
+			return true;
+		}
+	}
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
+	return false;
+}
+EXPORT_SYMBOL(tegra_dma_is_req_inflight);
+
+int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req)
+{
+	unsigned long irq_flags;
+	int start_dma = 0;
+
+	if (req->size > NV_DMA_MAX_TRASFER_SIZE ||
+		req->source_addr & 0x3 || req->dest_addr & 0x3) {
+		pr_err("Invalid DMA request for channel %d\n", ch->id);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&ch->lock, irq_flags);
+
+	req->bytes_transferred = 0;
+	req->status = 0;
+	req->buffer_status = 0;
+	if (list_empty(&ch->list))
+		start_dma = 1;
+
+	list_add_tail(&req->node, &ch->list);
+
+	if (start_dma)
+		tegra_dma_update_hw(ch, req);
+
+	spin_unlock_irqrestore(&ch->lock, irq_flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_dma_enqueue_req);
+
+struct tegra_dma_channel *tegra_dma_allocate_channel(int mode)
+{
+	int channel;
+	struct tegra_dma_channel *ch;
+
+	/* first channel is the shared channel */
+	if (mode & TEGRA_DMA_SHARED) {
+		channel = TEGRA_SYSTEM_DMA_CH_MIN;
+	} else {
+		channel = find_first_zero_bit(channel_usage,
+			ARRAY_SIZE(dma_channels));
+		if (channel >= ARRAY_SIZE(dma_channels))
+			return NULL;
+	}
+	__set_bit(channel, channel_usage);
+	ch = &dma_channels[channel];
+	ch->mode = mode;
+	return ch;
+}
+EXPORT_SYMBOL(tegra_dma_allocate_channel);
+
+void tegra_dma_free_channel(struct tegra_dma_channel *ch)
+{
+	if (ch->mode & TEGRA_DMA_SHARED)
+		return;
+	tegra_dma_cancel(ch);
+	__clear_bit(ch->id, channel_usage);
+}
+EXPORT_SYMBOL(tegra_dma_free_channel);
+
+static void tegra_dma_update_hw_partial(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req)
+{
+	if (req->to_memory) {
+		ch->apb_ptr = req->source_addr;
+		ch->ahb_ptr = req->dest_addr;
+	} else {
+		ch->apb_ptr = req->dest_addr;
+		ch->ahb_ptr = req->source_addr;
+	}
+	writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
+	writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+
+	req->status = TEGRA_DMA_REQ_INFLIGHT;
+	return;
+}
+
+static void tegra_dma_update_hw(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req)
+{
+	int ahb_addr_wrap;
+	int apb_addr_wrap;
+	int ahb_bus_width;
+	int apb_bus_width;
+	int index;
+	unsigned long csr;
+
+
+	ch->csr |= CSR_FLOW;
+	ch->csr &= ~CSR_REQ_SEL_MASK;
+	ch->csr |= req->req_sel << CSR_REQ_SEL_SHIFT;
+	ch->ahb_seq &= ~AHB_SEQ_BURST_MASK;
+	ch->ahb_seq |= AHB_SEQ_BURST_1;
+
+	/* One shot mode is always single buffered,
+	 * continuous mode is always double buffered
+	 * */
+	if (ch->mode & TEGRA_DMA_MODE_ONESHOT) {
+		ch->csr |= CSR_ONCE;
+		ch->ahb_seq &= ~AHB_SEQ_DBL_BUF;
+		ch->csr &= ~CSR_WCOUNT_MASK;
+		ch->csr |= ((req->size>>2) - 1) << CSR_WCOUNT_SHIFT;
+	} else {
+		ch->csr &= ~CSR_ONCE;
+		ch->ahb_seq |= AHB_SEQ_DBL_BUF;
+
+		/* In double buffered mode, we set the size to half the
+		 * requested size and interrupt when half the buffer
+		 * is full */
+		ch->csr &= ~CSR_WCOUNT_MASK;
+		ch->csr |= ((req->size>>3) - 1) << CSR_WCOUNT_SHIFT;
+	}
+
+	if (req->to_memory) {
+		ch->csr &= ~CSR_DIR;
+		ch->apb_ptr = req->source_addr;
+		ch->ahb_ptr = req->dest_addr;
+
+		apb_addr_wrap = req->source_wrap;
+		ahb_addr_wrap = req->dest_wrap;
+		apb_bus_width = req->source_bus_width;
+		ahb_bus_width = req->dest_bus_width;
+
+	} else {
+		ch->csr |= CSR_DIR;
+		ch->apb_ptr = req->dest_addr;
+		ch->ahb_ptr = req->source_addr;
+
+		apb_addr_wrap = req->dest_wrap;
+		ahb_addr_wrap = req->source_wrap;
+		apb_bus_width = req->dest_bus_width;
+		ahb_bus_width = req->source_bus_width;
+	}
+
+	apb_addr_wrap >>= 2;
+	ahb_addr_wrap >>= 2;
+
+	/* set address wrap for APB size */
+	index = 0;
+	do  {
+		if (apb_addr_wrap_table[index] == apb_addr_wrap)
+			break;
+		index++;
+	} while (index < ARRAY_SIZE(apb_addr_wrap_table));
+	BUG_ON(index == ARRAY_SIZE(apb_addr_wrap_table));
+	ch->apb_seq &= ~APB_SEQ_WRAP_MASK;
+	ch->apb_seq |= index << APB_SEQ_WRAP_SHIFT;
+
+	/* set address wrap for AHB size */
+	index = 0;
+	do  {
+		if (ahb_addr_wrap_table[index] == ahb_addr_wrap)
+			break;
+		index++;
+	} while (index < ARRAY_SIZE(ahb_addr_wrap_table));
+	BUG_ON(index == ARRAY_SIZE(ahb_addr_wrap_table));
+	ch->ahb_seq &= ~AHB_SEQ_WRAP_MASK;
+	ch->ahb_seq |= index << AHB_SEQ_WRAP_SHIFT;
+
+	for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) {
+		if (bus_width_table[index] == ahb_bus_width)
+			break;
+	}
+	BUG_ON(index == ARRAY_SIZE(bus_width_table));
+	ch->ahb_seq &= ~AHB_SEQ_BUS_WIDTH_MASK;
+	ch->ahb_seq |= index << AHB_SEQ_BUS_WIDTH_SHIFT;
+
+	for (index = 0; index < ARRAY_SIZE(bus_width_table); index++) {
+		if (bus_width_table[index] == apb_bus_width)
+			break;
+	}
+	BUG_ON(index == ARRAY_SIZE(bus_width_table));
+	ch->apb_seq &= ~APB_SEQ_BUS_WIDTH_MASK;
+	ch->apb_seq |= index << APB_SEQ_BUS_WIDTH_SHIFT;
+
+	ch->csr |= CSR_IE_EOC;
+
+	/* update hw registers with the shadow */
+	writel(ch->csr, ch->addr + APB_DMA_CHAN_CSR);
+	writel(ch->apb_seq, ch->addr + APB_DMA_CHAN_APB_SEQ);
+	writel(ch->apb_ptr, ch->addr + APB_DMA_CHAN_APB_PTR);
+	writel(ch->ahb_seq, ch->addr + APB_DMA_CHAN_AHB_SEQ);
+	writel(ch->ahb_ptr, ch->addr + APB_DMA_CHAN_AHB_PTR);
+
+	csr = ch->csr | CSR_ENB;
+	writel(csr, ch->addr + APB_DMA_CHAN_CSR);
+
+	req->status = TEGRA_DMA_REQ_INFLIGHT;
+}
+
+static void tegra_dma_init_hw(struct tegra_dma_channel *ch)
+{
+	/* One shot with an interrupt to CPU after transfer */
+	ch->csr = CSR_ONCE | CSR_IE_EOC;
+	ch->ahb_seq = AHB_SEQ_BUS_WIDTH_32 | AHB_SEQ_INTR_ENB;
+	ch->apb_seq = APB_SEQ_BUS_WIDTH_32 | 1 << APB_SEQ_WRAP_SHIFT;
+}
+
+static void handle_oneshot_dma(struct tegra_dma_channel *ch)
+{
+	struct tegra_dma_req *req;
+
+	spin_lock(&ch->lock);
+	if (list_empty(&ch->list)) {
+		spin_unlock(&ch->lock);
+		return;
+	}
+
+	req = list_entry(ch->list.next, typeof(*req), node);
+	if (req) {
+		int bytes_transferred;
+
+		bytes_transferred =
+			(ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+		bytes_transferred += 1;
+		bytes_transferred <<= 2;
+
+		list_del(&req->node);
+		req->bytes_transferred = bytes_transferred;
+		req->status = TEGRA_DMA_REQ_SUCCESS;
+
+		spin_unlock(&ch->lock);
+		/* Callback should be called without any lock */
+		pr_debug("%s: transferred %d bytes\n", __func__,
+			req->bytes_transferred);
+		req->complete(req);
+		spin_lock(&ch->lock);
+	}
+
+	if (!list_empty(&ch->list)) {
+		req = list_entry(ch->list.next, typeof(*req), node);
+		/* the complete function we just called may have enqueued
+		   another req, in which case dma has already started */
+		if (req->status != TEGRA_DMA_REQ_INFLIGHT)
+			tegra_dma_update_hw(ch, req);
+	}
+	spin_unlock(&ch->lock);
+}
+
+static void handle_continuous_dma(struct tegra_dma_channel *ch)
+{
+	struct tegra_dma_req *req;
+
+	spin_lock(&ch->lock);
+	if (list_empty(&ch->list)) {
+		spin_unlock(&ch->lock);
+		return;
+	}
+
+	req = list_entry(ch->list.next, typeof(*req), node);
+	if (req) {
+		if (req->buffer_status == TEGRA_DMA_REQ_BUF_STATUS_EMPTY) {
+			/* Load the next request into the hardware, if available
+			 * */
+			if (!list_is_last(&req->node, &ch->list)) {
+				struct tegra_dma_req *next_req;
+
+				next_req = list_entry(req->node.next,
+					typeof(*next_req), node);
+				tegra_dma_update_hw_partial(ch, next_req);
+			}
+			req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL;
+			req->status = TEGRA_DMA_REQ_SUCCESS;
+			/* DMA lock is NOT held when callback is called */
+			spin_unlock(&ch->lock);
+			if (likely(req->threshold))
+				req->threshold(req);
+			return;
+
+		} else if (req->buffer_status ==
+			TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL) {
+			/* Callback when the buffer is completely full (i.e on
+			 * the second  interrupt */
+			int bytes_transferred;
+
+			bytes_transferred =
+				(ch->csr & CSR_WCOUNT_MASK) >> CSR_WCOUNT_SHIFT;
+			bytes_transferred += 1;
+			bytes_transferred <<= 3;
+
+			req->buffer_status = TEGRA_DMA_REQ_BUF_STATUS_FULL;
+			req->bytes_transferred = bytes_transferred;
+			req->status = TEGRA_DMA_REQ_SUCCESS;
+			list_del(&req->node);
+
+			/* DMA lock is NOT held when callbak is called */
+			spin_unlock(&ch->lock);
+			req->complete(req);
+			return;
+
+		} else {
+			BUG();
+		}
+	}
+	spin_unlock(&ch->lock);
+}
+
+static irqreturn_t dma_isr(int irq, void *data)
+{
+	struct tegra_dma_channel *ch = data;
+	unsigned long status;
+
+	status = readl(ch->addr + APB_DMA_CHAN_STA);
+	if (status & STA_ISE_EOC)
+		writel(status, ch->addr + APB_DMA_CHAN_STA);
+	else {
+		pr_warning("Got a spurious ISR for DMA channel %d\n", ch->id);
+		return IRQ_HANDLED;
+	}
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t dma_thread_fn(int irq, void *data)
+{
+	struct tegra_dma_channel *ch = data;
+
+	if (ch->mode & TEGRA_DMA_MODE_ONESHOT)
+		handle_oneshot_dma(ch);
+	else
+		handle_continuous_dma(ch);
+
+
+	return IRQ_HANDLED;
+}
+
+int __init tegra_dma_init(void)
+{
+	int ret = 0;
+	int i;
+	unsigned int irq;
+	void __iomem *addr;
+
+	addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+	writel(GEN_ENABLE, addr + APB_DMA_GEN);
+	writel(0, addr + APB_DMA_CNTRL);
+	writel(0xFFFFFFFFul >> (31 - TEGRA_SYSTEM_DMA_CH_MAX),
+	       addr + APB_DMA_IRQ_MASK_SET);
+
+	memset(channel_usage, 0, sizeof(channel_usage));
+	memset(dma_channels, 0, sizeof(dma_channels));
+
+	/* Reserve all the channels we are not supposed to touch */
+	for (i = 0; i < TEGRA_SYSTEM_DMA_CH_MIN; i++)
+		__set_bit(i, channel_usage);
+
+	for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) {
+		struct tegra_dma_channel *ch = &dma_channels[i];
+
+		__clear_bit(i, channel_usage);
+
+		ch->id = i;
+		snprintf(ch->name, TEGRA_DMA_NAME_SIZE, "dma_channel_%d", i);
+
+		ch->addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE +
+			TEGRA_APB_DMA_CH0_SIZE * i);
+
+		spin_lock_init(&ch->lock);
+		INIT_LIST_HEAD(&ch->list);
+		tegra_dma_init_hw(ch);
+
+		irq = INT_APB_DMA_CH0 + i;
+		ret = request_threaded_irq(irq, dma_isr, dma_thread_fn, 0,
+			dma_channels[i].name, ch);
+		if (ret) {
+			pr_err("Failed to register IRQ %d for DMA %d\n",
+				irq, i);
+			goto fail;
+		}
+		ch->irq = irq;
+	}
+	/* mark the shared channel allocated */
+	__set_bit(TEGRA_SYSTEM_DMA_CH_MIN, channel_usage);
+
+	for (i = TEGRA_SYSTEM_DMA_CH_MAX+1; i < NV_DMA_MAX_CHANNELS; i++)
+		__set_bit(i, channel_usage);
+
+	return ret;
+fail:
+	writel(0, addr + APB_DMA_GEN);
+	for (i = TEGRA_SYSTEM_DMA_CH_MIN; i <= TEGRA_SYSTEM_DMA_CH_MAX; i++) {
+		struct tegra_dma_channel *ch = &dma_channels[i];
+		if (ch->irq)
+			free_irq(ch->irq, ch);
+	}
+	return ret;
+}
+
+#ifdef CONFIG_PM
+static u32 apb_dma[5*TEGRA_SYSTEM_DMA_CH_NR + 3];
+
+void tegra_dma_suspend(void)
+{
+	void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+	u32 *ctx = apb_dma;
+	int i;
+
+	*ctx++ = readl(addr + APB_DMA_GEN);
+	*ctx++ = readl(addr + APB_DMA_CNTRL);
+	*ctx++ = readl(addr + APB_DMA_IRQ_MASK);
+
+	for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) {
+		addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE +
+				  TEGRA_APB_DMA_CH0_SIZE * i);
+
+		*ctx++ = readl(addr + APB_DMA_CHAN_CSR);
+		*ctx++ = readl(addr + APB_DMA_CHAN_AHB_PTR);
+		*ctx++ = readl(addr + APB_DMA_CHAN_AHB_SEQ);
+		*ctx++ = readl(addr + APB_DMA_CHAN_APB_PTR);
+		*ctx++ = readl(addr + APB_DMA_CHAN_APB_SEQ);
+	}
+}
+
+void tegra_dma_resume(void)
+{
+	void __iomem *addr = IO_ADDRESS(TEGRA_APB_DMA_BASE);
+	u32 *ctx = apb_dma;
+	int i;
+
+	writel(*ctx++, addr + APB_DMA_GEN);
+	writel(*ctx++, addr + APB_DMA_CNTRL);
+	writel(*ctx++, addr + APB_DMA_IRQ_MASK);
+
+	for (i = 0; i < TEGRA_SYSTEM_DMA_CH_NR; i++) {
+		addr = IO_ADDRESS(TEGRA_APB_DMA_CH0_BASE +
+				  TEGRA_APB_DMA_CH0_SIZE * i);
+
+		writel(*ctx++, addr + APB_DMA_CHAN_CSR);
+		writel(*ctx++, addr + APB_DMA_CHAN_AHB_PTR);
+		writel(*ctx++, addr + APB_DMA_CHAN_AHB_SEQ);
+		writel(*ctx++, addr + APB_DMA_CHAN_APB_PTR);
+		writel(*ctx++, addr + APB_DMA_CHAN_APB_SEQ);
+	}
+}
+
+#endif
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
new file mode 100644
index 0000000000000000000000000000000000000000..1fa26d9a1a6810cfbeb0cfb0ce3f86f62bc9e849
--- /dev/null
+++ b/arch/arm/mach-tegra/fuse.c
@@ -0,0 +1,84 @@
+/*
+ * arch/arm/mach-tegra/fuse.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@android.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 <linux/io.h>
+
+#include <mach/iomap.h>
+
+#include "fuse.h"
+
+#define FUSE_UID_LOW		0x108
+#define FUSE_UID_HIGH		0x10c
+#define FUSE_SKU_INFO		0x110
+#define FUSE_SPARE_BIT		0x200
+
+static inline u32 fuse_readl(unsigned long offset)
+{
+	return readl(IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
+}
+
+static inline void fuse_writel(u32 value, unsigned long offset)
+{
+	writel(value, IO_TO_VIRT(TEGRA_FUSE_BASE + offset));
+}
+
+void tegra_init_fuse(void)
+{
+	u32 reg = readl(IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
+	reg |= 1 << 28;
+	writel(reg, IO_TO_VIRT(TEGRA_CLK_RESET_BASE + 0x48));
+
+	pr_info("Tegra SKU: %d CPU Process: %d Core Process: %d\n",
+		tegra_sku_id(), tegra_cpu_process_id(),
+		tegra_core_process_id());
+}
+
+unsigned long long tegra_chip_uid(void)
+{
+	unsigned long long lo, hi;
+
+	lo = fuse_readl(FUSE_UID_LOW);
+	hi = fuse_readl(FUSE_UID_HIGH);
+	return (hi << 32ull) | lo;
+}
+
+int tegra_sku_id(void)
+{
+	int sku_id;
+	u32 reg = fuse_readl(FUSE_SKU_INFO);
+	sku_id = reg & 0xFF;
+	return sku_id;
+}
+
+int tegra_cpu_process_id(void)
+{
+	int cpu_process_id;
+	u32 reg = fuse_readl(FUSE_SPARE_BIT);
+	cpu_process_id = (reg >> 6) & 3;
+	return cpu_process_id;
+}
+
+int tegra_core_process_id(void)
+{
+	int core_process_id;
+	u32 reg = fuse_readl(FUSE_SPARE_BIT);
+	core_process_id = (reg >> 12) & 3;
+	return core_process_id;
+}
diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h
new file mode 100644
index 0000000000000000000000000000000000000000..584b2e27dbda5679078ce2f60d552f98a4c13f3c
--- /dev/null
+++ b/arch/arm/mach-tegra/fuse.h
@@ -0,0 +1,24 @@
+/*
+ * arch/arm/mach-tegra/fuse.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *	Colin Cross <ccross@android.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.
+ *
+ */
+
+unsigned long long tegra_chip_uid(void);
+int tegra_sku_id(void);
+int tegra_cpu_process_id(void);
+int tegra_core_process_id(void);
+void tegra_init_fuse(void);
diff --git a/arch/arm/mach-tegra/gpio.c b/arch/arm/mach-tegra/gpio.c
index fe78fba25f3c7c39fdc494f3519ed956e76cbb5a..0775265e69f5e8fa7e67acef442f1ff9c02607a3 100644
--- a/arch/arm/mach-tegra/gpio.c
+++ b/arch/arm/mach-tegra/gpio.c
@@ -19,6 +19,7 @@
 
 #include <linux/init.h>
 #include <linux/irq.h>
+#include <linux/interrupt.h>
 
 #include <linux/io.h>
 #include <linux/gpio.h>
@@ -60,6 +61,13 @@ struct tegra_gpio_bank {
 	int bank;
 	int irq;
 	spinlock_t lvl_lock[4];
+#ifdef CONFIG_PM
+	u32 cnf[4];
+	u32 out[4];
+	u32 oe[4];
+	u32 int_enb[4];
+	u32 int_lvl[4];
+#endif
 };
 
 
@@ -131,7 +139,7 @@ static struct gpio_chip tegra_gpio_chip = {
 	.direction_output	= tegra_gpio_direction_output,
 	.set			= tegra_gpio_set,
 	.base			= 0,
-	.ngpio			= ARCH_NR_GPIOS,
+	.ngpio			= TEGRA_NR_GPIOS,
 };
 
 static void tegra_gpio_irq_ack(unsigned int irq)
@@ -244,6 +252,76 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
 
 }
 
+#ifdef CONFIG_PM
+void tegra_gpio_resume(void)
+{
+	unsigned long flags;
+	int b, p, i;
+
+	local_irq_save(flags);
+
+	for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+		struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+		for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+			unsigned int gpio = (b<<5) | (p<<3);
+			__raw_writel(bank->cnf[p], GPIO_CNF(gpio));
+			__raw_writel(bank->out[p], GPIO_OUT(gpio));
+			__raw_writel(bank->oe[p], GPIO_OE(gpio));
+			__raw_writel(bank->int_lvl[p], GPIO_INT_LVL(gpio));
+			__raw_writel(bank->int_enb[p], GPIO_INT_ENB(gpio));
+		}
+	}
+
+	local_irq_restore(flags);
+
+	for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) {
+		struct irq_desc *desc = irq_to_desc(i);
+		if (!desc || (desc->status & IRQ_WAKEUP))
+			continue;
+		enable_irq(i);
+	}
+}
+
+void tegra_gpio_suspend(void)
+{
+	unsigned long flags;
+	int b, p, i;
+
+	for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) {
+		struct irq_desc *desc = irq_to_desc(i);
+		if (!desc)
+			continue;
+		if (desc->status & IRQ_WAKEUP) {
+			int gpio = i - INT_GPIO_BASE;
+			pr_debug("gpio %d.%d is wakeup\n", gpio/8, gpio&7);
+			continue;
+		}
+		disable_irq(i);
+	}
+
+	local_irq_save(flags);
+	for (b = 0; b < ARRAY_SIZE(tegra_gpio_banks); b++) {
+		struct tegra_gpio_bank *bank = &tegra_gpio_banks[b];
+
+		for (p = 0; p < ARRAY_SIZE(bank->oe); p++) {
+			unsigned int gpio = (b<<5) | (p<<3);
+			bank->cnf[p] = __raw_readl(GPIO_CNF(gpio));
+			bank->out[p] = __raw_readl(GPIO_OUT(gpio));
+			bank->oe[p] = __raw_readl(GPIO_OE(gpio));
+			bank->int_enb[p] = __raw_readl(GPIO_INT_ENB(gpio));
+			bank->int_lvl[p] = __raw_readl(GPIO_INT_LVL(gpio));
+		}
+	}
+	local_irq_restore(flags);
+}
+
+static int tegra_gpio_wake_enable(unsigned int irq, unsigned int enable)
+{
+	struct tegra_gpio_bank *bank = get_irq_chip_data(irq);
+	return set_irq_wake(bank->irq, enable);
+}
+#endif
 
 static struct irq_chip tegra_gpio_irq_chip = {
 	.name		= "GPIO",
@@ -251,6 +329,9 @@ static struct irq_chip tegra_gpio_irq_chip = {
 	.mask		= tegra_gpio_irq_mask,
 	.unmask		= tegra_gpio_irq_unmask,
 	.set_type	= tegra_gpio_irq_set_type,
+#ifdef CONFIG_PM
+	.set_wake	= tegra_gpio_wake_enable,
+#endif
 };
 
 
@@ -274,7 +355,7 @@ static int __init tegra_gpio_init(void)
 
 	gpiochip_add(&tegra_gpio_chip);
 
-	for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + ARCH_NR_GPIOS); i++) {
+	for (i = INT_GPIO_BASE; i < (INT_GPIO_BASE + TEGRA_NR_GPIOS); i++) {
 		bank = &tegra_gpio_banks[GPIO_BANK(irq_to_gpio(i))];
 
 		lockdep_set_class(&irq_desc[i].lock, &gpio_lock_class);
@@ -312,15 +393,16 @@ static int dbg_gpio_show(struct seq_file *s, void *unused)
 	for (i = 0; i < 7; i++) {
 		for (j = 0; j < 4; j++) {
 			int gpio = tegra_gpio_compose(i, j, 0);
-			seq_printf(s, "%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
-			       i, j,
-			       __raw_readl(GPIO_CNF(gpio)),
-			       __raw_readl(GPIO_OE(gpio)),
-			       __raw_readl(GPIO_OUT(gpio)),
-			       __raw_readl(GPIO_IN(gpio)),
-			       __raw_readl(GPIO_INT_STA(gpio)),
-			       __raw_readl(GPIO_INT_ENB(gpio)),
-			       __raw_readl(GPIO_INT_LVL(gpio)));
+			seq_printf(s,
+				"%d:%d %02x %02x %02x %02x %02x %02x %06x\n",
+				i, j,
+				__raw_readl(GPIO_CNF(gpio)),
+				__raw_readl(GPIO_OE(gpio)),
+				__raw_readl(GPIO_OUT(gpio)),
+				__raw_readl(GPIO_IN(gpio)),
+				__raw_readl(GPIO_INT_STA(gpio)),
+				__raw_readl(GPIO_INT_ENB(gpio)),
+				__raw_readl(GPIO_INT_LVL(gpio)));
 		}
 	}
 	return 0;
diff --git a/arch/arm/mach-tegra/include/mach/clk.h b/arch/arm/mach-tegra/include/mach/clk.h
index 2896f25ebfb57f0ce29cc43a4965763284166422..d7723955dac7486519618d3e2c3715267eee667d 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/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
new file mode 100644
index 0000000000000000000000000000000000000000..39011bd9a925f547d190a51cb8db710553dc3772
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -0,0 +1,155 @@
+/*
+ * arch/arm/mach-tegra/include/mach/dma.h
+ *
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __MACH_TEGRA_DMA_H
+#define __MACH_TEGRA_DMA_H
+
+#include <linux/list.h>
+
+#if defined(CONFIG_TEGRA_SYSTEM_DMA)
+
+struct tegra_dma_req;
+struct tegra_dma_channel;
+
+#define TEGRA_DMA_REQ_SEL_CNTR			0
+#define TEGRA_DMA_REQ_SEL_I2S_2			1
+#define TEGRA_DMA_REQ_SEL_I2S_1			2
+#define TEGRA_DMA_REQ_SEL_SPD_I			3
+#define TEGRA_DMA_REQ_SEL_UI_I			4
+#define TEGRA_DMA_REQ_SEL_MIPI			5
+#define TEGRA_DMA_REQ_SEL_I2S2_2		6
+#define TEGRA_DMA_REQ_SEL_I2S2_1		7
+#define TEGRA_DMA_REQ_SEL_UARTA			8
+#define TEGRA_DMA_REQ_SEL_UARTB			9
+#define TEGRA_DMA_REQ_SEL_UARTC			10
+#define TEGRA_DMA_REQ_SEL_SPI			11
+#define TEGRA_DMA_REQ_SEL_AC97			12
+#define TEGRA_DMA_REQ_SEL_ACMODEM		13
+#define TEGRA_DMA_REQ_SEL_SL4B			14
+#define TEGRA_DMA_REQ_SEL_SL2B1			15
+#define TEGRA_DMA_REQ_SEL_SL2B2			16
+#define TEGRA_DMA_REQ_SEL_SL2B3			17
+#define TEGRA_DMA_REQ_SEL_SL2B4			18
+#define TEGRA_DMA_REQ_SEL_UARTD			19
+#define TEGRA_DMA_REQ_SEL_UARTE			20
+#define TEGRA_DMA_REQ_SEL_I2C			21
+#define TEGRA_DMA_REQ_SEL_I2C2			22
+#define TEGRA_DMA_REQ_SEL_I2C3			23
+#define TEGRA_DMA_REQ_SEL_DVC_I2C		24
+#define TEGRA_DMA_REQ_SEL_OWR			25
+#define TEGRA_DMA_REQ_SEL_INVALID		31
+
+enum tegra_dma_mode {
+	TEGRA_DMA_SHARED = 1,
+	TEGRA_DMA_MODE_CONTINOUS = 2,
+	TEGRA_DMA_MODE_ONESHOT = 4,
+};
+
+enum tegra_dma_req_error {
+	TEGRA_DMA_REQ_SUCCESS = 0,
+	TEGRA_DMA_REQ_ERROR_ABORTED,
+	TEGRA_DMA_REQ_INFLIGHT,
+};
+
+enum tegra_dma_req_buff_status {
+	TEGRA_DMA_REQ_BUF_STATUS_EMPTY = 0,
+	TEGRA_DMA_REQ_BUF_STATUS_HALF_FULL,
+	TEGRA_DMA_REQ_BUF_STATUS_FULL,
+};
+
+struct tegra_dma_req {
+	struct list_head node;
+	unsigned int modid;
+	int instance;
+
+	/* Called when the req is complete and from the DMA ISR context.
+	 * When this is called the req structure is no longer queued by
+	 * the DMA channel.
+	 *
+	 * State of the DMA depends on the number of req it has. If there are
+	 * no DMA requests queued up, then it will STOP the DMA. It there are
+	 * more requests in the DMA, then it will queue the next request.
+	 */
+	void (*complete)(struct tegra_dma_req *req);
+
+	/*  This is a called from the DMA ISR context when the DMA is still in
+	 *  progress and is actively filling same buffer.
+	 *
+	 *  In case of continous mode receive, this threshold is 1/2 the buffer
+	 *  size. In other cases, this will not even be called as there is no
+	 *  hardware support for it.
+	 *
+	 * In the case of continous mode receive, if there is next req already
+	 * queued, DMA programs the HW to use that req when this req is
+	 * completed. If there is no "next req" queued, then DMA ISR doesn't do
+	 * anything before calling this callback.
+	 *
+	 *	This is mainly used by the cases, where the clients has queued
+	 *	only one req and want to get some sort of DMA threshold
+	 *	callback to program the next buffer.
+	 *
+	 */
+	void (*threshold)(struct tegra_dma_req *req);
+
+	/* 1 to copy to memory.
+	 * 0 to copy from the memory to device FIFO */
+	int to_memory;
+
+	void *virt_addr;
+
+	unsigned long source_addr;
+	unsigned long dest_addr;
+	unsigned long dest_wrap;
+	unsigned long source_wrap;
+	unsigned long source_bus_width;
+	unsigned long dest_bus_width;
+	unsigned long req_sel;
+	unsigned int size;
+
+	/* Updated by the DMA driver on the conpletion of the request. */
+	int bytes_transferred;
+	int status;
+
+	/* DMA completion tracking information */
+	int buffer_status;
+
+	/* Client specific data */
+	void *dev;
+};
+
+int tegra_dma_enqueue_req(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req);
+int tegra_dma_dequeue_req(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req);
+void tegra_dma_dequeue(struct tegra_dma_channel *ch);
+void tegra_dma_flush(struct tegra_dma_channel *ch);
+
+bool tegra_dma_is_req_inflight(struct tegra_dma_channel *ch,
+	struct tegra_dma_req *req);
+bool tegra_dma_is_empty(struct tegra_dma_channel *ch);
+
+struct tegra_dma_channel *tegra_dma_allocate_channel(int mode);
+void tegra_dma_free_channel(struct tegra_dma_channel *ch);
+
+int __init tegra_dma_init(void);
+
+#endif
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/gpio.h b/arch/arm/mach-tegra/include/mach/gpio.h
index 540e822e50f7c15c0afa4799f060d5c33c638fb3..e31f486d69a2d232768f1dd3e959d25cecb2da6e 100644
--- a/arch/arm/mach-tegra/include/mach/gpio.h
+++ b/arch/arm/mach-tegra/include/mach/gpio.h
@@ -22,7 +22,7 @@
 
 #include <mach/irqs.h>
 
-#define ARCH_NR_GPIOS		INT_GPIO_NR
+#define TEGRA_NR_GPIOS		INT_GPIO_NR
 
 #include <asm-generic/gpio.h>
 
@@ -35,7 +35,7 @@
 
 static inline int gpio_to_irq(unsigned int gpio)
 {
-	if (gpio < ARCH_NR_GPIOS)
+	if (gpio < TEGRA_NR_GPIOS)
 		return INT_GPIO_BASE + gpio;
 	return -EINVAL;
 }
diff --git a/arch/arm/mach-tegra/include/mach/hardware.h b/arch/arm/mach-tegra/include/mach/hardware.h
index 6014edf60d9353d53ecea8fe08b0d5c7838b4719..56e43b3a5b97516161523f91a968e07368c1d71b 100644
--- a/arch/arm/mach-tegra/include/mach/hardware.h
+++ b/arch/arm/mach-tegra/include/mach/hardware.h
@@ -21,4 +21,8 @@
 #ifndef __MACH_TEGRA_HARDWARE_H
 #define __MACH_TEGRA_HARDWARE_H
 
+#define PCIBIOS_MIN_IO			0x1000
+#define PCIBIOS_MIN_MEM			0
+#define pcibios_assign_all_busses()	1
+
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/io.h b/arch/arm/mach-tegra/include/mach/io.h
index 35edfc32ffc9d96280f43f47da841d63ff0fdc32..f0981b1ac59eea58d65a1bbd251ad660deb80a0f 100644
--- a/arch/arm/mach-tegra/include/mach/io.h
+++ b/arch/arm/mach-tegra/include/mach/io.h
@@ -21,7 +21,7 @@
 #ifndef __MACH_TEGRA_IO_H
 #define __MACH_TEGRA_IO_H
 
-#define IO_SPACE_LIMIT 0xffffffff
+#define IO_SPACE_LIMIT 0xffff
 
 /* On TEGRA, many peripherals are very closely packed in
  * two 256MB io windows (that actually only use about 64KB
@@ -33,6 +33,10 @@
  *
  */
 
+#define IO_IRAM_PHYS	0x40000000
+#define IO_IRAM_VIRT	0xFE400000
+#define IO_IRAM_SIZE	SZ_256K
+
 #define IO_CPU_PHYS     0x50040000
 #define IO_CPU_VIRT     0xFE000000
 #define IO_CPU_SIZE	SZ_16K
@@ -55,6 +59,8 @@
 		IO_TO_VIRT_XLATE((n), IO_APB_PHYS, IO_APB_VIRT) :	\
 	IO_TO_VIRT_BETWEEN((n), IO_CPU_PHYS, IO_CPU_SIZE) ?		\
 		IO_TO_VIRT_XLATE((n), IO_CPU_PHYS, IO_CPU_VIRT) :	\
+	IO_TO_VIRT_BETWEEN((n), IO_IRAM_PHYS, IO_IRAM_SIZE) ?		\
+		IO_TO_VIRT_XLATE((n), IO_IRAM_PHYS, IO_IRAM_VIRT) :	\
 	0)
 
 #ifndef __ASSEMBLER__
@@ -67,10 +73,20 @@ void tegra_iounmap(volatile void __iomem *addr);
 
 #define IO_ADDRESS(n) ((void __iomem *) IO_TO_VIRT(n))
 
+#ifdef CONFIG_TEGRA_PCI
+extern void __iomem *tegra_pcie_io_base;
+
+static inline void __iomem *__io(unsigned long addr)
+{
+	return tegra_pcie_io_base + (addr & IO_SPACE_LIMIT);
+}
+#else
 static inline void __iomem *__io(unsigned long addr)
 {
 	return (void __iomem *)addr;
 }
+#endif
+
 #define __io(a)         __io(a)
 #define __mem_pci(a)    (a)
 
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index 1741f7dd7a9b71183c98b53cab88e688aa16b031..44a4f4bcf91f0e47bbbdddb7a667366e0588a607 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -23,9 +23,15 @@
 
 #include <asm/sizes.h>
 
+#define TEGRA_IRAM_BASE			0x40000000
+#define TEGRA_IRAM_SIZE			SZ_256K
+
 #define TEGRA_ARM_PERIF_BASE		0x50040000
 #define TEGRA_ARM_PERIF_SIZE		SZ_8K
 
+#define TEGRA_ARM_PL310_BASE		0x50043000
+#define TEGRA_ARM_PL310_SIZE		SZ_4K
+
 #define TEGRA_ARM_INT_DIST_BASE		0x50041000
 #define TEGRA_ARM_INT_DIST_SIZE		SZ_4K
 
@@ -68,7 +74,22 @@
 #define TEGRA_FLOW_CTRL_BASE		0x60007000
 #define TEGRA_FLOW_CTRL_SIZE		20
 
-#define TEGRA_STATMON_BASE		0x6000C4000
+#define TEGRA_AHB_DMA_BASE		0x60008000
+#define TEGRA_AHB_DMA_SIZE		SZ_4K
+
+#define TEGRA_AHB_DMA_CH0_BASE		0x60009000
+#define TEGRA_AHB_DMA_CH0_SIZE		32
+
+#define TEGRA_APB_DMA_BASE		0x6000A000
+#define TEGRA_APB_DMA_SIZE		SZ_4K
+
+#define TEGRA_APB_DMA_CH0_BASE		0x6000B000
+#define TEGRA_APB_DMA_CH0_SIZE		32
+
+#define TEGRA_AHB_GIZMO_BASE		0x6000C004
+#define TEGRA_AHB_GIZMO_SIZE		0x10C
+
+#define TEGRA_STATMON_BASE		0x6000C400
 #define TEGRA_STATMON_SIZE		SZ_1K
 
 #define TEGRA_GPIO_BASE			0x6000D000
@@ -137,7 +158,7 @@
 #define TEGRA_I2C3_BASE			0x7000C500
 #define TEGRA_I2C3_SIZE			SZ_256
 
-#define TEGRA_OWR_BASE			0x7000D000
+#define TEGRA_OWR_BASE			0x7000C600
 #define TEGRA_OWR_SIZE			80
 
 #define TEGRA_DVC_BASE			0x7000D000
@@ -182,12 +203,12 @@
 #define TEGRA_USB_BASE			0xC5000000
 #define TEGRA_USB_SIZE			SZ_16K
 
-#define TEGRA_USB1_BASE			0xC5004000
-#define TEGRA_USB1_SIZE			SZ_16K
-
-#define TEGRA_USB2_BASE			0xC5008000
+#define TEGRA_USB2_BASE			0xC5004000
 #define TEGRA_USB2_SIZE			SZ_16K
 
+#define TEGRA_USB3_BASE			0xC5008000
+#define TEGRA_USB3_SIZE			SZ_16K
+
 #define TEGRA_SDMMC1_BASE		0xC8000000
 #define TEGRA_SDMMC1_SIZE		SZ_512
 
diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h
index 20f640edaa0d31e0247681db1dc68ca53125cb79..71bbf34229538e87b81d0bca740eeb952c712bd4 100644
--- a/arch/arm/mach-tegra/include/mach/irqs.h
+++ b/arch/arm/mach-tegra/include/mach/irqs.h
@@ -25,6 +25,7 @@
 
 #define IRQ_LOCALTIMER                  29
 
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
 /* Primary Interrupt Controller */
 #define INT_PRI_BASE			(INT_GIC_BASE + 32)
 #define INT_TMR1			(INT_PRI_BASE + 0)
@@ -169,5 +170,6 @@
 #define INT_GPIO_NR			(28 * 8)
 
 #define NR_IRQS				(INT_GPIO_BASE + INT_GPIO_NR)
+#endif
 
 #endif
diff --git a/arch/arm/mach-tegra/include/mach/legacy_irq.h b/arch/arm/mach-tegra/include/mach/legacy_irq.h
new file mode 100644
index 0000000000000000000000000000000000000000..db1eb3dd04c8a55cc1152270bb6c0829f9be7c6a
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/legacy_irq.h
@@ -0,0 +1,31 @@
+/*
+ * arch/arm/mach-tegra/include/mach/legacy_irq.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@android.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.
+ *
+ */
+
+#ifndef _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H
+#define _ARCH_ARM_MACH_TEGRA_LEGARY_IRQ_H
+
+void tegra_legacy_mask_irq(unsigned int irq);
+void tegra_legacy_unmask_irq(unsigned int irq);
+void tegra_legacy_select_fiq(unsigned int irq, bool fiq);
+void tegra_legacy_force_irq_set(unsigned int irq);
+void tegra_legacy_force_irq_clr(unsigned int irq);
+int tegra_legacy_force_irq_status(unsigned int irq);
+void tegra_legacy_select_fiq(unsigned int irq, bool fiq);
+unsigned long tegra_legacy_vfiq(int nr);
+unsigned long tegra_legacy_class(int nr);
+
+#endif
diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t2.h b/arch/arm/mach-tegra/include/mach/pinmux-t2.h
new file mode 100644
index 0000000000000000000000000000000000000000..e5b9d740f9738cf678eddfc65702433a0c9c3662
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/pinmux-t2.h
@@ -0,0 +1,174 @@
+/*
+ * linux/arch/arm/mach-tegra/include/mach/pinmux-t2.h
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __MACH_TEGRA_PINMUX_T2_H
+#define __MACH_TEGRA_PINMUX_T2_H
+
+enum tegra_pingroup {
+	TEGRA_PINGROUP_ATA = 0,
+	TEGRA_PINGROUP_ATB,
+	TEGRA_PINGROUP_ATC,
+	TEGRA_PINGROUP_ATD,
+	TEGRA_PINGROUP_ATE,
+	TEGRA_PINGROUP_CDEV1,
+	TEGRA_PINGROUP_CDEV2,
+	TEGRA_PINGROUP_CRTP,
+	TEGRA_PINGROUP_CSUS,
+	TEGRA_PINGROUP_DAP1,
+	TEGRA_PINGROUP_DAP2,
+	TEGRA_PINGROUP_DAP3,
+	TEGRA_PINGROUP_DAP4,
+	TEGRA_PINGROUP_DDC,
+	TEGRA_PINGROUP_DTA,
+	TEGRA_PINGROUP_DTB,
+	TEGRA_PINGROUP_DTC,
+	TEGRA_PINGROUP_DTD,
+	TEGRA_PINGROUP_DTE,
+	TEGRA_PINGROUP_DTF,
+	TEGRA_PINGROUP_GMA,
+	TEGRA_PINGROUP_GMB,
+	TEGRA_PINGROUP_GMC,
+	TEGRA_PINGROUP_GMD,
+	TEGRA_PINGROUP_GME,
+	TEGRA_PINGROUP_GPU,
+	TEGRA_PINGROUP_GPU7,
+	TEGRA_PINGROUP_GPV,
+	TEGRA_PINGROUP_HDINT,
+	TEGRA_PINGROUP_I2CP,
+	TEGRA_PINGROUP_IRRX,
+	TEGRA_PINGROUP_IRTX,
+	TEGRA_PINGROUP_KBCA,
+	TEGRA_PINGROUP_KBCB,
+	TEGRA_PINGROUP_KBCC,
+	TEGRA_PINGROUP_KBCD,
+	TEGRA_PINGROUP_KBCE,
+	TEGRA_PINGROUP_KBCF,
+	TEGRA_PINGROUP_LCSN,
+	TEGRA_PINGROUP_LD0,
+	TEGRA_PINGROUP_LD1,
+	TEGRA_PINGROUP_LD10,
+	TEGRA_PINGROUP_LD11,
+	TEGRA_PINGROUP_LD12,
+	TEGRA_PINGROUP_LD13,
+	TEGRA_PINGROUP_LD14,
+	TEGRA_PINGROUP_LD15,
+	TEGRA_PINGROUP_LD16,
+	TEGRA_PINGROUP_LD17,
+	TEGRA_PINGROUP_LD2,
+	TEGRA_PINGROUP_LD3,
+	TEGRA_PINGROUP_LD4,
+	TEGRA_PINGROUP_LD5,
+	TEGRA_PINGROUP_LD6,
+	TEGRA_PINGROUP_LD7,
+	TEGRA_PINGROUP_LD8,
+	TEGRA_PINGROUP_LD9,
+	TEGRA_PINGROUP_LDC,
+	TEGRA_PINGROUP_LDI,
+	TEGRA_PINGROUP_LHP0,
+	TEGRA_PINGROUP_LHP1,
+	TEGRA_PINGROUP_LHP2,
+	TEGRA_PINGROUP_LHS,
+	TEGRA_PINGROUP_LM0,
+	TEGRA_PINGROUP_LM1,
+	TEGRA_PINGROUP_LPP,
+	TEGRA_PINGROUP_LPW0,
+	TEGRA_PINGROUP_LPW1,
+	TEGRA_PINGROUP_LPW2,
+	TEGRA_PINGROUP_LSC0,
+	TEGRA_PINGROUP_LSC1,
+	TEGRA_PINGROUP_LSCK,
+	TEGRA_PINGROUP_LSDA,
+	TEGRA_PINGROUP_LSDI,
+	TEGRA_PINGROUP_LSPI,
+	TEGRA_PINGROUP_LVP0,
+	TEGRA_PINGROUP_LVP1,
+	TEGRA_PINGROUP_LVS,
+	TEGRA_PINGROUP_OWC,
+	TEGRA_PINGROUP_PMC,
+	TEGRA_PINGROUP_PTA,
+	TEGRA_PINGROUP_RM,
+	TEGRA_PINGROUP_SDB,
+	TEGRA_PINGROUP_SDC,
+	TEGRA_PINGROUP_SDD,
+	TEGRA_PINGROUP_SDIO1,
+	TEGRA_PINGROUP_SLXA,
+	TEGRA_PINGROUP_SLXC,
+	TEGRA_PINGROUP_SLXD,
+	TEGRA_PINGROUP_SLXK,
+	TEGRA_PINGROUP_SPDI,
+	TEGRA_PINGROUP_SPDO,
+	TEGRA_PINGROUP_SPIA,
+	TEGRA_PINGROUP_SPIB,
+	TEGRA_PINGROUP_SPIC,
+	TEGRA_PINGROUP_SPID,
+	TEGRA_PINGROUP_SPIE,
+	TEGRA_PINGROUP_SPIF,
+	TEGRA_PINGROUP_SPIG,
+	TEGRA_PINGROUP_SPIH,
+	TEGRA_PINGROUP_UAA,
+	TEGRA_PINGROUP_UAB,
+	TEGRA_PINGROUP_UAC,
+	TEGRA_PINGROUP_UAD,
+	TEGRA_PINGROUP_UCA,
+	TEGRA_PINGROUP_UCB,
+	TEGRA_PINGROUP_UDA,
+	/* these pin groups only have pullup and pull down control */
+	TEGRA_PINGROUP_CK32,
+	TEGRA_PINGROUP_DDRC,
+	TEGRA_PINGROUP_PMCA,
+	TEGRA_PINGROUP_PMCB,
+	TEGRA_PINGROUP_PMCC,
+	TEGRA_PINGROUP_PMCD,
+	TEGRA_PINGROUP_PMCE,
+	TEGRA_PINGROUP_XM2C,
+	TEGRA_PINGROUP_XM2D,
+	TEGRA_MAX_PINGROUP,
+};
+
+enum tegra_drive_pingroup {
+	TEGRA_DRIVE_PINGROUP_AO1 = 0,
+	TEGRA_DRIVE_PINGROUP_AO2,
+	TEGRA_DRIVE_PINGROUP_AT1,
+	TEGRA_DRIVE_PINGROUP_AT2,
+	TEGRA_DRIVE_PINGROUP_CDEV1,
+	TEGRA_DRIVE_PINGROUP_CDEV2,
+	TEGRA_DRIVE_PINGROUP_CSUS,
+	TEGRA_DRIVE_PINGROUP_DAP1,
+	TEGRA_DRIVE_PINGROUP_DAP2,
+	TEGRA_DRIVE_PINGROUP_DAP3,
+	TEGRA_DRIVE_PINGROUP_DAP4,
+	TEGRA_DRIVE_PINGROUP_DBG,
+	TEGRA_DRIVE_PINGROUP_LCD1,
+	TEGRA_DRIVE_PINGROUP_LCD2,
+	TEGRA_DRIVE_PINGROUP_SDMMC2,
+	TEGRA_DRIVE_PINGROUP_SDMMC3,
+	TEGRA_DRIVE_PINGROUP_SPI,
+	TEGRA_DRIVE_PINGROUP_UAA,
+	TEGRA_DRIVE_PINGROUP_UAB,
+	TEGRA_DRIVE_PINGROUP_UART2,
+	TEGRA_DRIVE_PINGROUP_UART3,
+	TEGRA_DRIVE_PINGROUP_VI1,
+	TEGRA_DRIVE_PINGROUP_VI2,
+	TEGRA_DRIVE_PINGROUP_XM2A,
+	TEGRA_DRIVE_PINGROUP_XM2C,
+	TEGRA_DRIVE_PINGROUP_XM2D,
+	TEGRA_DRIVE_PINGROUP_XM2CLK,
+	TEGRA_DRIVE_PINGROUP_MEMCOMP,
+	TEGRA_MAX_DRIVE_PINGROUP,
+};
+
+#endif
+
diff --git a/arch/arm/mach-tegra/include/mach/pinmux.h b/arch/arm/mach-tegra/include/mach/pinmux.h
index 41c8ce5b7c27564b7621397a86fe0b2722a1e48f..defd8775defa04d18f66cbe3b7106811a2c0fcd2 100644
--- a/arch/arm/mach-tegra/include/mach/pinmux.h
+++ b/arch/arm/mach-tegra/include/mach/pinmux.h
@@ -17,126 +17,11 @@
 #ifndef __MACH_TEGRA_PINMUX_H
 #define __MACH_TEGRA_PINMUX_H
 
-enum tegra_pingroup {
-	TEGRA_PINGROUP_ATA = 0,
-	TEGRA_PINGROUP_ATB,
-	TEGRA_PINGROUP_ATC,
-	TEGRA_PINGROUP_ATD,
-	TEGRA_PINGROUP_ATE,
-	TEGRA_PINGROUP_CDEV1,
-	TEGRA_PINGROUP_CDEV2,
-	TEGRA_PINGROUP_CRTP,
-	TEGRA_PINGROUP_CSUS,
-	TEGRA_PINGROUP_DAP1,
-	TEGRA_PINGROUP_DAP2,
-	TEGRA_PINGROUP_DAP3,
-	TEGRA_PINGROUP_DAP4,
-	TEGRA_PINGROUP_DDC,
-	TEGRA_PINGROUP_DTA,
-	TEGRA_PINGROUP_DTB,
-	TEGRA_PINGROUP_DTC,
-	TEGRA_PINGROUP_DTD,
-	TEGRA_PINGROUP_DTE,
-	TEGRA_PINGROUP_DTF,
-	TEGRA_PINGROUP_GMA,
-	TEGRA_PINGROUP_GMB,
-	TEGRA_PINGROUP_GMC,
-	TEGRA_PINGROUP_GMD,
-	TEGRA_PINGROUP_GME,
-	TEGRA_PINGROUP_GPU,
-	TEGRA_PINGROUP_GPU7,
-	TEGRA_PINGROUP_GPV,
-	TEGRA_PINGROUP_HDINT,
-	TEGRA_PINGROUP_I2CP,
-	TEGRA_PINGROUP_IRRX,
-	TEGRA_PINGROUP_IRTX,
-	TEGRA_PINGROUP_KBCA,
-	TEGRA_PINGROUP_KBCB,
-	TEGRA_PINGROUP_KBCC,
-	TEGRA_PINGROUP_KBCD,
-	TEGRA_PINGROUP_KBCE,
-	TEGRA_PINGROUP_KBCF,
-	TEGRA_PINGROUP_LCSN,
-	TEGRA_PINGROUP_LD0,
-	TEGRA_PINGROUP_LD1,
-	TEGRA_PINGROUP_LD10,
-	TEGRA_PINGROUP_LD11,
-	TEGRA_PINGROUP_LD12,
-	TEGRA_PINGROUP_LD13,
-	TEGRA_PINGROUP_LD14,
-	TEGRA_PINGROUP_LD15,
-	TEGRA_PINGROUP_LD16,
-	TEGRA_PINGROUP_LD17,
-	TEGRA_PINGROUP_LD2,
-	TEGRA_PINGROUP_LD3,
-	TEGRA_PINGROUP_LD4,
-	TEGRA_PINGROUP_LD5,
-	TEGRA_PINGROUP_LD6,
-	TEGRA_PINGROUP_LD7,
-	TEGRA_PINGROUP_LD8,
-	TEGRA_PINGROUP_LD9,
-	TEGRA_PINGROUP_LDC,
-	TEGRA_PINGROUP_LDI,
-	TEGRA_PINGROUP_LHP0,
-	TEGRA_PINGROUP_LHP1,
-	TEGRA_PINGROUP_LHP2,
-	TEGRA_PINGROUP_LHS,
-	TEGRA_PINGROUP_LM0,
-	TEGRA_PINGROUP_LM1,
-	TEGRA_PINGROUP_LPP,
-	TEGRA_PINGROUP_LPW0,
-	TEGRA_PINGROUP_LPW1,
-	TEGRA_PINGROUP_LPW2,
-	TEGRA_PINGROUP_LSC0,
-	TEGRA_PINGROUP_LSC1,
-	TEGRA_PINGROUP_LSCK,
-	TEGRA_PINGROUP_LSDA,
-	TEGRA_PINGROUP_LSDI,
-	TEGRA_PINGROUP_LSPI,
-	TEGRA_PINGROUP_LVP0,
-	TEGRA_PINGROUP_LVP1,
-	TEGRA_PINGROUP_LVS,
-	TEGRA_PINGROUP_OWC,
-	TEGRA_PINGROUP_PMC,
-	TEGRA_PINGROUP_PTA,
-	TEGRA_PINGROUP_RM,
-	TEGRA_PINGROUP_SDB,
-	TEGRA_PINGROUP_SDC,
-	TEGRA_PINGROUP_SDD,
-	TEGRA_PINGROUP_SDIO1,
-	TEGRA_PINGROUP_SLXA,
-	TEGRA_PINGROUP_SLXC,
-	TEGRA_PINGROUP_SLXD,
-	TEGRA_PINGROUP_SLXK,
-	TEGRA_PINGROUP_SPDI,
-	TEGRA_PINGROUP_SPDO,
-	TEGRA_PINGROUP_SPIA,
-	TEGRA_PINGROUP_SPIB,
-	TEGRA_PINGROUP_SPIC,
-	TEGRA_PINGROUP_SPID,
-	TEGRA_PINGROUP_SPIE,
-	TEGRA_PINGROUP_SPIF,
-	TEGRA_PINGROUP_SPIG,
-	TEGRA_PINGROUP_SPIH,
-	TEGRA_PINGROUP_UAA,
-	TEGRA_PINGROUP_UAB,
-	TEGRA_PINGROUP_UAC,
-	TEGRA_PINGROUP_UAD,
-	TEGRA_PINGROUP_UCA,
-	TEGRA_PINGROUP_UCB,
-	TEGRA_PINGROUP_UDA,
-	/* these pin groups only have pullup and pull down control */
-	TEGRA_PINGROUP_CK32,
-	TEGRA_PINGROUP_DDRC,
-	TEGRA_PINGROUP_PMCA,
-	TEGRA_PINGROUP_PMCB,
-	TEGRA_PINGROUP_PMCC,
-	TEGRA_PINGROUP_PMCD,
-	TEGRA_PINGROUP_PMCE,
-	TEGRA_PINGROUP_XM2C,
-	TEGRA_PINGROUP_XM2D,
-	TEGRA_MAX_PINGROUP,
-};
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#include "pinmux-t2.h"
+#else
+#error "Undefined Tegra architecture"
+#endif
 
 enum tegra_mux_func {
 	TEGRA_MUX_RSVD = 0x8000,
@@ -205,6 +90,7 @@ enum tegra_mux_func {
 	TEGRA_MUX_VI,
 	TEGRA_MUX_VI_SENSOR_CLK,
 	TEGRA_MUX_XIO,
+	TEGRA_MUX_SAFE,
 	TEGRA_MAX_MUX,
 };
 
@@ -219,6 +105,18 @@ enum tegra_tristate {
 	TEGRA_TRI_TRISTATE = 1,
 };
 
+enum tegra_vddio {
+	TEGRA_VDDIO_BB = 0,
+	TEGRA_VDDIO_LCD,
+	TEGRA_VDDIO_VI,
+	TEGRA_VDDIO_UART,
+	TEGRA_VDDIO_DDR,
+	TEGRA_VDDIO_NAND,
+	TEGRA_VDDIO_SYS,
+	TEGRA_VDDIO_AUDIO,
+	TEGRA_VDDIO_SD,
+};
+
 struct tegra_pingroup_config {
 	enum tegra_pingroup	pingroup;
 	enum tegra_mux_func	func;
@@ -270,38 +168,6 @@ enum tegra_pull_strength {
 	TEGRA_MAX_PULL,
 };
 
-enum tegra_drive_pingroup {
-	TEGRA_DRIVE_PINGROUP_AO1 = 0,
-	TEGRA_DRIVE_PINGROUP_AO2,
-	TEGRA_DRIVE_PINGROUP_AT1,
-	TEGRA_DRIVE_PINGROUP_AT2,
-	TEGRA_DRIVE_PINGROUP_CDEV1,
-	TEGRA_DRIVE_PINGROUP_CDEV2,
-	TEGRA_DRIVE_PINGROUP_CSUS,
-	TEGRA_DRIVE_PINGROUP_DAP1,
-	TEGRA_DRIVE_PINGROUP_DAP2,
-	TEGRA_DRIVE_PINGROUP_DAP3,
-	TEGRA_DRIVE_PINGROUP_DAP4,
-	TEGRA_DRIVE_PINGROUP_DBG,
-	TEGRA_DRIVE_PINGROUP_LCD1,
-	TEGRA_DRIVE_PINGROUP_LCD2,
-	TEGRA_DRIVE_PINGROUP_SDMMC2,
-	TEGRA_DRIVE_PINGROUP_SDMMC3,
-	TEGRA_DRIVE_PINGROUP_SPI,
-	TEGRA_DRIVE_PINGROUP_UAA,
-	TEGRA_DRIVE_PINGROUP_UAB,
-	TEGRA_DRIVE_PINGROUP_UART2,
-	TEGRA_DRIVE_PINGROUP_UART3,
-	TEGRA_DRIVE_PINGROUP_VI1,
-	TEGRA_DRIVE_PINGROUP_VI2,
-	TEGRA_DRIVE_PINGROUP_XM2A,
-	TEGRA_DRIVE_PINGROUP_XM2C,
-	TEGRA_DRIVE_PINGROUP_XM2D,
-	TEGRA_DRIVE_PINGROUP_XM2CLK,
-	TEGRA_DRIVE_PINGROUP_MEMCOMP,
-	TEGRA_MAX_DRIVE_PINGROUP,
-};
-
 enum tegra_drive {
 	TEGRA_DRIVE_DIV_8 = 0,
 	TEGRA_DRIVE_DIV_4,
@@ -331,18 +197,44 @@ struct tegra_drive_pingroup_config {
 	enum tegra_slew slew_falling;
 };
 
-int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func);
-int tegra_pinmux_set_tristate(enum tegra_pingroup pg, enum tegra_tristate tristate);
-int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg, enum tegra_pullupdown pupd);
+struct tegra_drive_pingroup_desc {
+	const char *name;
+	s16 reg;
+};
+
+struct tegra_pingroup_desc {
+	const char *name;
+	int funcs[4];
+	int func_safe;
+	int vddio;
+	s16 tri_reg; 	/* offset into the TRISTATE_REG_* register bank */
+	s16 mux_reg;	/* offset into the PIN_MUX_CTL_* register bank */
+	s16 pupd_reg;	/* offset into the PULL_UPDOWN_REG_* register bank */
+	s8 tri_bit; 	/* offset into the TRISTATE_REG_* register bit */
+	s8 mux_bit;	/* offset into the PIN_MUX_CTL_* register bit */
+	s8 pupd_bit;	/* offset into the PULL_UPDOWN_REG_* register bit */
+};
+
+extern const struct tegra_pingroup_desc tegra_soc_pingroups[];
+extern const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[];
 
-void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup,
-	enum tegra_mux_func func, enum tegra_pullupdown pupd,
+int tegra_pinmux_set_tristate(enum tegra_pingroup pg,
 	enum tegra_tristate tristate);
+int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg,
+	enum tegra_pullupdown pupd);
 
-void tegra_pinmux_config_table(struct tegra_pingroup_config *config, int len);
+void tegra_pinmux_config_table(const struct tegra_pingroup_config *config,
+	int len);
 
 void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config,
 	int len);
-
+void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config,
+	int len);
+void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config,
+	int len);
+void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config,
+	int len, enum tegra_tristate tristate);
+void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config,
+	int len, enum tegra_pullupdown pupd);
 #endif
 
diff --git a/arch/arm/mach-tegra/io.c b/arch/arm/mach-tegra/io.c
index 9fe2c5c683d471e96e794a86be2061d9a46694a8..31848a9592f8770d3df1d28301913b2a8ffb52cc 100644
--- a/arch/arm/mach-tegra/io.c
+++ b/arch/arm/mach-tegra/io.c
@@ -49,6 +49,12 @@ static struct map_desc tegra_io_desc[] __initdata = {
 		.length = IO_CPU_SIZE,
 		.type = MT_DEVICE,
 	},
+	{
+		.virtual = IO_IRAM_VIRT,
+		.pfn = __phys_to_pfn(IO_IRAM_PHYS),
+		.length = IO_IRAM_SIZE,
+		.type = MT_DEVICE,
+	},
 };
 
 void __init tegra_map_common_io(void)
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 1fdbe708d43d12b5bc73ddf1c553461700932677..50a8dfb9a0cfe8cbb1dbdaeb8a913d5858000902 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -4,6 +4,8 @@
  * Author:
  *	Colin Cross <ccross@google.com>
  *
+ * Copyright (C) 2010, NVIDIA Corporation
+ *
  * 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.
@@ -27,8 +29,143 @@
 
 #include "board.h"
 
+#define INT_SYS_NR	(INT_GPIO_BASE - INT_PRI_BASE)
+#define INT_SYS_SZ	(INT_SEC_BASE - INT_PRI_BASE)
+#define PPI_NR		((INT_SYS_NR+INT_SYS_SZ-1)/INT_SYS_SZ)
+
+#define APBDMA_IRQ_STA_CPU  0x14
+#define APBDMA_IRQ_MASK_SET 0x20
+#define APBDMA_IRQ_MASK_CLR 0x24
+
+#define ICTLR_CPU_IER		0x20
+#define ICTLR_CPU_IER_SET	0x24
+#define ICTLR_CPU_IER_CLR	0x28
+#define ICTLR_CPU_IEP_CLASS	0x2c
+#define ICTLR_COP_IER		0x30
+#define ICTLR_COP_IER_SET	0x34
+#define ICTLR_COP_IER_CLR	0x38
+#define ICTLR_COP_IEP_CLASS	0x3c
+
+static void (*gic_mask_irq)(unsigned int irq);
+static void (*gic_unmask_irq)(unsigned int irq);
+
+#define irq_to_ictlr(irq) (((irq)-32) >> 5)
+static void __iomem *tegra_ictlr_base = IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE);
+#define ictlr_to_virt(ictlr) (tegra_ictlr_base + (ictlr)*0x100)
+
+static void tegra_mask(unsigned int irq)
+{
+	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq));
+	gic_mask_irq(irq);
+	writel(1<<(irq&31), addr+ICTLR_CPU_IER_CLR);
+}
+
+static void tegra_unmask(unsigned int irq)
+{
+	void __iomem *addr = ictlr_to_virt(irq_to_ictlr(irq));
+	gic_unmask_irq(irq);
+	writel(1<<(irq&31), addr+ICTLR_CPU_IER_SET);
+}
+
+#ifdef CONFIG_PM
+
+static int tegra_set_wake(unsigned int irq, unsigned int on)
+{
+	return 0;
+}
+#endif
+
+static struct irq_chip tegra_irq = {
+	.name		= "PPI",
+	.mask		= tegra_mask,
+	.unmask		= tegra_unmask,
+#ifdef CONFIG_PM
+	.set_wake	= tegra_set_wake,
+#endif
+};
+
 void __init tegra_init_irq(void)
 {
+	struct irq_chip *gic;
+	unsigned int i;
+
+	for (i = 0; i < PPI_NR; i++) {
+		writel(~0, ictlr_to_virt(i) + ICTLR_CPU_IER_CLR);
+		writel(0, ictlr_to_virt(i) + ICTLR_CPU_IEP_CLASS);
+	}
+
 	gic_dist_init(0, IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE), 29);
 	gic_cpu_init(0, IO_ADDRESS(TEGRA_ARM_PERIF_BASE + 0x100));
+
+	gic = get_irq_chip(29);
+	gic_unmask_irq = gic->unmask;
+	gic_mask_irq = gic->mask;
+	tegra_irq.ack = gic->ack;
+#ifdef CONFIG_SMP
+	tegra_irq.set_affinity = gic->set_affinity;
+#endif
+
+	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
+		set_irq_chip(i, &tegra_irq);
+		set_irq_handler(i, handle_level_irq);
+		set_irq_flags(i, IRQF_VALID);
+	}
+}
+
+#ifdef CONFIG_PM
+static u32 cop_ier[PPI_NR];
+static u32 cpu_ier[PPI_NR];
+static u32 cpu_iep[PPI_NR];
+
+void tegra_irq_suspend(void)
+{
+	unsigned long flags;
+	int i;
+
+	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
+		struct irq_desc *desc = irq_to_desc(i);
+		if (!desc)
+			continue;
+		if (desc->status & IRQ_WAKEUP) {
+			pr_debug("irq %d is wakeup\n", i);
+			continue;
+		}
+		disable_irq(i);
+	}
+
+	local_irq_save(flags);
+	for (i = 0; i < PPI_NR; i++) {
+		void __iomem *ictlr = ictlr_to_virt(i);
+		cpu_ier[i] = readl(ictlr + ICTLR_CPU_IER);
+		cpu_iep[i] = readl(ictlr + ICTLR_CPU_IEP_CLASS);
+		cop_ier[i] = readl(ictlr + ICTLR_COP_IER);
+		writel(~0, ictlr + ICTLR_COP_IER_CLR);
+	}
+	local_irq_restore(flags);
+}
+
+void tegra_irq_resume(void)
+{
+	unsigned long flags;
+	int i;
+
+	local_irq_save(flags);
+	for (i = 0; i < PPI_NR; i++) {
+		void __iomem *ictlr = ictlr_to_virt(i);
+		writel(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS);
+		writel(~0ul, ictlr + ICTLR_CPU_IER_CLR);
+		writel(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET);
+		writel(0, ictlr + ICTLR_COP_IEP_CLASS);
+		writel(~0ul, ictlr + ICTLR_COP_IER_CLR);
+		writel(cop_ier[i], ictlr + ICTLR_COP_IER_SET);
+	}
+	local_irq_restore(flags);
+
+	for (i = INT_PRI_BASE; i < INT_GPIO_BASE; i++) {
+		struct irq_desc *desc = irq_to_desc(i);
+		if (!desc || (desc->status & IRQ_WAKEUP))
+			continue;
+		enable_irq(i);
+	}
 }
+#endif
diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c
new file mode 100644
index 0000000000000000000000000000000000000000..7cc8601c19ffcfa271454ade79ff6cc64a05e5fc
--- /dev/null
+++ b/arch/arm/mach-tegra/legacy_irq.c
@@ -0,0 +1,114 @@
+/*
+ * arch/arm/mach-tegra/legacy_irq.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ * Author: Colin Cross <ccross@android.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/io.h>
+#include <linux/kernel.h>
+#include <mach/iomap.h>
+#include <mach/legacy_irq.h>
+
+#define ICTLR_CPU_IER		0x20
+#define ICTLR_CPU_IER_SET	0x24
+#define ICTLR_CPU_IER_CLR	0x28
+#define ICTLR_CPU_IEP_CLASS	0x2C
+#define ICTLR_CPU_IEP_VFIQ	0x08
+#define ICTLR_CPU_IEP_FIR	0x14
+#define ICTLR_CPU_IEP_FIR_SET	0x18
+#define ICTLR_CPU_IEP_FIR_CLR	0x1c
+
+static void __iomem *ictlr_reg_base[] = {
+	IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE),
+	IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
+	IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE),
+	IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
+};
+
+/* When going into deep sleep, the CPU is powered down, taking the GIC with it
+   In order to wake, the wake interrupts need to be enabled in the legacy
+   interrupt controller. */
+void tegra_legacy_unmask_irq(unsigned int irq)
+{
+	void __iomem *base;
+	pr_debug("%s: %d\n", __func__, irq);
+
+	irq -= 32;
+	base = ictlr_reg_base[irq>>5];
+	writel(1 << (irq & 31), base + ICTLR_CPU_IER_SET);
+}
+
+void tegra_legacy_mask_irq(unsigned int irq)
+{
+	void __iomem *base;
+	pr_debug("%s: %d\n", __func__, irq);
+
+	irq -= 32;
+	base = ictlr_reg_base[irq>>5];
+	writel(1 << (irq & 31), base + ICTLR_CPU_IER_CLR);
+}
+
+void tegra_legacy_force_irq_set(unsigned int irq)
+{
+	void __iomem *base;
+	pr_debug("%s: %d\n", __func__, irq);
+
+	irq -= 32;
+	base = ictlr_reg_base[irq>>5];
+	writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_SET);
+}
+
+void tegra_legacy_force_irq_clr(unsigned int irq)
+{
+	void __iomem *base;
+	pr_debug("%s: %d\n", __func__, irq);
+
+	irq -= 32;
+	base = ictlr_reg_base[irq>>5];
+	writel(1 << (irq & 31), base + ICTLR_CPU_IEP_FIR_CLR);
+}
+
+int tegra_legacy_force_irq_status(unsigned int irq)
+{
+	void __iomem *base;
+	pr_debug("%s: %d\n", __func__, irq);
+
+	irq -= 32;
+	base = ictlr_reg_base[irq>>5];
+	return !!(readl(base + ICTLR_CPU_IEP_FIR) & (1 << (irq & 31)));
+}
+
+void tegra_legacy_select_fiq(unsigned int irq, bool fiq)
+{
+	void __iomem *base;
+	pr_debug("%s: %d\n", __func__, irq);
+
+	irq -= 32;
+	base = ictlr_reg_base[irq>>5];
+	writel(fiq << (irq & 31), base + ICTLR_CPU_IEP_CLASS);
+}
+
+unsigned long tegra_legacy_vfiq(int nr)
+{
+	void __iomem *base;
+	base = ictlr_reg_base[nr];
+	return readl(base + ICTLR_CPU_IEP_VFIQ);
+}
+
+unsigned long tegra_legacy_class(int nr)
+{
+	void __iomem *base;
+	base = ictlr_reg_base[nr];
+	return readl(base + ICTLR_CPU_IEP_CLASS);
+}
diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c
new file mode 100644
index 0000000000000000000000000000000000000000..53f5fa37014a34eeeaaf98350b29ee2c2384d5e9
--- /dev/null
+++ b/arch/arm/mach-tegra/pcie.c
@@ -0,0 +1,915 @@
+/*
+ * arch/arm/mach-tegra/pci.c
+ *
+ * PCIe host controller driver for TEGRA(2) SOCs
+ *
+ * Copyright (c) 2010, CompuLab, Ltd.
+ * Author: Mike Rapoport <mike@compulab.co.il>
+ *
+ * Based on NVIDIA PCIe driver
+ * Copyright (c) 2008-2009, NVIDIA Corporation.
+ *
+ * Bits taken from arch/arm/mach-dove/pcie.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+
+#include <asm/sizes.h>
+#include <asm/mach/pci.h>
+
+#include <mach/pinmux.h>
+#include <mach/iomap.h>
+#include <mach/clk.h>
+
+/* register definitions */
+#define AFI_OFFSET	0x3800
+#define PADS_OFFSET	0x3000
+#define RP0_OFFSET	0x0000
+#define RP1_OFFSET	0x1000
+
+#define AFI_AXI_BAR0_SZ	0x00
+#define AFI_AXI_BAR1_SZ	0x04
+#define AFI_AXI_BAR2_SZ	0x08
+#define AFI_AXI_BAR3_SZ	0x0c
+#define AFI_AXI_BAR4_SZ	0x10
+#define AFI_AXI_BAR5_SZ	0x14
+
+#define AFI_AXI_BAR0_START	0x18
+#define AFI_AXI_BAR1_START	0x1c
+#define AFI_AXI_BAR2_START	0x20
+#define AFI_AXI_BAR3_START	0x24
+#define AFI_AXI_BAR4_START	0x28
+#define AFI_AXI_BAR5_START	0x2c
+
+#define AFI_FPCI_BAR0	0x30
+#define AFI_FPCI_BAR1	0x34
+#define AFI_FPCI_BAR2	0x38
+#define AFI_FPCI_BAR3	0x3c
+#define AFI_FPCI_BAR4	0x40
+#define AFI_FPCI_BAR5	0x44
+
+#define AFI_CACHE_BAR0_SZ	0x48
+#define AFI_CACHE_BAR0_ST	0x4c
+#define AFI_CACHE_BAR1_SZ	0x50
+#define AFI_CACHE_BAR1_ST	0x54
+
+#define AFI_MSI_BAR_SZ		0x60
+#define AFI_MSI_FPCI_BAR_ST	0x64
+#define AFI_MSI_AXI_BAR_ST	0x68
+
+#define AFI_CONFIGURATION		0xac
+#define  AFI_CONFIGURATION_EN_FPCI	(1 << 0)
+
+#define AFI_FPCI_ERROR_MASKS	0xb0
+
+#define AFI_INTR_MASK		0xb4
+#define  AFI_INTR_MASK_INT_MASK	(1 << 0)
+#define  AFI_INTR_MASK_MSI_MASK	(1 << 8)
+
+#define AFI_INTR_CODE		0xb8
+#define  AFI_INTR_CODE_MASK	0xf
+#define  AFI_INTR_MASTER_ABORT	4
+#define  AFI_INTR_LEGACY	6
+
+#define AFI_INTR_SIGNATURE	0xbc
+#define AFI_SM_INTR_ENABLE	0xc4
+
+#define AFI_AFI_INTR_ENABLE		0xc8
+#define  AFI_INTR_EN_INI_SLVERR		(1 << 0)
+#define  AFI_INTR_EN_INI_DECERR		(1 << 1)
+#define  AFI_INTR_EN_TGT_SLVERR		(1 << 2)
+#define  AFI_INTR_EN_TGT_DECERR		(1 << 3)
+#define  AFI_INTR_EN_TGT_WRERR		(1 << 4)
+#define  AFI_INTR_EN_DFPCI_DECERR	(1 << 5)
+#define  AFI_INTR_EN_AXI_DECERR		(1 << 6)
+#define  AFI_INTR_EN_FPCI_TIMEOUT	(1 << 7)
+
+#define AFI_PCIE_CONFIG					0x0f8
+#define  AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE		(1 << 1)
+#define  AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE		(1 << 2)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK	(0xf << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_SINGLE	(0x0 << 20)
+#define  AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL	(0x1 << 20)
+
+#define AFI_FUSE			0x104
+#define  AFI_FUSE_PCIE_T0_GEN2_DIS	(1 << 2)
+
+#define AFI_PEX0_CTRL			0x110
+#define AFI_PEX1_CTRL			0x118
+#define  AFI_PEX_CTRL_RST		(1 << 0)
+#define  AFI_PEX_CTRL_REFCLK_EN		(1 << 3)
+
+#define RP_VEND_XP	0x00000F00
+#define  RP_VEND_XP_DL_UP	(1 << 30)
+
+#define RP_LINK_CONTROL_STATUS			0x00000090
+#define  RP_LINK_CONTROL_STATUS_LINKSTAT_MASK	0x3fff0000
+
+#define PADS_CTL_SEL		0x0000009C
+
+#define PADS_CTL		0x000000A0
+#define  PADS_CTL_IDDQ_1L	(1 << 0)
+#define  PADS_CTL_TX_DATA_EN_1L	(1 << 6)
+#define  PADS_CTL_RX_DATA_EN_1L	(1 << 10)
+
+#define PADS_PLL_CTL				0x000000B8
+#define  PADS_PLL_CTL_RST_B4SM			(1 << 1)
+#define  PADS_PLL_CTL_LOCKDET			(1 << 8)
+#define  PADS_PLL_CTL_REFCLK_MASK		(0x3 << 16)
+#define  PADS_PLL_CTL_REFCLK_INTERNAL_CML	(0 << 16)
+#define  PADS_PLL_CTL_REFCLK_INTERNAL_CMOS	(1 << 16)
+#define  PADS_PLL_CTL_REFCLK_EXTERNAL		(2 << 16)
+#define  PADS_PLL_CTL_TXCLKREF_MASK		(0x1 << 20)
+#define  PADS_PLL_CTL_TXCLKREF_DIV10		(0 << 20)
+#define  PADS_PLL_CTL_TXCLKREF_DIV5		(1 << 20)
+
+/* PMC access is required for PCIE xclk (un)clamping */
+#define PMC_SCRATCH42		0x144
+#define PMC_SCRATCH42_PCX_CLAMP	(1 << 0)
+
+static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+
+#define pmc_writel(value, reg) \
+	__raw_writel(value, (u32)reg_pmc_base + (reg))
+#define pmc_readl(reg) \
+	__raw_readl((u32)reg_pmc_base + (reg))
+
+/*
+ * Tegra2 defines 1GB in the AXI address map for PCIe.
+ *
+ * That address space is split into different regions, with sizes and
+ * offsets as follows:
+ *
+ * 0x80000000 - 0x80003fff - PCI controller registers
+ * 0x80004000 - 0x80103fff - PCI configuration space
+ * 0x80104000 - 0x80203fff - PCI extended configuration space
+ * 0x80203fff - 0x803fffff - unused
+ * 0x80400000 - 0x8040ffff - downstream IO
+ * 0x80410000 - 0x8fffffff - unused
+ * 0x90000000 - 0x9fffffff - non-prefetchable memory
+ * 0xa0000000 - 0xbfffffff - prefetchable memory
+ */
+#define TEGRA_PCIE_BASE		0x80000000
+
+#define PCIE_REGS_SZ		SZ_16K
+#define PCIE_CFG_OFF		PCIE_REGS_SZ
+#define PCIE_CFG_SZ		SZ_1M
+#define PCIE_EXT_CFG_OFF	(PCIE_CFG_SZ + PCIE_CFG_OFF)
+#define PCIE_EXT_CFG_SZ		SZ_1M
+#define PCIE_IOMAP_SZ		(PCIE_REGS_SZ + PCIE_CFG_SZ + PCIE_EXT_CFG_SZ)
+
+#define MMIO_BASE		(TEGRA_PCIE_BASE + SZ_4M)
+#define MMIO_SIZE		SZ_64K
+#define MEM_BASE_0		(TEGRA_PCIE_BASE + SZ_256M)
+#define MEM_SIZE_0		SZ_128M
+#define MEM_BASE_1		(MEM_BASE_0 + MEM_SIZE_0)
+#define MEM_SIZE_1		SZ_128M
+#define PREFETCH_MEM_BASE_0	(MEM_BASE_1 + MEM_SIZE_1)
+#define PREFETCH_MEM_SIZE_0	SZ_128M
+#define PREFETCH_MEM_BASE_1	(PREFETCH_MEM_BASE_0 + PREFETCH_MEM_SIZE_0)
+#define PREFETCH_MEM_SIZE_1	SZ_128M
+
+#define  PCIE_CONF_BUS(b)	((b) << 16)
+#define  PCIE_CONF_DEV(d)	((d) << 11)
+#define  PCIE_CONF_FUNC(f)	((f) << 8)
+#define  PCIE_CONF_REG(r)	\
+	(((r) & ~0x3) | (((r) < 256) ? PCIE_CFG_OFF : PCIE_EXT_CFG_OFF))
+
+struct tegra_pcie_port {
+	int			index;
+	u8			root_bus_nr;
+	void __iomem		*base;
+
+	bool			link_up;
+
+	char			io_space_name[16];
+	char			mem_space_name[16];
+	char			prefetch_space_name[20];
+	struct resource		res[3];
+};
+
+struct tegra_pcie_info {
+	struct tegra_pcie_port	port[2];
+	int			num_ports;
+
+	void __iomem		*regs;
+	struct resource		res_mmio;
+
+	struct clk		*pex_clk;
+	struct clk		*afi_clk;
+	struct clk		*pcie_xclk;
+	struct clk		*pll_e;
+};
+
+static struct tegra_pcie_info tegra_pcie = {
+	.res_mmio = {
+		.name = "PCI IO",
+		.start = MMIO_BASE,
+		.end = MMIO_BASE + MMIO_SIZE - 1,
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+void __iomem *tegra_pcie_io_base;
+EXPORT_SYMBOL(tegra_pcie_io_base);
+
+static inline void afi_writel(u32 value, unsigned long offset)
+{
+	writel(value, offset + AFI_OFFSET + tegra_pcie.regs);
+}
+
+static inline u32 afi_readl(unsigned long offset)
+{
+	return readl(offset + AFI_OFFSET + tegra_pcie.regs);
+}
+
+static inline void pads_writel(u32 value, unsigned long offset)
+{
+	writel(value, offset + PADS_OFFSET + tegra_pcie.regs);
+}
+
+static inline u32 pads_readl(unsigned long offset)
+{
+	return readl(offset + PADS_OFFSET + tegra_pcie.regs);
+}
+
+static struct tegra_pcie_port *bus_to_port(int bus)
+{
+	int i;
+
+	for (i = tegra_pcie.num_ports - 1; i >= 0; i--) {
+		int rbus = tegra_pcie.port[i].root_bus_nr;
+		if (rbus != -1 && rbus == bus)
+			break;
+	}
+
+	return i >= 0 ? tegra_pcie.port + i : NULL;
+}
+
+static int tegra_pcie_read_conf(struct pci_bus *bus, unsigned int devfn,
+				int where, int size, u32 *val)
+{
+	struct tegra_pcie_port *pp = bus_to_port(bus->number);
+	void __iomem *addr;
+
+	if (pp) {
+		if (devfn != 0) {
+			*val = 0xffffffff;
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		}
+
+		addr = pp->base + (where & ~0x3);
+	} else {
+		addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) +
+					  PCIE_CONF_DEV(PCI_SLOT(devfn)) +
+					  PCIE_CONF_FUNC(PCI_FUNC(devfn)) +
+					  PCIE_CONF_REG(where));
+	}
+
+	*val = readl(addr);
+
+	if (size == 1)
+		*val = (*val >> (8 * (where & 3))) & 0xff;
+	else if (size == 2)
+		*val = (*val >> (8 * (where & 3))) & 0xffff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int tegra_pcie_write_conf(struct pci_bus *bus, unsigned int devfn,
+				 int where, int size, u32 val)
+{
+	struct tegra_pcie_port *pp = bus_to_port(bus->number);
+	void __iomem *addr;
+
+	u32 mask;
+	u32 tmp;
+
+	if (pp) {
+		if (devfn != 0)
+			return PCIBIOS_DEVICE_NOT_FOUND;
+
+		addr = pp->base + (where & ~0x3);
+	} else {
+		addr = tegra_pcie.regs + (PCIE_CONF_BUS(bus->number) +
+					  PCIE_CONF_DEV(PCI_SLOT(devfn)) +
+					  PCIE_CONF_FUNC(PCI_FUNC(devfn)) +
+					  PCIE_CONF_REG(where));
+	}
+
+	if (size == 4) {
+		writel(val, addr);
+		return PCIBIOS_SUCCESSFUL;
+	}
+
+	if (size == 2)
+		mask = ~(0xffff << ((where & 0x3) * 8));
+	else if (size == 1)
+		mask = ~(0xff << ((where & 0x3) * 8));
+	else
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	tmp = readl(addr) & mask;
+	tmp |= val << ((where & 0x3) * 8);
+	writel(tmp, addr);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops tegra_pcie_ops = {
+	.read	= tegra_pcie_read_conf,
+	.write	= tegra_pcie_write_conf,
+};
+
+static void __devinit tegra_pcie_fixup_bridge(struct pci_dev *dev)
+{
+	u16 reg;
+
+	if ((dev->class >> 16) == PCI_BASE_CLASS_BRIDGE) {
+		pci_read_config_word(dev, PCI_COMMAND, &reg);
+		reg |= (PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
+			PCI_COMMAND_MASTER | PCI_COMMAND_SERR);
+		pci_write_config_word(dev, PCI_COMMAND, reg);
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_fixup_bridge);
+
+/* Tegra PCIE root complex wrongly reports device class */
+static void __devinit tegra_pcie_fixup_class(struct pci_dev *dev)
+{
+	dev->class = PCI_CLASS_BRIDGE_PCI << 8;
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf0, tegra_pcie_fixup_class);
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, 0x0bf1, tegra_pcie_fixup_class);
+
+/* Tegra PCIE requires relaxed ordering */
+static void __devinit tegra_pcie_relax_enable(struct pci_dev *dev)
+{
+	u16 val16;
+	int pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+
+	if (pos <= 0) {
+		dev_err(&dev->dev, "skipping relaxed ordering fixup\n");
+		return;
+	}
+
+	pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &val16);
+	val16 |= PCI_EXP_DEVCTL_RELAX_EN;
+	pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, val16);
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_ANY_ID, PCI_ANY_ID, tegra_pcie_relax_enable);
+
+static int tegra_pcie_setup(int nr, struct pci_sys_data *sys)
+{
+	struct tegra_pcie_port *pp;
+
+	if (nr >= tegra_pcie.num_ports)
+		return 0;
+
+	pp = tegra_pcie.port + nr;
+	pp->root_bus_nr = sys->busnr;
+
+	/*
+	 * IORESOURCE_IO
+	 */
+	snprintf(pp->io_space_name, sizeof(pp->io_space_name),
+		 "PCIe %d I/O", pp->index);
+	pp->io_space_name[sizeof(pp->io_space_name) - 1] = 0;
+	pp->res[0].name = pp->io_space_name;
+	if (pp->index == 0) {
+		pp->res[0].start = PCIBIOS_MIN_IO;
+		pp->res[0].end = pp->res[0].start + SZ_32K - 1;
+	} else {
+		pp->res[0].start = PCIBIOS_MIN_IO + SZ_32K;
+		pp->res[0].end = IO_SPACE_LIMIT;
+	}
+	pp->res[0].flags = IORESOURCE_IO;
+	if (request_resource(&ioport_resource, &pp->res[0]))
+		panic("Request PCIe IO resource failed\n");
+	sys->resource[0] = &pp->res[0];
+
+	/*
+	 * IORESOURCE_MEM
+	 */
+	snprintf(pp->mem_space_name, sizeof(pp->mem_space_name),
+		 "PCIe %d MEM", pp->index);
+	pp->mem_space_name[sizeof(pp->mem_space_name) - 1] = 0;
+	pp->res[1].name = pp->mem_space_name;
+	if (pp->index == 0) {
+		pp->res[1].start = MEM_BASE_0;
+		pp->res[1].end = pp->res[1].start + MEM_SIZE_0 - 1;
+	} else {
+		pp->res[1].start = MEM_BASE_1;
+		pp->res[1].end = pp->res[1].start + MEM_SIZE_1 - 1;
+	}
+	pp->res[1].flags = IORESOURCE_MEM;
+	if (request_resource(&iomem_resource, &pp->res[1]))
+		panic("Request PCIe Memory resource failed\n");
+	sys->resource[1] = &pp->res[1];
+
+	/*
+	 * IORESOURCE_MEM | IORESOURCE_PREFETCH
+	 */
+	snprintf(pp->prefetch_space_name, sizeof(pp->prefetch_space_name),
+		 "PCIe %d PREFETCH MEM", pp->index);
+	pp->prefetch_space_name[sizeof(pp->prefetch_space_name) - 1] = 0;
+	pp->res[2].name = pp->prefetch_space_name;
+	if (pp->index == 0) {
+		pp->res[2].start = PREFETCH_MEM_BASE_0;
+		pp->res[2].end = pp->res[2].start + PREFETCH_MEM_SIZE_0 - 1;
+	} else {
+		pp->res[2].start = PREFETCH_MEM_BASE_1;
+		pp->res[2].end = pp->res[2].start + PREFETCH_MEM_SIZE_1 - 1;
+	}
+	pp->res[2].flags = IORESOURCE_MEM | IORESOURCE_PREFETCH;
+	if (request_resource(&iomem_resource, &pp->res[2]))
+		panic("Request PCIe Prefetch Memory resource failed\n");
+	sys->resource[2] = &pp->res[2];
+
+	return 1;
+}
+
+static int tegra_pcie_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	return INT_PCIE_INTR;
+}
+
+static struct pci_bus __init *tegra_pcie_scan_bus(int nr,
+						  struct pci_sys_data *sys)
+{
+	struct tegra_pcie_port *pp;
+
+	if (nr >= tegra_pcie.num_ports)
+		return 0;
+
+	pp = tegra_pcie.port + nr;
+	pp->root_bus_nr = sys->busnr;
+
+	return pci_scan_bus(sys->busnr, &tegra_pcie_ops, sys);
+}
+
+static struct hw_pci tegra_pcie_hw __initdata = {
+	.nr_controllers	= 2,
+	.setup		= tegra_pcie_setup,
+	.scan		= tegra_pcie_scan_bus,
+	.swizzle	= pci_std_swizzle,
+	.map_irq	= tegra_pcie_map_irq,
+};
+
+
+static irqreturn_t tegra_pcie_isr(int irq, void *arg)
+{
+	const char *err_msg[] = {
+		"Unknown",
+		"AXI slave error",
+		"AXI decode error",
+		"Target abort",
+		"Master abort",
+		"Invalid write",
+		"Response decoding error",
+		"AXI response decoding error",
+		"Transcation timeout",
+	};
+
+	u32 code, signature;
+
+	code = afi_readl(AFI_INTR_CODE) & AFI_INTR_CODE_MASK;
+	signature = afi_readl(AFI_INTR_SIGNATURE);
+	afi_writel(0, AFI_INTR_CODE);
+
+	if (code == AFI_INTR_LEGACY)
+		return IRQ_NONE;
+
+	if (code >= ARRAY_SIZE(err_msg))
+		code = 0;
+
+	/*
+	 * do not pollute kernel log with master abort reports since they
+	 * happen a lot during enumeration
+	 */
+	if (code == AFI_INTR_MASTER_ABORT)
+		pr_debug("PCIE: %s, signature: %08x\n", err_msg[code], signature);
+	else
+		pr_err("PCIE: %s, signature: %08x\n", err_msg[code], signature);
+
+	return IRQ_HANDLED;
+}
+
+static void tegra_pcie_setup_translations(void)
+{
+	u32 fpci_bar;
+	u32 size;
+	u32 axi_address;
+
+	/* Bar 0: config Bar */
+	fpci_bar = ((u32)0xfdff << 16);
+	size = PCIE_CFG_SZ;
+	axi_address = TEGRA_PCIE_BASE + PCIE_CFG_OFF;
+	afi_writel(axi_address, AFI_AXI_BAR0_START);
+	afi_writel(size >> 12, AFI_AXI_BAR0_SZ);
+	afi_writel(fpci_bar, AFI_FPCI_BAR0);
+
+	/* Bar 1: extended config Bar */
+	fpci_bar = ((u32)0xfe1 << 20);
+	size = PCIE_EXT_CFG_SZ;
+	axi_address = TEGRA_PCIE_BASE + PCIE_EXT_CFG_OFF;
+	afi_writel(axi_address, AFI_AXI_BAR1_START);
+	afi_writel(size >> 12, AFI_AXI_BAR1_SZ);
+	afi_writel(fpci_bar, AFI_FPCI_BAR1);
+
+	/* Bar 2: downstream IO bar */
+	fpci_bar = ((__u32)0xfdfc << 16);
+	size = MMIO_SIZE;
+	axi_address = MMIO_BASE;
+	afi_writel(axi_address, AFI_AXI_BAR2_START);
+	afi_writel(size >> 12, AFI_AXI_BAR2_SZ);
+	afi_writel(fpci_bar, AFI_FPCI_BAR2);
+
+	/* Bar 3: prefetchable memory BAR */
+	fpci_bar = (((PREFETCH_MEM_BASE_0 >> 12) & 0x0fffffff) << 4) | 0x1;
+	size =  PREFETCH_MEM_SIZE_0 +  PREFETCH_MEM_SIZE_1;
+	axi_address = PREFETCH_MEM_BASE_0;
+	afi_writel(axi_address, AFI_AXI_BAR3_START);
+	afi_writel(size >> 12, AFI_AXI_BAR3_SZ);
+	afi_writel(fpci_bar, AFI_FPCI_BAR3);
+
+	/* Bar 4: non prefetchable memory BAR */
+	fpci_bar = (((MEM_BASE_0 >> 12)	& 0x0FFFFFFF) << 4) | 0x1;
+	size = MEM_SIZE_0 + MEM_SIZE_1;
+	axi_address = MEM_BASE_0;
+	afi_writel(axi_address, AFI_AXI_BAR4_START);
+	afi_writel(size >> 12, AFI_AXI_BAR4_SZ);
+	afi_writel(fpci_bar, AFI_FPCI_BAR4);
+
+	/* Bar 5: NULL out the remaining BAR as it is not used */
+	fpci_bar = 0;
+	size = 0;
+	axi_address = 0;
+	afi_writel(axi_address, AFI_AXI_BAR5_START);
+	afi_writel(size >> 12, AFI_AXI_BAR5_SZ);
+	afi_writel(fpci_bar, AFI_FPCI_BAR5);
+
+	/* map all upstream transactions as uncached */
+	afi_writel(PHYS_OFFSET, AFI_CACHE_BAR0_ST);
+	afi_writel(0, AFI_CACHE_BAR0_SZ);
+	afi_writel(0, AFI_CACHE_BAR1_ST);
+	afi_writel(0, AFI_CACHE_BAR1_SZ);
+
+	/* No MSI */
+	afi_writel(0, AFI_MSI_FPCI_BAR_ST);
+	afi_writel(0, AFI_MSI_BAR_SZ);
+	afi_writel(0, AFI_MSI_AXI_BAR_ST);
+	afi_writel(0, AFI_MSI_BAR_SZ);
+}
+
+static void tegra_pcie_enable_controller(void)
+{
+	u32 val, reg;
+	int i;
+
+	/* Enable slot clock and pulse the reset signals */
+	for (i = 0, reg = AFI_PEX0_CTRL; i < 2; i++, reg += 0x8) {
+		val = afi_readl(reg) |  AFI_PEX_CTRL_REFCLK_EN;
+		afi_writel(val, reg);
+		val &= ~AFI_PEX_CTRL_RST;
+		afi_writel(val, reg);
+
+		val = afi_readl(reg) | AFI_PEX_CTRL_RST;
+		afi_writel(val, reg);
+	}
+
+	/* Enable dual controller and both ports */
+	val = afi_readl(AFI_PCIE_CONFIG);
+	val &= ~(AFI_PCIE_CONFIG_PCIEC0_DISABLE_DEVICE |
+		 AFI_PCIE_CONFIG_PCIEC1_DISABLE_DEVICE |
+		 AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_MASK);
+	val |= AFI_PCIE_CONFIG_SM2TMS0_XBAR_CONFIG_DUAL;
+	afi_writel(val, AFI_PCIE_CONFIG);
+
+	val = afi_readl(AFI_FUSE) & ~AFI_FUSE_PCIE_T0_GEN2_DIS;
+	afi_writel(val, AFI_FUSE);
+
+	/* Initialze internal PHY, enable up to 16 PCIE lanes */
+	pads_writel(0x0, PADS_CTL_SEL);
+
+	/* override IDDQ to 1 on all 4 lanes */
+	val = pads_readl(PADS_CTL) | PADS_CTL_IDDQ_1L;
+	pads_writel(val, PADS_CTL);
+
+	/*
+	 * set up PHY PLL inputs select PLLE output as refclock,
+	 * set TX ref sel to div10 (not div5)
+	 */
+	val = pads_readl(PADS_PLL_CTL);
+	val &= ~(PADS_PLL_CTL_REFCLK_MASK | PADS_PLL_CTL_TXCLKREF_MASK);
+	val |= (PADS_PLL_CTL_REFCLK_INTERNAL_CML | PADS_PLL_CTL_TXCLKREF_DIV10);
+	pads_writel(val, PADS_PLL_CTL);
+
+	/* take PLL out of reset  */
+	val = pads_readl(PADS_PLL_CTL) | PADS_PLL_CTL_RST_B4SM;
+	pads_writel(val, PADS_PLL_CTL);
+
+	/*
+	 * Hack, set the clock voltage to the DEFAULT provided by hw folks.
+	 * This doesn't exist in the documentation
+	 */
+	pads_writel(0xfa5cfa5c, 0xc8);
+
+	/* Wait for the PLL to lock */
+	do {
+		val = pads_readl(PADS_PLL_CTL);
+	} while (!(val & PADS_PLL_CTL_LOCKDET));
+
+	/* turn off IDDQ override */
+	val = pads_readl(PADS_CTL) & ~PADS_CTL_IDDQ_1L;
+	pads_writel(val, PADS_CTL);
+
+	/* enable TX/RX data */
+	val = pads_readl(PADS_CTL);
+	val |= (PADS_CTL_TX_DATA_EN_1L | PADS_CTL_RX_DATA_EN_1L);
+	pads_writel(val, PADS_CTL);
+
+	/* Take the PCIe interface module out of reset */
+	tegra_periph_reset_deassert(tegra_pcie.pcie_xclk);
+
+	/* Finally enable PCIe */
+	val = afi_readl(AFI_CONFIGURATION) | AFI_CONFIGURATION_EN_FPCI;
+	afi_writel(val, AFI_CONFIGURATION);
+
+	val = (AFI_INTR_EN_INI_SLVERR | AFI_INTR_EN_INI_DECERR |
+	       AFI_INTR_EN_TGT_SLVERR | AFI_INTR_EN_TGT_DECERR |
+	       AFI_INTR_EN_TGT_WRERR | AFI_INTR_EN_DFPCI_DECERR);
+	afi_writel(val, AFI_AFI_INTR_ENABLE);
+	afi_writel(0xffffffff, AFI_SM_INTR_ENABLE);
+
+	/* FIXME: No MSI for now, only INT */
+	afi_writel(AFI_INTR_MASK_INT_MASK, AFI_INTR_MASK);
+
+	/* Disable all execptions */
+	afi_writel(0, AFI_FPCI_ERROR_MASKS);
+
+	return;
+}
+
+static void tegra_pcie_xclk_clamp(bool clamp)
+{
+	u32 reg;
+
+	reg = pmc_readl(PMC_SCRATCH42) & ~PMC_SCRATCH42_PCX_CLAMP;
+
+	if (clamp)
+		reg |= PMC_SCRATCH42_PCX_CLAMP;
+
+	pmc_writel(reg, PMC_SCRATCH42);
+}
+
+static int tegra_pcie_power_on(void)
+{
+	tegra_pcie_xclk_clamp(true);
+	tegra_periph_reset_assert(tegra_pcie.pcie_xclk);
+	tegra_pcie_xclk_clamp(false);
+
+	clk_enable(tegra_pcie.afi_clk);
+	clk_enable(tegra_pcie.pex_clk);
+	return clk_enable(tegra_pcie.pll_e);
+}
+
+static void tegra_pcie_power_off(void)
+{
+	tegra_periph_reset_assert(tegra_pcie.pcie_xclk);
+	tegra_periph_reset_assert(tegra_pcie.afi_clk);
+	tegra_periph_reset_assert(tegra_pcie.pex_clk);
+
+	tegra_pcie_xclk_clamp(true);
+}
+
+static int tegra_pcie_clocks_get(void)
+{
+	int err;
+
+	tegra_pcie.pex_clk = clk_get(NULL, "pex");
+	if (IS_ERR(tegra_pcie.pex_clk))
+		return PTR_ERR(tegra_pcie.pex_clk);
+
+	tegra_pcie.afi_clk = clk_get(NULL, "afi");
+	if (IS_ERR(tegra_pcie.afi_clk)) {
+		err = PTR_ERR(tegra_pcie.afi_clk);
+		goto err_afi_clk;
+	}
+
+	tegra_pcie.pcie_xclk = clk_get(NULL, "pcie_xclk");
+	if (IS_ERR(tegra_pcie.pcie_xclk)) {
+		err =  PTR_ERR(tegra_pcie.pcie_xclk);
+		goto err_pcie_xclk;
+	}
+
+	tegra_pcie.pll_e = clk_get_sys(NULL, "pll_e");
+	if (IS_ERR(tegra_pcie.pll_e)) {
+		err = PTR_ERR(tegra_pcie.pll_e);
+		goto err_pll_e;
+	}
+
+	return 0;
+
+err_pll_e:
+	clk_put(tegra_pcie.pcie_xclk);
+err_pcie_xclk:
+	clk_put(tegra_pcie.afi_clk);
+err_afi_clk:
+	clk_put(tegra_pcie.pex_clk);
+
+	return err;
+}
+
+static void tegra_pcie_clocks_put(void)
+{
+	clk_put(tegra_pcie.pll_e);
+	clk_put(tegra_pcie.pcie_xclk);
+	clk_put(tegra_pcie.afi_clk);
+	clk_put(tegra_pcie.pex_clk);
+}
+
+static int __init tegra_pcie_get_resources(void)
+{
+	struct resource *res_mmio = &tegra_pcie.res_mmio;
+	int err;
+
+	err = tegra_pcie_clocks_get();
+	if (err) {
+		pr_err("PCIE: failed to get clocks: %d\n", err);
+		return err;
+	}
+
+	err = tegra_pcie_power_on();
+	if (err) {
+		pr_err("PCIE: failed to power up: %d\n", err);
+		goto err_pwr_on;
+	}
+
+	tegra_pcie.regs = ioremap_nocache(TEGRA_PCIE_BASE, PCIE_IOMAP_SZ);
+	if (tegra_pcie.regs == NULL) {
+		pr_err("PCIE: Failed to map PCI/AFI registers\n");
+		err = -ENOMEM;
+		goto err_map_reg;
+	}
+
+	err = request_resource(&iomem_resource, res_mmio);
+	if (err) {
+		pr_err("PCIE: Failed to request resources: %d\n", err);
+		goto err_req_io;
+	}
+
+	tegra_pcie_io_base = ioremap_nocache(res_mmio->start,
+					     resource_size(res_mmio));
+	if (tegra_pcie_io_base == NULL) {
+		pr_err("PCIE: Failed to map IO\n");
+		err = -ENOMEM;
+		goto err_map_io;
+	}
+
+	err = request_irq(INT_PCIE_INTR, tegra_pcie_isr,
+			  IRQF_SHARED, "PCIE", &tegra_pcie);
+	if (err) {
+		pr_err("PCIE: Failed to register IRQ: %d\n", err);
+		goto err_irq;
+	}
+	set_irq_flags(INT_PCIE_INTR, IRQF_VALID);
+
+	return 0;
+
+err_irq:
+	iounmap(tegra_pcie_io_base);
+err_map_io:
+	release_resource(&tegra_pcie.res_mmio);
+err_req_io:
+	iounmap(tegra_pcie.regs);
+err_map_reg:
+	tegra_pcie_power_off();
+err_pwr_on:
+	tegra_pcie_clocks_put();
+
+	return err;
+}
+
+/*
+ * FIXME: If there are no PCIe cards attached, then calling this function
+ * can result in the increase of the bootup time as there are big timeout
+ * loops.
+ */
+#define TEGRA_PCIE_LINKUP_TIMEOUT	200	/* up to 1.2 seconds */
+static bool tegra_pcie_check_link(struct tegra_pcie_port *pp, int idx,
+				  u32 reset_reg)
+{
+	u32 reg;
+	int retries = 3;
+	int timeout;
+
+	do {
+		timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
+		while (timeout) {
+			reg = readl(pp->base + RP_VEND_XP);
+
+			if (reg & RP_VEND_XP_DL_UP)
+				break;
+
+			mdelay(1);
+			timeout--;
+		}
+
+		if (!timeout)  {
+			pr_err("PCIE: port %d: link down, retrying\n", idx);
+			goto retry;
+		}
+
+		timeout = TEGRA_PCIE_LINKUP_TIMEOUT;
+		while (timeout) {
+			reg = readl(pp->base + RP_LINK_CONTROL_STATUS);
+
+			if (reg & 0x20000000)
+				return true;
+
+			mdelay(1);
+			timeout--;
+		}
+
+retry:
+		/* Pulse the PEX reset */
+		reg = afi_readl(reset_reg) | AFI_PEX_CTRL_RST;
+		afi_writel(reg, reset_reg);
+		mdelay(1);
+		reg = afi_readl(reset_reg) & ~AFI_PEX_CTRL_RST;
+		afi_writel(reg, reset_reg);
+
+		retries--;
+	} while (retries);
+
+	return false;
+}
+
+static void __init tegra_pcie_add_port(int index, u32 offset, u32 reset_reg)
+{
+	struct tegra_pcie_port *pp;
+
+	pp = tegra_pcie.port + tegra_pcie.num_ports;
+
+	pp->index = -1;
+	pp->base = tegra_pcie.regs + offset;
+	pp->link_up = tegra_pcie_check_link(pp, index, reset_reg);
+
+	if (!pp->link_up) {
+		pp->base = NULL;
+		printk(KERN_INFO "PCIE: port %d: link down, ignoring\n", index);
+		return;
+	}
+
+	tegra_pcie.num_ports++;
+	pp->index = index;
+	pp->root_bus_nr = -1;
+	memset(pp->res, 0, sizeof(pp->res));
+}
+
+int __init tegra_pcie_init(bool init_port0, bool init_port1)
+{
+	int err;
+
+	if (!(init_port0 || init_port1))
+		return -ENODEV;
+
+	err = tegra_pcie_get_resources();
+	if (err)
+		return err;
+
+	tegra_pcie_enable_controller();
+
+	/* setup the AFI address translations */
+	tegra_pcie_setup_translations();
+
+	if (init_port0)
+		tegra_pcie_add_port(0, RP0_OFFSET, AFI_PEX0_CTRL);
+
+	if (init_port1)
+		tegra_pcie_add_port(1, RP1_OFFSET, AFI_PEX1_CTRL);
+
+	pci_common_init(&tegra_pcie_hw);
+
+	return 0;
+}
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
new file mode 100644
index 0000000000000000000000000000000000000000..a6ea34e782dc339091b49e54871f953048651ad3
--- /dev/null
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -0,0 +1,260 @@
+/*
+ * linux/arch/arm/mach-tegra/pinmux-t2-tables.c
+ *
+ * Common pinmux configurations for Tegra 2 SoCs
+ *
+ * Copyright (C) 2010 NVIDIA Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/spinlock.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <mach/iomap.h>
+#include <mach/pinmux.h>
+
+#define DRIVE_PINGROUP(pg_name, r)				\
+	[TEGRA_DRIVE_PINGROUP_ ## pg_name] = {			\
+		.name = #pg_name,				\
+		.reg = r					\
+	}
+
+const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE_PINGROUP] = {
+	DRIVE_PINGROUP(AO1,		0x868),
+	DRIVE_PINGROUP(AO2,		0x86c),
+	DRIVE_PINGROUP(AT1,		0x870),
+	DRIVE_PINGROUP(AT2,		0x874),
+	DRIVE_PINGROUP(CDEV1,		0x878),
+	DRIVE_PINGROUP(CDEV2,		0x87c),
+	DRIVE_PINGROUP(CSUS,		0x880),
+	DRIVE_PINGROUP(DAP1,		0x884),
+	DRIVE_PINGROUP(DAP2,		0x888),
+	DRIVE_PINGROUP(DAP3,		0x88c),
+	DRIVE_PINGROUP(DAP4,		0x890),
+	DRIVE_PINGROUP(DBG,		0x894),
+	DRIVE_PINGROUP(LCD1,		0x898),
+	DRIVE_PINGROUP(LCD2,		0x89c),
+	DRIVE_PINGROUP(SDMMC2,		0x8a0),
+	DRIVE_PINGROUP(SDMMC3,		0x8a4),
+	DRIVE_PINGROUP(SPI,		0x8a8),
+	DRIVE_PINGROUP(UAA,		0x8ac),
+	DRIVE_PINGROUP(UAB,		0x8b0),
+	DRIVE_PINGROUP(UART2,		0x8b4),
+	DRIVE_PINGROUP(UART3,		0x8b8),
+	DRIVE_PINGROUP(VI1,		0x8bc),
+	DRIVE_PINGROUP(VI2,		0x8c0),
+	DRIVE_PINGROUP(XM2A,		0x8c4),
+	DRIVE_PINGROUP(XM2C,		0x8c8),
+	DRIVE_PINGROUP(XM2D,		0x8cc),
+	DRIVE_PINGROUP(XM2CLK,		0x8d0),
+	DRIVE_PINGROUP(MEMCOMP,		0x8d4),
+};
+
+#define PINGROUP(pg_name, vdd, f0, f1, f2, f3, f_safe,		\
+		 tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b)	\
+	[TEGRA_PINGROUP_ ## pg_name] = {			\
+		.name = #pg_name,				\
+		.vddio = TEGRA_VDDIO_ ## vdd,			\
+		.funcs = {					\
+			TEGRA_MUX_ ## f0,			\
+			TEGRA_MUX_ ## f1,			\
+			TEGRA_MUX_ ## f2,			\
+			TEGRA_MUX_ ## f3,			\
+		},						\
+		.func_safe = TEGRA_MUX_ ## f_safe,		\
+		.tri_reg = tri_r,				\
+		.tri_bit = tri_b,				\
+		.mux_reg = mux_r,				\
+		.mux_bit = mux_b,				\
+		.pupd_reg = pupd_r,				\
+		.pupd_bit = pupd_b,				\
+	}
+
+const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
+	PINGROUP(ATA,   NAND,  IDE,       NAND,      GMI,       RSVD,          IDE,       0x14, 0,  0x80, 24, 0xA0, 0),
+	PINGROUP(ATB,   NAND,  IDE,       NAND,      GMI,       SDIO4,         IDE,       0x14, 1,  0x80, 16, 0xA0, 2),
+	PINGROUP(ATC,   NAND,  IDE,       NAND,      GMI,       SDIO4,         IDE,       0x14, 2,  0x80, 22, 0xA0, 4),
+	PINGROUP(ATD,   NAND,  IDE,       NAND,      GMI,       SDIO4,         IDE,       0x14, 3,  0x80, 20, 0xA0, 6),
+	PINGROUP(ATE,   NAND,  IDE,       NAND,      GMI,       RSVD,          IDE,       0x18, 25, 0x80, 12, 0xA0, 8),
+	PINGROUP(CDEV1, AUDIO, OSC,       PLLA_OUT,  PLLM_OUT1, AUDIO_SYNC,    OSC,       0x14, 4,  0x88, 2,  0xA8, 0),
+	PINGROUP(CDEV2, AUDIO, OSC,       AHB_CLK,   APB_CLK,   PLLP_OUT4,     OSC,       0x14, 5,  0x88, 4,  0xA8, 2),
+	PINGROUP(CRTP,  LCD,   CRT,       RSVD,      RSVD,      RSVD,          RSVD,      0x20, 14, 0x98, 20, 0xA4, 24),
+	PINGROUP(CSUS,  VI,    PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, PLLC_OUT1, 0x14, 6,  0x88, 6,  0xAC, 24),
+	PINGROUP(DAP1,  AUDIO, DAP1,      RSVD,      GMI,       SDIO2,         DAP1,      0x14, 7,  0x88, 20, 0xA0, 10),
+	PINGROUP(DAP2,  AUDIO, DAP2,      TWC,       RSVD,      GMI,           DAP2,      0x14, 8,  0x88, 22, 0xA0, 12),
+	PINGROUP(DAP3,  BB,    DAP3,      RSVD,      RSVD,      RSVD,          DAP3,      0x14, 9,  0x88, 24, 0xA0, 14),
+	PINGROUP(DAP4,  UART,  DAP4,      RSVD,      GMI,       RSVD,          DAP4,      0x14, 10, 0x88, 26, 0xA0, 16),
+	PINGROUP(DDC,   LCD,   I2C2,      RSVD,      RSVD,      RSVD,          RSVD4,     0x18, 31, 0x88, 0,  0xB0, 28),
+	PINGROUP(DTA,   VI,    RSVD,      SDIO2,     VI,        RSVD,          RSVD4,     0x14, 11, 0x84, 20, 0xA0, 18),
+	PINGROUP(DTB,   VI,    RSVD,      RSVD,      VI,        SPI1,          RSVD1,     0x14, 12, 0x84, 22, 0xA0, 20),
+	PINGROUP(DTC,   VI,    RSVD,      RSVD,      VI,        RSVD,          RSVD1,     0x14, 13, 0x84, 26, 0xA0, 22),
+	PINGROUP(DTD,   VI,    RSVD,      SDIO2,     VI,        RSVD,          RSVD1,     0x14, 14, 0x84, 28, 0xA0, 24),
+	PINGROUP(DTE,   VI,    RSVD,      RSVD,      VI,        SPI1,          RSVD1,     0x14, 15, 0x84, 30, 0xA0, 26),
+	PINGROUP(DTF,   VI,    I2C3,      RSVD,      VI,        RSVD,          RSVD4,     0x20, 12, 0x98, 30, 0xA0, 28),
+	PINGROUP(GMA,   NAND,  UARTE,     SPI3,      GMI,       SDIO4,         SPI3,      0x14, 28, 0x84, 0,  0xB0, 20),
+	PINGROUP(GMB,   NAND,  IDE,       NAND,      GMI,       GMI_INT,       GMI,       0x18, 29, 0x88, 28, 0xB0, 22),
+	PINGROUP(GMC,   NAND,  UARTD,     SPI4,      GMI,       SFLASH,        SPI4,      0x14, 29, 0x84, 2,  0xB0, 24),
+	PINGROUP(GMD,   NAND,  RSVD,      NAND,      GMI,       SFLASH,        GMI,       0x18, 30, 0x88, 30, 0xB0, 26),
+	PINGROUP(GME,   NAND,  RSVD,      DAP5,      GMI,       SDIO4,         GMI,       0x18, 0,  0x8C, 0,  0xA8, 24),
+	PINGROUP(GPU,   UART,  PWM,       UARTA,     GMI,       RSVD,          RSVD4,     0x14, 16, 0x8C, 4,  0xA4, 20),
+	PINGROUP(GPU7,  SYS,   RTCK,      RSVD,      RSVD,      RSVD,          RTCK,      0x20, 11, 0x98, 28, 0xA4, 6),
+	PINGROUP(GPV,   SD,    PCIE,      RSVD,      RSVD,      RSVD,          PCIE,      0x14, 17, 0x8C, 2,  0xA0, 30),
+	PINGROUP(HDINT, LCD,   HDMI,      RSVD,      RSVD,      RSVD,          HDMI,      0x1C, 23, 0x84, 4,  0xAC, 22),
+	PINGROUP(I2CP,  SYS,   I2C,       RSVD,      RSVD,      RSVD,          RSVD4,     0x14, 18, 0x88, 8,  0xA4, 2),
+	PINGROUP(IRRX,  UART,  UARTA,     UARTB,     GMI,       SPI4,          UARTB,     0x14, 20, 0x88, 18, 0xA8, 22),
+	PINGROUP(IRTX,  UART,  UARTA,     UARTB,     GMI,       SPI4,          UARTB,     0x14, 19, 0x88, 16, 0xA8, 20),
+	PINGROUP(KBCA,  SYS,   KBC,       NAND,      SDIO2,     EMC_TEST0_DLL, KBC,       0x14, 22, 0x88, 10, 0xA4, 8),
+	PINGROUP(KBCB,  SYS,   KBC,       NAND,      SDIO2,     MIO,           KBC,       0x14, 21, 0x88, 12, 0xA4, 10),
+	PINGROUP(KBCC,  SYS,   KBC,       NAND,      TRACE,     EMC_TEST1_DLL, KBC,       0x18, 26, 0x88, 14, 0xA4, 12),
+	PINGROUP(KBCD,  SYS,   KBC,       NAND,      SDIO2,     MIO,           KBC,       0x20, 10, 0x98, 26, 0xA4, 14),
+	PINGROUP(KBCE,  SYS,   KBC,       NAND,      OWR,       RSVD,          KBC,       0x14, 26, 0x80, 28, 0xB0, 2),
+	PINGROUP(KBCF,  SYS,   KBC,       NAND,      TRACE,     MIO,           KBC,       0x14, 27, 0x80, 26, 0xB0, 0),
+	PINGROUP(LCSN,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      RSVD,          RSVD4,     0x1C, 31, 0x90, 12, 0xAC, 20),
+	PINGROUP(LD0,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 0,  0x94, 0,  0xAC, 12),
+	PINGROUP(LD1,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 1,  0x94, 2,  0xAC, 12),
+	PINGROUP(LD10,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 10, 0x94, 20, 0xAC, 12),
+	PINGROUP(LD11,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 11, 0x94, 22, 0xAC, 12),
+	PINGROUP(LD12,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 12, 0x94, 24, 0xAC, 12),
+	PINGROUP(LD13,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 13, 0x94, 26, 0xAC, 12),
+	PINGROUP(LD14,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 14, 0x94, 28, 0xAC, 12),
+	PINGROUP(LD15,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 15, 0x94, 30, 0xAC, 12),
+	PINGROUP(LD16,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 16, 0x98, 0,  0xAC, 12),
+	PINGROUP(LD17,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 17, 0x98, 2,  0xAC, 12),
+	PINGROUP(LD2,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 2,  0x94, 4,  0xAC, 12),
+	PINGROUP(LD3,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 3,  0x94, 6,  0xAC, 12),
+	PINGROUP(LD4,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 4,  0x94, 8,  0xAC, 12),
+	PINGROUP(LD5,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 5,  0x94, 10, 0xAC, 12),
+	PINGROUP(LD6,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 6,  0x94, 12, 0xAC, 12),
+	PINGROUP(LD7,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 7,  0x94, 14, 0xAC, 12),
+	PINGROUP(LD8,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 8,  0x94, 16, 0xAC, 12),
+	PINGROUP(LD9,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 9,  0x94, 18, 0xAC, 12),
+	PINGROUP(LDC,   LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 30, 0x90, 14, 0xAC, 20),
+	PINGROUP(LDI,   LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x20, 6,  0x98, 16, 0xAC, 18),
+	PINGROUP(LHP0,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 18, 0x98, 10, 0xAC, 16),
+	PINGROUP(LHP1,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 19, 0x98, 4,  0xAC, 14),
+	PINGROUP(LHP2,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 20, 0x98, 6,  0xAC, 14),
+	PINGROUP(LHS,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x20, 7,  0x90, 22, 0xAC, 22),
+	PINGROUP(LM0,   LCD,   DISPLAYA,  DISPLAYB,  SPI3,      RSVD,          RSVD4,     0x1C, 24, 0x90, 26, 0xAC, 22),
+	PINGROUP(LM1,   LCD,   DISPLAYA,  DISPLAYB,  RSVD,      CRT,           RSVD3,     0x1C, 25, 0x90, 28, 0xAC, 22),
+	PINGROUP(LPP,   LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x20, 8,  0x98, 14, 0xAC, 18),
+	PINGROUP(LPW0,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          DISPLAYA,  0x20, 3,  0x90, 0,  0xAC, 20),
+	PINGROUP(LPW1,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x20, 4,  0x90, 2,  0xAC, 20),
+	PINGROUP(LPW2,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          DISPLAYA,  0x20, 5,  0x90, 4,  0xAC, 20),
+	PINGROUP(LSC0,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 27, 0x90, 18, 0xAC, 22),
+	PINGROUP(LSC1,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          DISPLAYA,  0x1C, 28, 0x90, 20, 0xAC, 20),
+	PINGROUP(LSCK,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          DISPLAYA,  0x1C, 29, 0x90, 16, 0xAC, 20),
+	PINGROUP(LSDA,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          DISPLAYA,  0x20, 1,  0x90, 8,  0xAC, 20),
+	PINGROUP(LSDI,  LCD,   DISPLAYA,  DISPLAYB,  SPI3,      RSVD,          DISPLAYA,  0x20, 2,  0x90, 6,  0xAC, 20),
+	PINGROUP(LSPI,  LCD,   DISPLAYA,  DISPLAYB,  XIO,       HDMI,          DISPLAYA,  0x20, 0,  0x90, 10, 0xAC, 22),
+	PINGROUP(LVP0,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 21, 0x90, 30, 0xAC, 22),
+	PINGROUP(LVP1,  LCD,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          RSVD4,     0x1C, 22, 0x98, 8,  0xAC, 16),
+	PINGROUP(LVS,   LCD,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          RSVD4,     0x1C, 26, 0x90, 24, 0xAC, 22),
+	PINGROUP(OWC,   SYS,   OWR,       RSVD,      RSVD,      RSVD,          OWR,       0x14, 31, 0x84, 8,  0xB0, 30),
+	PINGROUP(PMC,   SYS,   PWR_ON,    PWR_INTR,  RSVD,      RSVD,          PWR_ON,    0x14, 23, 0x98, 18, -1,   -1),
+	PINGROUP(PTA,   NAND,  I2C2,      HDMI,      GMI,       RSVD,          RSVD4,     0x14, 24, 0x98, 22, 0xA4, 4),
+	PINGROUP(RM,    UART,  I2C,       RSVD,      RSVD,      RSVD,          RSVD4,     0x14, 25, 0x80, 14, 0xA4, 0),
+	PINGROUP(SDB,   SD,    UARTA,     PWM,       SDIO3,     SPI2,          PWM,       0x20, 15, 0x8C, 10, -1,   -1),
+	PINGROUP(SDC,   SD,    PWM,       TWC,       SDIO3,     SPI3,          TWC,       0x18, 1,  0x8C, 12, 0xAC, 28),
+	PINGROUP(SDD,   SD,    UARTA,     PWM,       SDIO3,     SPI3,          PWM,       0x18, 2,  0x8C, 14, 0xAC, 30),
+	PINGROUP(SDIO1, BB,    SDIO1,     RSVD,      UARTE,     UARTA,         RSVD2,     0x14, 30, 0x80, 30, 0xB0, 18),
+	PINGROUP(SLXA,  SD,    PCIE,      SPI4,      SDIO3,     SPI2,          PCIE,      0x18, 3,  0x84, 6,  0xA4, 22),
+	PINGROUP(SLXC,  SD,    SPDIF,     SPI4,      SDIO3,     SPI2,          SPI4,      0x18, 5,  0x84, 10, 0xA4, 26),
+	PINGROUP(SLXD,  SD,    SPDIF,     SPI4,      SDIO3,     SPI2,          SPI4,      0x18, 6,  0x84, 12, 0xA4, 28),
+	PINGROUP(SLXK,  SD,    PCIE,      SPI4,      SDIO3,     SPI2,          PCIE,      0x18, 7,  0x84, 14, 0xA4, 30),
+	PINGROUP(SPDI,  AUDIO, SPDIF,     RSVD,      I2C,       SDIO2,         RSVD2,     0x18, 8,  0x8C, 8,  0xA4, 16),
+	PINGROUP(SPDO,  AUDIO, SPDIF,     RSVD,      I2C,       SDIO2,         RSVD2,     0x18, 9,  0x8C, 6,  0xA4, 18),
+	PINGROUP(SPIA,  AUDIO, SPI1,      SPI2,      SPI3,      GMI,           GMI,       0x18, 10, 0x8C, 30, 0xA8, 4),
+	PINGROUP(SPIB,  AUDIO, SPI1,      SPI2,      SPI3,      GMI,           GMI,       0x18, 11, 0x8C, 28, 0xA8, 6),
+	PINGROUP(SPIC,  AUDIO, SPI1,      SPI2,      SPI3,      GMI,           GMI,       0x18, 12, 0x8C, 26, 0xA8, 8),
+	PINGROUP(SPID,  AUDIO, SPI2,      SPI1,      SPI2_ALT,  GMI,           GMI,       0x18, 13, 0x8C, 24, 0xA8, 10),
+	PINGROUP(SPIE,  AUDIO, SPI2,      SPI1,      SPI2_ALT,  GMI,           GMI,       0x18, 14, 0x8C, 22, 0xA8, 12),
+	PINGROUP(SPIF,  AUDIO, SPI3,      SPI1,      SPI2,      RSVD,          RSVD4,     0x18, 15, 0x8C, 20, 0xA8, 14),
+	PINGROUP(SPIG,  AUDIO, SPI3,      SPI2,      SPI2_ALT,  I2C,           SPI2_ALT,  0x18, 16, 0x8C, 18, 0xA8, 16),
+	PINGROUP(SPIH,  AUDIO, SPI3,      SPI2,      SPI2_ALT,  I2C,           SPI2_ALT,  0x18, 17, 0x8C, 16, 0xA8, 18),
+	PINGROUP(UAA,   BB,    SPI3,      MIPI_HS,   UARTA,     ULPI,          MIPI_HS,   0x18, 18, 0x80, 0,  0xAC, 0),
+	PINGROUP(UAB,   BB,    SPI2,      MIPI_HS,   UARTA,     ULPI,          MIPI_HS,   0x18, 19, 0x80, 2,  0xAC, 2),
+	PINGROUP(UAC,   BB,    OWR,       RSVD,      RSVD,      RSVD,          RSVD4,     0x18, 20, 0x80, 4,  0xAC, 4),
+	PINGROUP(UAD,   UART,  IRDA,      SPDIF,     UARTA,     SPI4,          SPDIF,     0x18, 21, 0x80, 6,  0xAC, 6),
+	PINGROUP(UCA,   UART,  UARTC,     RSVD,      GMI,       RSVD,          RSVD4,     0x18, 22, 0x84, 16, 0xAC, 8),
+	PINGROUP(UCB,   UART,  UARTC,     PWM,       GMI,       RSVD,          RSVD4,     0x18, 23, 0x84, 18, 0xAC, 10),
+	PINGROUP(UDA,   BB,    SPI1,      RSVD,      UARTD,     ULPI,          RSVD2,     0x20, 13, 0x80, 8,  0xB0, 16),
+	/* these pin groups only have pullup and pull down control */
+	PINGROUP(CK32,  SYS,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xB0, 14),
+	PINGROUP(DDRC,  DDR,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xAC, 26),
+	PINGROUP(PMCA,  SYS,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xB0, 4),
+	PINGROUP(PMCB,  SYS,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xB0, 6),
+	PINGROUP(PMCC,  SYS,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xB0, 8),
+	PINGROUP(PMCD,  SYS,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xB0, 10),
+	PINGROUP(PMCE,  SYS,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xB0, 12),
+	PINGROUP(XM2C,  DDR,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xA8, 30),
+	PINGROUP(XM2D,  DDR,   RSVD,      RSVD,      RSVD,      RSVD,          RSVD,      -1,   -1, -1,   -1, 0xA8, 28),
+};
+
+#ifdef CONFIG_PM
+#define TRISTATE_REG_A         0x14
+#define TRISTATE_REG_NUM       4
+#define PIN_MUX_CTL_REG_A      0x80
+#define PIN_MUX_CTL_REG_NUM    8
+#define PULLUPDOWN_REG_A       0xa0
+#define PULLUPDOWN_REG_NUM     5
+
+static u32 pinmux_reg[TRISTATE_REG_NUM + PIN_MUX_CTL_REG_NUM +
+		     PULLUPDOWN_REG_NUM];
+
+static inline unsigned long pg_readl(unsigned long offset)
+{
+	return readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
+}
+
+static inline void pg_writel(unsigned long value, unsigned long offset)
+{
+	writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
+}
+
+void tegra_pinmux_suspend(void)
+{
+	unsigned int i;
+	u32 *ctx = pinmux_reg;
+
+	for (i = 0; i < TRISTATE_REG_NUM; i++)
+		*ctx++ = pg_readl(TRISTATE_REG_A + i*4);
+
+	for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++)
+		*ctx++ = pg_readl(PIN_MUX_CTL_REG_A + i*4);
+
+	for (i = 0; i < PULLUPDOWN_REG_NUM; i++)
+		*ctx++ = pg_readl(PULLUPDOWN_REG_A + i*4);
+}
+
+void tegra_pinmux_resume(void)
+{
+	unsigned int i;
+	u32 *ctx = pinmux_reg;
+
+	for (i = 0; i < PIN_MUX_CTL_REG_NUM; i++)
+		pg_writel(*ctx++, PIN_MUX_CTL_REG_A + i*4);
+
+	for (i = 0; i < PULLUPDOWN_REG_NUM; i++)
+		pg_writel(*ctx++, PULLUPDOWN_REG_A + i*4);
+
+	for (i = 0; i < TRISTATE_REG_NUM; i++)
+		pg_writel(*ctx++, TRISTATE_REG_A + i*4);
+}
+#endif
diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
index 13ae10237e84688565af92668975a2c11e58171a..f80d507671bc1b22921ab2821c8a2cde427f1b5f 100644
--- a/arch/arm/mach-tegra/pinmux.c
+++ b/arch/arm/mach-tegra/pinmux.c
@@ -14,7 +14,8 @@
  *
  */
 
-
+#include <linux/init.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/spinlock.h>
@@ -23,21 +24,6 @@
 #include <mach/iomap.h>
 #include <mach/pinmux.h>
 
-
-#define TEGRA_TRI_STATE(x)	(0x14 + (4 * (x)))
-#define TEGRA_PP_MUX_CTL(x)	(0x80 + (4 * (x)))
-#define TEGRA_PP_PU_PD(x)	(0xa0 + (4 * (x)))
-
-#define REG_A 0
-#define REG_B 1
-#define REG_C 2
-#define REG_D 3
-#define REG_E 4
-#define REG_F 5
-#define REG_G 6
-
-#define REG_N -1
-
 #define HSM_EN(reg)	(((reg) >> 2) & 0x1)
 #define SCHMT_EN(reg)	(((reg) >> 3) & 0x1)
 #define LPMD(reg)	(((reg) >> 4) & 0x3)
@@ -46,154 +32,8 @@
 #define SLWR(reg)	(((reg) >> 28) & 0x3)
 #define SLWF(reg)	(((reg) >> 30) & 0x3)
 
-struct tegra_pingroup_desc {
-	const char *name;
-	int funcs[4];
-	s8 tri_reg; 	/* offset into the TRISTATE_REG_* register bank */
-	s8 tri_bit; 	/* offset into the TRISTATE_REG_* register bit */
-	s8 mux_reg;	/* offset into the PIN_MUX_CTL_* register bank */
-	s8 mux_bit;	/* offset into the PIN_MUX_CTL_* register bit */
-	s8 pupd_reg;	/* offset into the PULL_UPDOWN_REG_* register bank */
-	s8 pupd_bit;	/* offset into the PULL_UPDOWN_REG_* register bit */
-};
-
-#define PINGROUP(pg_name, f0, f1, f2, f3,			\
-		 tri_r, tri_b, mux_r, mux_b, pupd_r, pupd_b)	\
-	[TEGRA_PINGROUP_ ## pg_name] = {			\
-		.name = #pg_name,				\
-		.funcs = {					\
-			TEGRA_MUX_ ## f0,			\
-			TEGRA_MUX_ ## f1,			\
-			TEGRA_MUX_ ## f2,			\
-			TEGRA_MUX_ ## f3,			\
-		},						\
-		.tri_reg = REG_ ## tri_r,			\
-		.tri_bit = tri_b,				\
-		.mux_reg = REG_ ## mux_r,			\
-		.mux_bit = mux_b,				\
-		.pupd_reg = REG_ ## pupd_r,			\
-		.pupd_bit = pupd_b,				\
-	}
-
-static const struct tegra_pingroup_desc pingroups[TEGRA_MAX_PINGROUP] = {
-	PINGROUP(ATA,   IDE,       NAND,      GMI,       RSVD,          A, 0,  A, 24, A, 0),
-	PINGROUP(ATB,   IDE,       NAND,      GMI,       SDIO4,         A, 1,  A, 16, A, 2),
-	PINGROUP(ATC,   IDE,       NAND,      GMI,       SDIO4,         A, 2,  A, 22, A, 4),
-	PINGROUP(ATD,   IDE,       NAND,      GMI,       SDIO4,         A, 3,  A, 20, A, 6),
-	PINGROUP(ATE,   IDE,       NAND,      GMI,       RSVD,          B, 25, A, 12, A, 8),
-	PINGROUP(CDEV1, OSC,       PLLA_OUT,  PLLM_OUT1, AUDIO_SYNC,    A, 4,  C, 2,  C, 0),
-	PINGROUP(CDEV2, OSC,       AHB_CLK,   APB_CLK,   PLLP_OUT4,     A, 5,  C, 4,  C, 2),
-	PINGROUP(CRTP,  CRT,       RSVD,      RSVD,      RSVD,          D, 14, G, 20, B, 24),
-	PINGROUP(CSUS,  PLLC_OUT1, PLLP_OUT2, PLLP_OUT3, VI_SENSOR_CLK, A, 6,  C, 6,  D, 24),
-	PINGROUP(DAP1,  DAP1,      RSVD,      GMI,       SDIO2,         A, 7,  C, 20, A, 10),
-	PINGROUP(DAP2,  DAP2,      TWC,       RSVD,      GMI,           A, 8,  C, 22, A, 12),
-	PINGROUP(DAP3,  DAP3,      RSVD,      RSVD,      RSVD,          A, 9,  C, 24, A, 14),
-	PINGROUP(DAP4,  DAP4,      RSVD,      GMI,       RSVD,          A, 10, C, 26, A, 16),
-	PINGROUP(DDC,   I2C2,      RSVD,      RSVD,      RSVD,          B, 31, C, 0,  E, 28),
-	PINGROUP(DTA,   RSVD,      SDIO2,     VI,        RSVD,          A, 11, B, 20, A, 18),
-	PINGROUP(DTB,   RSVD,      RSVD,      VI,        SPI1,          A, 12, B, 22, A, 20),
-	PINGROUP(DTC,   RSVD,      RSVD,      VI,        RSVD,          A, 13, B, 26, A, 22),
-	PINGROUP(DTD,   RSVD,      SDIO2,     VI,        RSVD,          A, 14, B, 28, A, 24),
-	PINGROUP(DTE,   RSVD,      RSVD,      VI,        SPI1,          A, 15, B, 30, A, 26),
-	PINGROUP(DTF,   I2C3,      RSVD,      VI,        RSVD,          D, 12, G, 30, A, 28),
-	PINGROUP(GMA,   UARTE,     SPI3,      GMI,       SDIO4,         A, 28, B, 0,  E, 20),
-	PINGROUP(GMB,   IDE,       NAND,      GMI,       GMI_INT,       B, 29, C, 28, E, 22),
-	PINGROUP(GMC,   UARTD,     SPI4,      GMI,       SFLASH,        A, 29, B, 2,  E, 24),
-	PINGROUP(GMD,   RSVD,      NAND,      GMI,       SFLASH,        B, 30, C, 30, E, 26),
-	PINGROUP(GME,   RSVD,      DAP5,      GMI,       SDIO4,         B, 0,  D, 0,  C, 24),
-	PINGROUP(GPU,   PWM,       UARTA,     GMI,       RSVD,          A, 16, D, 4,  B, 20),
-	PINGROUP(GPU7,  RTCK,      RSVD,      RSVD,      RSVD,          D, 11, G, 28, B, 6),
-	PINGROUP(GPV,   PCIE,      RSVD,      RSVD,      RSVD,          A, 17, D, 2,  A, 30),
-	PINGROUP(HDINT, HDMI,      RSVD,      RSVD,      RSVD,          C, 23, B, 4,  D, 22),
-	PINGROUP(I2CP,  I2C,       RSVD,      RSVD,      RSVD,          A, 18, C, 8,  B, 2),
-	PINGROUP(IRRX,  UARTA,     UARTB,     GMI,       SPI4,          A, 20, C, 18, C, 22),
-	PINGROUP(IRTX,  UARTA,     UARTB,     GMI,       SPI4,          A, 19, C, 16, C, 20),
-	PINGROUP(KBCA,  KBC,       NAND,      SDIO2,     EMC_TEST0_DLL, A, 22, C, 10, B, 8),
-	PINGROUP(KBCB,  KBC,       NAND,      SDIO2,     MIO,           A, 21, C, 12, B, 10),
-	PINGROUP(KBCC,  KBC,       NAND,      TRACE,     EMC_TEST1_DLL, B, 26, C, 14, B, 12),
-	PINGROUP(KBCD,  KBC,       NAND,      SDIO2,     MIO,           D, 10, G, 26, B, 14),
-	PINGROUP(KBCE,  KBC,       NAND,      OWR,       RSVD,          A, 26, A, 28, E, 2),
-	PINGROUP(KBCF,  KBC,       NAND,      TRACE,     MIO,           A, 27, A, 26, E, 0),
-	PINGROUP(LCSN,  DISPLAYA,  DISPLAYB,  SPI3,      RSVD,          C, 31, E, 12, D, 20),
-	PINGROUP(LD0,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 0,  F, 0,  D, 12),
-	PINGROUP(LD1,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 1,  F, 2,  D, 12),
-	PINGROUP(LD10,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 10, F, 20, D, 12),
-	PINGROUP(LD11,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 11, F, 22, D, 12),
-	PINGROUP(LD12,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 12, F, 24, D, 12),
-	PINGROUP(LD13,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 13, F, 26, D, 12),
-	PINGROUP(LD14,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 14, F, 28, D, 12),
-	PINGROUP(LD15,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 15, F, 30, D, 12),
-	PINGROUP(LD16,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 16, G, 0,  D, 12),
-	PINGROUP(LD17,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 17, G, 2,  D, 12),
-	PINGROUP(LD2,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 2,  F, 4,  D, 12),
-	PINGROUP(LD3,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 3,  F, 6,  D, 12),
-	PINGROUP(LD4,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 4,  F, 8,  D, 12),
-	PINGROUP(LD5,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 5,  F, 10, D, 12),
-	PINGROUP(LD6,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 6,  F, 12, D, 12),
-	PINGROUP(LD7,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 7,  F, 14, D, 12),
-	PINGROUP(LD8,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 8,  F, 16, D, 12),
-	PINGROUP(LD9,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 9,  F, 18, D, 12),
-	PINGROUP(LDC,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 30, E, 14, D, 20),
-	PINGROUP(LDI,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          D, 6,  G, 16, D, 18),
-	PINGROUP(LHP0,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 18, G, 10, D, 16),
-	PINGROUP(LHP1,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 19, G, 4,  D, 14),
-	PINGROUP(LHP2,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 20, G, 6,  D, 14),
-	PINGROUP(LHS,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          D, 7,  E, 22, D, 22),
-	PINGROUP(LM0,   DISPLAYA,  DISPLAYB,  SPI3,      RSVD,          C, 24, E, 26, D, 22),
-	PINGROUP(LM1,   DISPLAYA,  DISPLAYB,  RSVD,      CRT,           C, 25, E, 28, D, 22),
-	PINGROUP(LPP,   DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          D, 8,  G, 14, D, 18),
-	PINGROUP(LPW0,  DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          D, 3,  E, 0,  D, 20),
-	PINGROUP(LPW1,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          D, 4,  E, 2,  D, 20),
-	PINGROUP(LPW2,  DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          D, 5,  E, 4,  D, 20),
-	PINGROUP(LSC0,  DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 27, E, 18, D, 22),
-	PINGROUP(LSC1,  DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          C, 28, E, 20, D, 20),
-	PINGROUP(LSCK,  DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          C, 29, E, 16, D, 20),
-	PINGROUP(LSDA,  DISPLAYA,  DISPLAYB,  SPI3,      HDMI,          D, 1,  E, 8,  D, 20),
-	PINGROUP(LSDI,  DISPLAYA,  DISPLAYB,  SPI3,      RSVD,          D, 2,  E, 6,  D, 20),
-	PINGROUP(LSPI,  DISPLAYA,  DISPLAYB,  XIO,       HDMI,          D, 0,  E, 10, D, 22),
-	PINGROUP(LVP0,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 21, E, 30, D, 22),
-	PINGROUP(LVP1,  DISPLAYA,  DISPLAYB,  RSVD,      RSVD,          C, 22, G, 8,  D, 16),
-	PINGROUP(LVS,   DISPLAYA,  DISPLAYB,  XIO,       RSVD,          C, 26, E, 24, D, 22),
-	PINGROUP(OWC,   OWR,       RSVD,      RSVD,      RSVD,          A, 31, B, 8,  E, 30),
-	PINGROUP(PMC,   PWR_ON,    PWR_INTR,  RSVD,      RSVD,          A, 23, G, 18, N, -1),
-	PINGROUP(PTA,   I2C2,      HDMI,      GMI,       RSVD,          A, 24, G, 22, B, 4),
-	PINGROUP(RM,    I2C,       RSVD,      RSVD,      RSVD,          A, 25, A, 14, B, 0),
-	PINGROUP(SDB,   UARTA,     PWM,       SDIO3,     SPI2,          D, 15, D, 10, N, -1),
-	PINGROUP(SDC,   PWM,       TWC,       SDIO3,     SPI3,          B, 1,  D, 12, D, 28),
-	PINGROUP(SDD,   UARTA,     PWM,       SDIO3,     SPI3,          B, 2,  D, 14, D, 30),
-	PINGROUP(SDIO1, SDIO1,     RSVD,      UARTE,     UARTA,         A, 30, A, 30, E, 18),
-	PINGROUP(SLXA,  PCIE,      SPI4,      SDIO3,     SPI2,          B, 3,  B, 6,  B, 22),
-	PINGROUP(SLXC,  SPDIF,     SPI4,      SDIO3,     SPI2,          B, 5,  B, 10, B, 26),
-	PINGROUP(SLXD,  SPDIF,     SPI4,      SDIO3,     SPI2,          B, 6,  B, 12, B, 28),
-	PINGROUP(SLXK,  PCIE,      SPI4,      SDIO3,     SPI2,          B, 7,  B, 14, B, 30),
-	PINGROUP(SPDI,  SPDIF,     RSVD,      I2C,       SDIO2,         B, 8,  D, 8,  B, 16),
-	PINGROUP(SPDO,  SPDIF,     RSVD,      I2C,       SDIO2,         B, 9,  D, 6,  B, 18),
-	PINGROUP(SPIA,  SPI1,      SPI2,      SPI3,      GMI,           B, 10, D, 30, C, 4),
-	PINGROUP(SPIB,  SPI1,      SPI2,      SPI3,      GMI,           B, 11, D, 28, C, 6),
-	PINGROUP(SPIC,  SPI1,      SPI2,      SPI3,      GMI,           B, 12, D, 26, C, 8),
-	PINGROUP(SPID,  SPI2,      SPI1,      SPI2_ALT,  GMI,           B, 13, D, 24, C, 10),
-	PINGROUP(SPIE,  SPI2,      SPI1,      SPI2_ALT,  GMI,           B, 14, D, 22, C, 12),
-	PINGROUP(SPIF,  SPI3,      SPI1,      SPI2,      RSVD,          B, 15, D, 20, C, 14),
-	PINGROUP(SPIG,  SPI3,      SPI2,      SPI2_ALT,  I2C,           B, 16, D, 18, C, 16),
-	PINGROUP(SPIH,  SPI3,      SPI2,      SPI2_ALT,  I2C,           B, 17, D, 16, C, 18),
-	PINGROUP(UAA,   SPI3,      MIPI_HS,   UARTA,     ULPI,          B, 18, A, 0,  D, 0),
-	PINGROUP(UAB,   SPI2,      MIPI_HS,   UARTA,     ULPI,          B, 19, A, 2,  D, 2),
-	PINGROUP(UAC,   OWR,       RSVD,      RSVD,      RSVD,          B, 20, A, 4,  D, 4),
-	PINGROUP(UAD,   IRDA,      SPDIF,     UARTA,     SPI4,          B, 21, A, 6,  D, 6),
-	PINGROUP(UCA,   UARTC,     RSVD,      GMI,       RSVD,          B, 22, B, 16, D, 8),
-	PINGROUP(UCB,   UARTC,     PWM,       GMI,       RSVD,          B, 23, B, 18, D, 10),
-	PINGROUP(UDA,   SPI1,      RSVD,      UARTD,     ULPI,          D, 13, A, 8,  E, 16),
-	/* these pin groups only have pullup and pull down control */
-	PINGROUP(CK32,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  E, 14),
-	PINGROUP(DDRC,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  D, 26),
-	PINGROUP(PMCA,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  E, 4),
-	PINGROUP(PMCB,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  E, 6),
-	PINGROUP(PMCC,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  E, 8),
-	PINGROUP(PMCD,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  E, 10),
-	PINGROUP(PMCE,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  E, 12),
-	PINGROUP(XM2C,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  C, 30),
-	PINGROUP(XM2D,  RSVD,      RSVD,      RSVD,      RSVD,          N, -1,  N, -1,  C, 28),
-};
+static const struct tegra_pingroup_desc *const pingroups = tegra_soc_pingroups;
+static const struct tegra_drive_pingroup_desc *const drive_pingroups = tegra_soc_drive_pingroups;
 
 static char *tegra_mux_names[TEGRA_MAX_MUX] = {
 	[TEGRA_MUX_AHB_CLK] = "AHB_CLK",
@@ -256,48 +96,7 @@ static char *tegra_mux_names[TEGRA_MAX_MUX] = {
 	[TEGRA_MUX_VI] = "VI",
 	[TEGRA_MUX_VI_SENSOR_CLK] = "VI_SENSOR_CLK",
 	[TEGRA_MUX_XIO] = "XIO",
-};
-
-struct tegra_drive_pingroup_desc {
-	const char *name;
-	s16 reg;
-};
-
-#define DRIVE_PINGROUP(pg_name, r)				\
-	[TEGRA_DRIVE_PINGROUP_ ## pg_name] = {			\
-		.name = #pg_name,				\
-		.reg = r					\
-	}
-
-static const struct tegra_drive_pingroup_desc drive_pingroups[TEGRA_MAX_PINGROUP] = {
-	DRIVE_PINGROUP(AO1,		0x868),
-	DRIVE_PINGROUP(AO2,		0x86c),
-	DRIVE_PINGROUP(AT1,		0x870),
-	DRIVE_PINGROUP(AT2,		0x874),
-	DRIVE_PINGROUP(CDEV1,		0x878),
-	DRIVE_PINGROUP(CDEV2,		0x87c),
-	DRIVE_PINGROUP(CSUS,		0x880),
-	DRIVE_PINGROUP(DAP1,		0x884),
-	DRIVE_PINGROUP(DAP2,		0x888),
-	DRIVE_PINGROUP(DAP3,		0x88c),
-	DRIVE_PINGROUP(DAP4,		0x890),
-	DRIVE_PINGROUP(DBG,		0x894),
-	DRIVE_PINGROUP(LCD1,		0x898),
-	DRIVE_PINGROUP(LCD2,		0x89c),
-	DRIVE_PINGROUP(SDMMC2,	0x8a0),
-	DRIVE_PINGROUP(SDMMC3,	0x8a4),
-	DRIVE_PINGROUP(SPI,		0x8a8),
-	DRIVE_PINGROUP(UAA,		0x8ac),
-	DRIVE_PINGROUP(UAB,		0x8b0),
-	DRIVE_PINGROUP(UART2,		0x8b4),
-	DRIVE_PINGROUP(UART3,		0x8b8),
-	DRIVE_PINGROUP(VI1,		0x8bc),
-	DRIVE_PINGROUP(VI2,		0x8c0),
-	DRIVE_PINGROUP(XM2A,		0x8c4),
-	DRIVE_PINGROUP(XM2C,		0x8c8),
-	DRIVE_PINGROUP(XM2D,		0x8cc),
-	DRIVE_PINGROUP(XM2CLK,	0x8d0),
-	DRIVE_PINGROUP(MEMCOMP,	0x8d4),
+	[TEGRA_MUX_SAFE] = "<safe>",
 };
 
 static const char *tegra_drive_names[TEGRA_MAX_DRIVE] = {
@@ -381,22 +180,27 @@ static inline void pg_writel(unsigned long value, unsigned long offset)
 	writel(value, IO_TO_VIRT(TEGRA_APB_MISC_BASE + offset));
 }
 
-int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func)
+static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config)
 {
 	int mux = -1;
 	int i;
 	unsigned long reg;
 	unsigned long flags;
+	enum tegra_pingroup pg = config->pingroup;
+	enum tegra_mux_func func = config->func;
 
 	if (pg < 0 || pg >=  TEGRA_MAX_PINGROUP)
 		return -ERANGE;
 
-	if (pingroups[pg].mux_reg == REG_N)
+	if (pingroups[pg].mux_reg < 0)
 		return -EINVAL;
 
 	if (func < 0)
 		return -ERANGE;
 
+	if (func == TEGRA_MUX_SAFE)
+		func = pingroups[pg].func_safe;
+
 	if (func & TEGRA_MUX_RSVD) {
 		mux = func & 0x3;
 	} else {
@@ -413,10 +217,10 @@ int tegra_pinmux_set_func(enum tegra_pingroup pg, enum tegra_mux_func func)
 
 	spin_lock_irqsave(&mux_lock, flags);
 
-	reg = pg_readl(TEGRA_PP_MUX_CTL(pingroups[pg].mux_reg));
+	reg = pg_readl(pingroups[pg].mux_reg);
 	reg &= ~(0x3 << pingroups[pg].mux_bit);
 	reg |= mux << pingroups[pg].mux_bit;
-	pg_writel(reg, TEGRA_PP_MUX_CTL(pingroups[pg].mux_reg));
+	pg_writel(reg, pingroups[pg].mux_reg);
 
 	spin_unlock_irqrestore(&mux_lock, flags);
 
@@ -432,16 +236,16 @@ int tegra_pinmux_set_tristate(enum tegra_pingroup pg,
 	if (pg < 0 || pg >=  TEGRA_MAX_PINGROUP)
 		return -ERANGE;
 
-	if (pingroups[pg].tri_reg == REG_N)
+	if (pingroups[pg].tri_reg < 0)
 		return -EINVAL;
 
 	spin_lock_irqsave(&mux_lock, flags);
 
-	reg = pg_readl(TEGRA_TRI_STATE(pingroups[pg].tri_reg));
+	reg = pg_readl(pingroups[pg].tri_reg);
 	reg &= ~(0x1 << pingroups[pg].tri_bit);
 	if (tristate)
 		reg |= 1 << pingroups[pg].tri_bit;
-	pg_writel(reg, TEGRA_TRI_STATE(pingroups[pg].tri_reg));
+	pg_writel(reg, pingroups[pg].tri_reg);
 
 	spin_unlock_irqrestore(&mux_lock, flags);
 
@@ -457,7 +261,7 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg,
 	if (pg < 0 || pg >=  TEGRA_MAX_PINGROUP)
 		return -ERANGE;
 
-	if (pingroups[pg].pupd_reg == REG_N)
+	if (pingroups[pg].pupd_reg < 0)
 		return -EINVAL;
 
 	if (pupd != TEGRA_PUPD_NORMAL &&
@@ -468,38 +272,39 @@ int tegra_pinmux_set_pullupdown(enum tegra_pingroup pg,
 
 	spin_lock_irqsave(&mux_lock, flags);
 
-	reg = pg_readl(TEGRA_PP_PU_PD(pingroups[pg].pupd_reg));
+	reg = pg_readl(pingroups[pg].pupd_reg);
 	reg &= ~(0x3 << pingroups[pg].pupd_bit);
 	reg |= pupd << pingroups[pg].pupd_bit;
-	pg_writel(reg, TEGRA_PP_PU_PD(pingroups[pg].pupd_reg));
+	pg_writel(reg, pingroups[pg].pupd_reg);
 
 	spin_unlock_irqrestore(&mux_lock, flags);
 
 	return 0;
 }
 
-void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup,
-				 enum tegra_mux_func func,
-				 enum tegra_pullupdown pupd,
-				 enum tegra_tristate tristate)
+static void tegra_pinmux_config_pingroup(const struct tegra_pingroup_config *config)
 {
+	enum tegra_pingroup pingroup = config->pingroup;
+	enum tegra_mux_func func     = config->func;
+	enum tegra_pullupdown pupd   = config->pupd;
+	enum tegra_tristate tristate = config->tristate;
 	int err;
 
-	if (pingroups[pingroup].mux_reg != REG_N) {
-		err = tegra_pinmux_set_func(pingroup, func);
+	if (pingroups[pingroup].mux_reg >= 0) {
+		err = tegra_pinmux_set_func(config);
 		if (err < 0)
 			pr_err("pinmux: can't set pingroup %s func to %s: %d\n",
 			       pingroup_name(pingroup), func_name(func), err);
 	}
 
-	if (pingroups[pingroup].pupd_reg != REG_N) {
+	if (pingroups[pingroup].pupd_reg >= 0) {
 		err = tegra_pinmux_set_pullupdown(pingroup, pupd);
 		if (err < 0)
 			pr_err("pinmux: can't set pingroup %s pullupdown to %s: %d\n",
 			       pingroup_name(pingroup), pupd_name(pupd), err);
 	}
 
-	if (pingroups[pingroup].tri_reg != REG_N) {
+	if (pingroups[pingroup].tri_reg >= 0) {
 		err = tegra_pinmux_set_tristate(pingroup, tristate);
 		if (err < 0)
 			pr_err("pinmux: can't set pingroup %s tristate to %s: %d\n",
@@ -507,17 +312,12 @@ void tegra_pinmux_config_pingroup(enum tegra_pingroup pingroup,
 	}
 }
 
-
-
-void tegra_pinmux_config_table(struct tegra_pingroup_config *config, int len)
+void tegra_pinmux_config_table(const struct tegra_pingroup_config *config, int len)
 {
 	int i;
 
 	for (i = 0; i < len; i++)
-		tegra_pinmux_config_pingroup(config[i].pingroup,
-					     config[i].func,
-					     config[i].pupd,
-					     config[i].tristate);
+		tegra_pinmux_config_pingroup(&config[i]);
 }
 
 static const char *drive_pinmux_name(enum tegra_drive_pingroup pg)
@@ -784,6 +584,86 @@ void tegra_drive_pinmux_config_table(struct tegra_drive_pingroup_config *config,
 						     config[i].slew_falling);
 }
 
+void tegra_pinmux_set_safe_pinmux_table(const struct tegra_pingroup_config *config,
+	int len)
+{
+	int i;
+	struct tegra_pingroup_config c;
+
+	for (i = 0; i < len; i++) {
+		int err;
+		c = config[i];
+		if (c.pingroup < 0 || c.pingroup >= TEGRA_MAX_PINGROUP) {
+			WARN_ON(1);
+			continue;
+		}
+		c.func = pingroups[c.pingroup].func_safe;
+		err = tegra_pinmux_set_func(&c);
+		if (err < 0)
+			pr_err("%s: tegra_pinmux_set_func returned %d setting "
+			       "%s to %s\n", __func__, err,
+			       pingroup_name(c.pingroup), func_name(c.func));
+	}
+}
+
+void tegra_pinmux_config_pinmux_table(const struct tegra_pingroup_config *config,
+	int len)
+{
+	int i;
+
+	for (i = 0; i < len; i++) {
+		int err;
+		if (config[i].pingroup < 0 ||
+		    config[i].pingroup >= TEGRA_MAX_PINGROUP) {
+			WARN_ON(1);
+			continue;
+		}
+		err = tegra_pinmux_set_func(&config[i]);
+		if (err < 0)
+			pr_err("%s: tegra_pinmux_set_func returned %d setting "
+			       "%s to %s\n", __func__, err,
+			       pingroup_name(config[i].pingroup),
+			       func_name(config[i].func));
+	}
+}
+
+void tegra_pinmux_config_tristate_table(const struct tegra_pingroup_config *config,
+	int len, enum tegra_tristate tristate)
+{
+	int i;
+	int err;
+	enum tegra_pingroup pingroup;
+
+	for (i = 0; i < len; i++) {
+		pingroup = config[i].pingroup;
+		if (pingroups[pingroup].tri_reg >= 0) {
+			err = tegra_pinmux_set_tristate(pingroup, tristate);
+			if (err < 0)
+				pr_err("pinmux: can't set pingroup %s tristate"
+					" to %s: %d\n",	pingroup_name(pingroup),
+					tri_name(tristate), err);
+		}
+	}
+}
+
+void tegra_pinmux_config_pullupdown_table(const struct tegra_pingroup_config *config,
+	int len, enum tegra_pullupdown pupd)
+{
+	int i;
+	int err;
+	enum tegra_pingroup pingroup;
+
+	for (i = 0; i < len; i++) {
+		pingroup = config[i].pingroup;
+		if (pingroups[pingroup].pupd_reg >= 0) {
+			err = tegra_pinmux_set_pullupdown(pingroup, pupd);
+			if (err < 0)
+				pr_err("pinmux: can't set pingroup %s pullupdown"
+					" to %s: %d\n",	pingroup_name(pingroup),
+					pupd_name(pupd), err);
+		}
+	}
+}
 
 #ifdef	CONFIG_DEBUG_FS
 
@@ -812,11 +692,11 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
 		len = strlen(pingroups[i].name);
 		dbg_pad_field(s, 5 - len);
 
-		if (pingroups[i].mux_reg == REG_N) {
+		if (pingroups[i].mux_reg < 0) {
 			seq_printf(s, "TEGRA_MUX_NONE");
 			len = strlen("NONE");
 		} else {
-			mux = (pg_readl(TEGRA_PP_MUX_CTL(pingroups[i].mux_reg)) >>
+			mux = (pg_readl(pingroups[i].mux_reg) >>
 			       pingroups[i].mux_bit) & 0x3;
 			if (pingroups[i].funcs[mux] == TEGRA_MUX_RSVD) {
 				seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1);
@@ -829,21 +709,21 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
 		}
 		dbg_pad_field(s, 13-len);
 
-		if (pingroups[i].mux_reg == REG_N) {
+		if (pingroups[i].pupd_reg < 0) {
 			seq_printf(s, "TEGRA_PUPD_NORMAL");
 			len = strlen("NORMAL");
 		} else {
-			pupd = (pg_readl(TEGRA_PP_PU_PD(pingroups[i].pupd_reg)) >>
+			pupd = (pg_readl(pingroups[i].pupd_reg) >>
 				pingroups[i].pupd_bit) & 0x3;
 			seq_printf(s, "TEGRA_PUPD_%s", pupd_name(pupd));
 			len = strlen(pupd_name(pupd));
 		}
 		dbg_pad_field(s, 9 - len);
 
-		if (pingroups[i].tri_reg == REG_N) {
+		if (pingroups[i].tri_reg < 0) {
 			seq_printf(s, "TEGRA_TRI_NORMAL");
 		} else {
-			tri = (pg_readl(TEGRA_TRI_STATE(pingroups[i].tri_reg)) >>
+			tri = (pg_readl(pingroups[i].tri_reg) >>
 			       pingroups[i].tri_bit) & 0x1;
 
 			seq_printf(s, "TEGRA_TRI_%s", tri_name(tri));
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index 426163231fff155cdd9c1a9094e33d3ac6fa7217..ae3b308e22a4d158b464d8b47f383a7509cf6efd 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,10 +104,14 @@
 #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
 
+#define PLLE_MISC_READY			(1 << 15)
+
 #define PERIPH_CLK_TO_ENB_REG(c)	((c->clk_num / 32) * 4)
 #define PERIPH_CLK_TO_ENB_SET_REG(c)	((c->clk_num / 32) * 8)
 #define PERIPH_CLK_TO_ENB_BIT(c)	(1 << (c->clk_num % 32))
@@ -143,30 +163,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 +271,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 +292,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 +300,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 +323,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 +387,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 +427,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 +455,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 +481,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 +511,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 +552,46 @@ 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,
+};
+
+static int tegra2_plle_clk_enable(struct clk *c)
+{
+	u32 val;
+
+	pr_debug("%s on clock %s\n", __func__, c->name);
+
+	mdelay(1);
+
+	val = clk_readl(c->reg + PLL_BASE);
+	if (!(val & PLLE_MISC_READY))
+		return -EBUSY;
+
+	val = clk_readl(c->reg + PLL_BASE);
+	val |= PLL_BASE_ENABLE | PLL_BASE_BYPASS;
+	clk_writel(val, c->reg + PLL_BASE);
+
+	return 0;
+}
+
+static struct clk_ops tegra_plle_ops = {
+	.init       = tegra2_pll_clk_init,
+	.enable     = tegra2_plle_clk_enable,
+	.set_rate   = tegra2_pll_clk_set_rate,
 };
 
 /* Clock divider ops */
@@ -503,8 +615,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 +675,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 +690,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 +743,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 +763,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 +817,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 +840,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 +899,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 +911,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 +1036,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 +1052,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 +1072,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 +1082,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 +1110,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 +1120,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 +1148,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 +1158,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 +1168,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 +1178,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 +1188,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 +1196,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 +1213,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 +1223,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 +1247,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 +1255,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 +1279,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 +1341,24 @@ static struct clk tegra_pll_x = {
 	.vco_min   = 20000000,
 	.vco_max   = 1200000000,
 	.pll_table = tegra_pll_x_table,
+	.max_rate  = 1000000000,
+};
+
+static struct clk_pll_table tegra_pll_e_table[] = {
+	{ 12000000, 100000000,  200,  24, 1, 0 },
+	{ 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_e = {
+	.name      = "pll_e",
+	.flags	   = PLL_ALT_MISC_REG,
+	.ops       = &tegra_plle_ops,
+	.input_min = 12000000,
+	.input_max = 12000000,
+	.max_rate  = 100000000,
+	.parent    = &tegra_clk_m,
+	.reg       = 0xe8,
+	.pll_table = tegra_pll_e_table,
 };
 
 static struct clk tegra_clk_d = {
@@ -1038,19 +1369,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 +1466,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 +1509,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 +1536,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 +1555,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 +1588,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 +1600,79 @@ 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),
+	PERIPH_CLK("pex",       NULL,			"pex",  70,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
+	PERIPH_CLK("afi",       NULL,			"afi",  72,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
+	PERIPH_CLK("pcie_xclk", NULL,		  "pcie_xclk",  74,     0,	26000000,  mux_clk_m,			PERIPH_MANUAL_RESET),
 };
 
 #define CLK_DUPLICATE(_name, _dev, _con)		\
@@ -1286,6 +1694,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 +1726,13 @@ 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,	"pll_e",	&tegra_pll_e),
+	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 +1769,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 0000000000000000000000000000000000000000..5529c238dd77d783f315cf2c893ef188c574b0a2
--- /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@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 0000000000000000000000000000000000000000..f8c1adba96a6db529334921d5ea6f1573667e259
--- /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@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;
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 4b9eec68fad6b84bca9c2f5f6f397f0e9be79c45..78f9fd02c1b27c3ebf0753e1d9a995212b96ba48 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -329,6 +329,13 @@ config SPI_STMP3XXX
 	help
 	  SPI driver for Freescale STMP37xx/378x SoC SSP interface
 
+config SPI_TEGRA
+	tristate "Nvidia Tegra SPI controller"
+	depends on ARCH_TEGRA
+	select TEGRA_SYSTEM_DMA
+	help
+	  SPI driver for NVidia Tegra SoCs
+
 config SPI_TOPCLIFF_PCH
 	tristate "Topcliff PCH SPI Controller"
 	depends on PCI
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 557aaadf56b2df40620a3b02073b44b78f09fb66..8bc1a5abac1fea65c166ba1c1a15e4c63b25a05d 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -39,6 +39,7 @@ obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
 obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
 obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx_hw.o
 obj-$(CONFIG_SPI_S3C64XX)		+= spi_s3c64xx.o
+obj-$(CONFIG_SPI_TEGRA)			+= spi_tegra.o
 obj-$(CONFIG_SPI_TOPCLIFF_PCH)		+= spi_topcliff_pch.o
 obj-$(CONFIG_SPI_TXX9)			+= spi_txx9.o
 obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
diff --git a/drivers/spi/spi_tegra.c b/drivers/spi/spi_tegra.c
new file mode 100644
index 0000000000000000000000000000000000000000..bb7df02a5472fdf3f7caf73d6ae82a6ffca9464b
--- /dev/null
+++ b/drivers/spi/spi_tegra.c
@@ -0,0 +1,618 @@
+/*
+ * Driver for Nvidia TEGRA spi controller.
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ *     Erik Gilling <konkers@android.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 <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/clk.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+
+#include <linux/spi/spi.h>
+
+#include <mach/dma.h>
+
+#define SLINK_COMMAND		0x000
+#define   SLINK_BIT_LENGTH(x)		(((x) & 0x1f) << 0)
+#define   SLINK_WORD_SIZE(x)		(((x) & 0x1f) << 5)
+#define   SLINK_BOTH_EN			(1 << 10)
+#define   SLINK_CS_SW			(1 << 11)
+#define   SLINK_CS_VALUE		(1 << 12)
+#define   SLINK_CS_POLARITY		(1 << 13)
+#define   SLINK_IDLE_SDA_DRIVE_LOW	(0 << 16)
+#define   SLINK_IDLE_SDA_DRIVE_HIGH	(1 << 16)
+#define   SLINK_IDLE_SDA_PULL_LOW	(2 << 16)
+#define   SLINK_IDLE_SDA_PULL_HIGH	(3 << 16)
+#define   SLINK_IDLE_SDA_MASK		(3 << 16)
+#define   SLINK_CS_POLARITY1		(1 << 20)
+#define   SLINK_CK_SDA			(1 << 21)
+#define   SLINK_CS_POLARITY2		(1 << 22)
+#define   SLINK_CS_POLARITY3		(1 << 23)
+#define   SLINK_IDLE_SCLK_DRIVE_LOW	(0 << 24)
+#define   SLINK_IDLE_SCLK_DRIVE_HIGH	(1 << 24)
+#define   SLINK_IDLE_SCLK_PULL_LOW	(2 << 24)
+#define   SLINK_IDLE_SCLK_PULL_HIGH	(3 << 24)
+#define   SLINK_IDLE_SCLK_MASK		(3 << 24)
+#define   SLINK_M_S			(1 << 28)
+#define   SLINK_WAIT			(1 << 29)
+#define   SLINK_GO			(1 << 30)
+#define   SLINK_ENB			(1 << 31)
+
+#define SLINK_COMMAND2		0x004
+#define   SLINK_LSBFE			(1 << 0)
+#define   SLINK_SSOE			(1 << 1)
+#define   SLINK_SPIE			(1 << 4)
+#define   SLINK_BIDIROE			(1 << 6)
+#define   SLINK_MODFEN			(1 << 7)
+#define   SLINK_INT_SIZE(x)		(((x) & 0x1f) << 8)
+#define   SLINK_CS_ACTIVE_BETWEEN	(1 << 17)
+#define   SLINK_SS_EN_CS(x)		(((x) & 0x3) << 18)
+#define   SLINK_SS_SETUP(x)		(((x) & 0x3) << 20)
+#define   SLINK_FIFO_REFILLS_0		(0 << 22)
+#define   SLINK_FIFO_REFILLS_1		(1 << 22)
+#define   SLINK_FIFO_REFILLS_2		(2 << 22)
+#define   SLINK_FIFO_REFILLS_3		(3 << 22)
+#define   SLINK_FIFO_REFILLS_MASK	(3 << 22)
+#define   SLINK_WAIT_PACK_INT(x)	(((x) & 0x7) << 26)
+#define   SLINK_SPC0			(1 << 29)
+#define   SLINK_TXEN			(1 << 30)
+#define   SLINK_RXEN			(1 << 31)
+
+#define SLINK_STATUS		0x008
+#define   SLINK_COUNT(val)		(((val) >> 0) & 0x1f)
+#define   SLINK_WORD(val)		(((val) >> 5) & 0x1f)
+#define   SLINK_BLK_CNT(val)		(((val) >> 0) & 0xffff)
+#define   SLINK_MODF			(1 << 16)
+#define   SLINK_RX_UNF			(1 << 18)
+#define   SLINK_TX_OVF			(1 << 19)
+#define   SLINK_TX_FULL			(1 << 20)
+#define   SLINK_TX_EMPTY		(1 << 21)
+#define   SLINK_RX_FULL			(1 << 22)
+#define   SLINK_RX_EMPTY		(1 << 23)
+#define   SLINK_TX_UNF			(1 << 24)
+#define   SLINK_RX_OVF			(1 << 25)
+#define   SLINK_TX_FLUSH		(1 << 26)
+#define   SLINK_RX_FLUSH		(1 << 27)
+#define   SLINK_SCLK			(1 << 28)
+#define   SLINK_ERR			(1 << 29)
+#define   SLINK_RDY			(1 << 30)
+#define   SLINK_BSY			(1 << 31)
+
+#define SLINK_MAS_DATA		0x010
+#define SLINK_SLAVE_DATA	0x014
+
+#define SLINK_DMA_CTL		0x018
+#define   SLINK_DMA_BLOCK_SIZE(x)	(((x) & 0xffff) << 0)
+#define   SLINK_TX_TRIG_1		(0 << 16)
+#define   SLINK_TX_TRIG_4		(1 << 16)
+#define   SLINK_TX_TRIG_8		(2 << 16)
+#define   SLINK_TX_TRIG_16		(3 << 16)
+#define   SLINK_TX_TRIG_MASK		(3 << 16)
+#define   SLINK_RX_TRIG_1		(0 << 18)
+#define   SLINK_RX_TRIG_4		(1 << 18)
+#define   SLINK_RX_TRIG_8		(2 << 18)
+#define   SLINK_RX_TRIG_16		(3 << 18)
+#define   SLINK_RX_TRIG_MASK		(3 << 18)
+#define   SLINK_PACKED			(1 << 20)
+#define   SLINK_PACK_SIZE_4		(0 << 21)
+#define   SLINK_PACK_SIZE_8		(1 << 21)
+#define   SLINK_PACK_SIZE_16		(2 << 21)
+#define   SLINK_PACK_SIZE_32		(3 << 21)
+#define   SLINK_PACK_SIZE_MASK		(3 << 21)
+#define   SLINK_IE_TXC			(1 << 26)
+#define   SLINK_IE_RXC			(1 << 27)
+#define   SLINK_DMA_EN			(1 << 31)
+
+#define SLINK_STATUS2		0x01c
+#define   SLINK_TX_FIFO_EMPTY_COUNT(val)	(((val) & 0x3f) >> 0)
+#define   SLINK_RX_FIFO_FULL_COUNT(val)		(((val) & 0x3f) >> 16)
+
+#define SLINK_TX_FIFO		0x100
+#define SLINK_RX_FIFO		0x180
+
+static const unsigned long spi_tegra_req_sels[] = {
+	TEGRA_DMA_REQ_SEL_SL2B1,
+	TEGRA_DMA_REQ_SEL_SL2B2,
+	TEGRA_DMA_REQ_SEL_SL2B3,
+	TEGRA_DMA_REQ_SEL_SL2B4,
+};
+
+#define BB_LEN			32
+
+struct spi_tegra_data {
+	struct spi_master	*master;
+	struct platform_device	*pdev;
+	spinlock_t		lock;
+
+	struct clk		*clk;
+	void __iomem		*base;
+	unsigned long		phys;
+
+	u32			cur_speed;
+
+	struct list_head	queue;
+	struct spi_transfer	*cur;
+	unsigned		cur_pos;
+	unsigned		cur_len;
+	unsigned		cur_bytes_per_word;
+
+	/* The tegra spi controller has a bug which causes the first word
+	 * in PIO transactions to be garbage.  Since packed DMA transactions
+	 * require transfers to be 4 byte aligned we need a bounce buffer
+	 * for the generic case.
+	 */
+	struct tegra_dma_req	rx_dma_req;
+	struct tegra_dma_channel *rx_dma;
+	u32			*rx_bb;
+	dma_addr_t		rx_bb_phys;
+};
+
+
+static inline unsigned long spi_tegra_readl(struct spi_tegra_data *tspi,
+					    unsigned long reg)
+{
+	return readl(tspi->base + reg);
+}
+
+static inline void spi_tegra_writel(struct spi_tegra_data *tspi,
+				    unsigned long val,
+				    unsigned long reg)
+{
+	writel(val, tspi->base + reg);
+}
+
+static void spi_tegra_go(struct spi_tegra_data *tspi)
+{
+	unsigned long val;
+
+	wmb();
+
+	val = spi_tegra_readl(tspi, SLINK_DMA_CTL);
+	val &= ~SLINK_DMA_BLOCK_SIZE(~0) & ~SLINK_DMA_EN;
+	val |= SLINK_DMA_BLOCK_SIZE(tspi->rx_dma_req.size / 4 - 1);
+	spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
+
+	tegra_dma_enqueue_req(tspi->rx_dma, &tspi->rx_dma_req);
+
+	val |= SLINK_DMA_EN;
+	spi_tegra_writel(tspi, val, SLINK_DMA_CTL);
+}
+
+static unsigned spi_tegra_fill_tx_fifo(struct spi_tegra_data *tspi,
+				  struct spi_transfer *t)
+{
+	unsigned len = min(t->len - tspi->cur_pos, BB_LEN *
+			   tspi->cur_bytes_per_word);
+	u8 *tx_buf = (u8 *)t->tx_buf + tspi->cur_pos;
+	int i, j;
+	unsigned long val;
+
+	val = spi_tegra_readl(tspi, SLINK_COMMAND);
+	val &= ~SLINK_WORD_SIZE(~0);
+	val |= SLINK_WORD_SIZE(len / tspi->cur_bytes_per_word - 1);
+	spi_tegra_writel(tspi, val, SLINK_COMMAND);
+
+	for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
+		val = 0;
+		for (j = 0; j < tspi->cur_bytes_per_word; j++)
+			val |= tx_buf[i + j] << j * 8;
+
+		spi_tegra_writel(tspi, val, SLINK_TX_FIFO);
+	}
+
+	tspi->rx_dma_req.size = len / tspi->cur_bytes_per_word * 4;
+
+	return len;
+}
+
+static unsigned spi_tegra_drain_rx_fifo(struct spi_tegra_data *tspi,
+				  struct spi_transfer *t)
+{
+	unsigned len = tspi->cur_len;
+	u8 *rx_buf = (u8 *)t->rx_buf + tspi->cur_pos;
+	int i, j;
+	unsigned long val;
+
+	for (i = 0; i < len; i += tspi->cur_bytes_per_word) {
+		val = tspi->rx_bb[i / tspi->cur_bytes_per_word];
+		for (j = 0; j < tspi->cur_bytes_per_word; j++)
+			rx_buf[i + j] = (val >> (j * 8)) & 0xff;
+	}
+
+	return len;
+}
+
+static void spi_tegra_start_transfer(struct spi_device *spi,
+				    struct spi_transfer *t)
+{
+	struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
+	u32 speed;
+	u8 bits_per_word;
+	unsigned long val;
+
+	speed = t->speed_hz ? t->speed_hz : spi->max_speed_hz;
+	bits_per_word = t->bits_per_word ? t->bits_per_word  :
+		spi->bits_per_word;
+
+	tspi->cur_bytes_per_word = (bits_per_word - 1) / 8 + 1;
+
+	if (speed != tspi->cur_speed)
+		clk_set_rate(tspi->clk, speed);
+
+	if (tspi->cur_speed == 0)
+		clk_enable(tspi->clk);
+
+	tspi->cur_speed = speed;
+
+	val = spi_tegra_readl(tspi, SLINK_COMMAND2);
+	val &= ~SLINK_SS_EN_CS(~0) | SLINK_RXEN | SLINK_TXEN;
+	if (t->rx_buf)
+		val |= SLINK_RXEN;
+	if (t->tx_buf)
+		val |= SLINK_TXEN;
+	val |= SLINK_SS_EN_CS(spi->chip_select);
+	val |= SLINK_SPIE;
+	spi_tegra_writel(tspi, val, SLINK_COMMAND2);
+
+	val = spi_tegra_readl(tspi, SLINK_COMMAND);
+	val &= ~SLINK_BIT_LENGTH(~0);
+	val |= SLINK_BIT_LENGTH(bits_per_word - 1);
+
+	/* FIXME: should probably control CS manually so that we can be sure
+	 * it does not go low between transfer and to support delay_usecs
+	 * correctly.
+	 */
+	val &= ~SLINK_IDLE_SCLK_MASK & ~SLINK_CK_SDA & ~SLINK_CS_SW;
+
+	if (spi->mode & SPI_CPHA)
+		val |= SLINK_CK_SDA;
+
+	if (spi->mode & SPI_CPOL)
+		val |= SLINK_IDLE_SCLK_DRIVE_HIGH;
+	else
+		val |= SLINK_IDLE_SCLK_DRIVE_LOW;
+
+	val |= SLINK_M_S;
+
+	spi_tegra_writel(tspi, val, SLINK_COMMAND);
+
+	spi_tegra_writel(tspi, SLINK_RX_FLUSH | SLINK_TX_FLUSH, SLINK_STATUS);
+
+	tspi->cur = t;
+	tspi->cur_pos = 0;
+	tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, t);
+
+	spi_tegra_go(tspi);
+}
+
+static void spi_tegra_start_message(struct spi_device *spi,
+				    struct spi_message *m)
+{
+	struct spi_transfer *t;
+
+	m->actual_length = 0;
+	m->status = 0;
+
+	t = list_first_entry(&m->transfers, struct spi_transfer, transfer_list);
+	spi_tegra_start_transfer(spi, t);
+}
+
+static void tegra_spi_rx_dma_complete(struct tegra_dma_req *req)
+{
+	struct spi_tegra_data *tspi = req->dev;
+	unsigned long flags;
+	struct spi_message *m;
+	struct spi_device *spi;
+	int timeout = 0;
+	unsigned long val;
+
+	/* the SPI controller may come back with both the BSY and RDY bits
+	 * set.  In this case we need to wait for the BSY bit to clear so
+	 * that we are sure the DMA is finished.  1000 reads was empirically
+	 * determined to be long enough.
+	 */
+	while (timeout++ < 1000) {
+		if (!(spi_tegra_readl(tspi, SLINK_STATUS) & SLINK_BSY))
+			break;
+	}
+
+	spin_lock_irqsave(&tspi->lock, flags);
+
+	val = spi_tegra_readl(tspi, SLINK_STATUS);
+	val |= SLINK_RDY;
+	spi_tegra_writel(tspi, val, SLINK_STATUS);
+
+	m = list_first_entry(&tspi->queue, struct spi_message, queue);
+
+	if (timeout >= 1000)
+		m->status = -EIO;
+
+	spi = m->state;
+
+	tspi->cur_pos += spi_tegra_drain_rx_fifo(tspi, tspi->cur);
+	m->actual_length += tspi->cur_pos;
+
+	if (tspi->cur_pos < tspi->cur->len) {
+		tspi->cur_len = spi_tegra_fill_tx_fifo(tspi, tspi->cur);
+		spi_tegra_go(tspi);
+	} else if (!list_is_last(&tspi->cur->transfer_list,
+				 &m->transfers)) {
+		tspi->cur =  list_first_entry(&tspi->cur->transfer_list,
+					      struct spi_transfer,
+					      transfer_list);
+		spi_tegra_start_transfer(spi, tspi->cur);
+	} else {
+		list_del(&m->queue);
+
+		m->complete(m->context);
+
+		if (!list_empty(&tspi->queue)) {
+			m = list_first_entry(&tspi->queue, struct spi_message,
+					     queue);
+			spi = m->state;
+			spi_tegra_start_message(spi, m);
+		} else {
+			clk_disable(tspi->clk);
+			tspi->cur_speed = 0;
+		}
+	}
+
+	spin_unlock_irqrestore(&tspi->lock, flags);
+}
+
+static int spi_tegra_setup(struct spi_device *spi)
+{
+	struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
+	unsigned long cs_bit;
+	unsigned long val;
+	unsigned long flags;
+
+	dev_dbg(&spi->dev, "setup %d bpw, %scpol, %scpha, %dHz\n",
+		spi->bits_per_word,
+		spi->mode & SPI_CPOL ? "" : "~",
+		spi->mode & SPI_CPHA ? "" : "~",
+		spi->max_speed_hz);
+
+
+	switch (spi->chip_select) {
+	case 0:
+		cs_bit = SLINK_CS_POLARITY;
+		break;
+
+	case 1:
+		cs_bit = SLINK_CS_POLARITY1;
+		break;
+
+	case 2:
+		cs_bit = SLINK_CS_POLARITY2;
+		break;
+
+	case 4:
+		cs_bit = SLINK_CS_POLARITY3;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&tspi->lock, flags);
+
+	val = spi_tegra_readl(tspi, SLINK_COMMAND);
+	if (spi->mode & SPI_CS_HIGH)
+		val |= cs_bit;
+	else
+		val &= ~cs_bit;
+	spi_tegra_writel(tspi, val, SLINK_COMMAND);
+
+	spin_unlock_irqrestore(&tspi->lock, flags);
+
+	return 0;
+}
+
+static int spi_tegra_transfer(struct spi_device *spi, struct spi_message *m)
+{
+	struct spi_tegra_data *tspi = spi_master_get_devdata(spi->master);
+	struct spi_transfer *t;
+	unsigned long flags;
+	int was_empty;
+
+	if (list_empty(&m->transfers) || !m->complete)
+		return -EINVAL;
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		if (t->bits_per_word < 0 || t->bits_per_word > 32)
+			return -EINVAL;
+
+		if (t->len == 0)
+			return -EINVAL;
+
+		if (!t->rx_buf && !t->tx_buf)
+			return -EINVAL;
+	}
+
+	m->state = spi;
+
+	spin_lock_irqsave(&tspi->lock, flags);
+	was_empty = list_empty(&tspi->queue);
+	list_add_tail(&m->queue, &tspi->queue);
+
+	if (was_empty)
+		spi_tegra_start_message(spi, m);
+
+	spin_unlock_irqrestore(&tspi->lock, flags);
+
+	return 0;
+}
+
+static int __init spi_tegra_probe(struct platform_device *pdev)
+{
+	struct spi_master	*master;
+	struct spi_tegra_data	*tspi;
+	struct resource		*r;
+	int ret;
+
+	master = spi_alloc_master(&pdev->dev, sizeof *tspi);
+	if (master == NULL) {
+		dev_err(&pdev->dev, "master allocation failed\n");
+		return -ENOMEM;
+	}
+
+	/* the spi->mode bits understood by this driver: */
+	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
+
+	master->bus_num = pdev->id;
+
+	master->setup = spi_tegra_setup;
+	master->transfer = spi_tegra_transfer;
+	master->num_chipselect = 4;
+
+	dev_set_drvdata(&pdev->dev, master);
+	tspi = spi_master_get_devdata(master);
+	tspi->master = master;
+	tspi->pdev = pdev;
+	spin_lock_init(&tspi->lock);
+
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (r == NULL) {
+		ret = -ENODEV;
+		goto err0;
+	}
+
+	if (!request_mem_region(r->start, (r->end - r->start) + 1,
+				dev_name(&pdev->dev))) {
+		ret = -EBUSY;
+		goto err0;
+	}
+
+	tspi->phys = r->start;
+	tspi->base = ioremap(r->start, r->end - r->start + 1);
+	if (!tspi->base) {
+		dev_err(&pdev->dev, "can't ioremap iomem\n");
+		ret = -ENOMEM;
+		goto err1;
+	}
+
+	tspi->clk = clk_get(&pdev->dev, NULL);
+	if (IS_ERR_OR_NULL(tspi->clk)) {
+		dev_err(&pdev->dev, "can not get clock\n");
+		ret = PTR_ERR(tspi->clk);
+		goto err2;
+	}
+
+	INIT_LIST_HEAD(&tspi->queue);
+
+	tspi->rx_dma = tegra_dma_allocate_channel(TEGRA_DMA_MODE_ONESHOT);
+	if (!tspi->rx_dma) {
+		dev_err(&pdev->dev, "can not allocate rx dma channel\n");
+		ret = -ENODEV;
+		goto err3;
+	}
+
+	tspi->rx_bb = dma_alloc_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
+					 &tspi->rx_bb_phys, GFP_KERNEL);
+	if (!tspi->rx_bb) {
+		dev_err(&pdev->dev, "can not allocate rx bounce buffer\n");
+		ret = -ENOMEM;
+		goto err4;
+	}
+
+	tspi->rx_dma_req.complete = tegra_spi_rx_dma_complete;
+	tspi->rx_dma_req.to_memory = 1;
+	tspi->rx_dma_req.dest_addr = tspi->rx_bb_phys;
+	tspi->rx_dma_req.dest_bus_width = 32;
+	tspi->rx_dma_req.source_addr = tspi->phys + SLINK_RX_FIFO;
+	tspi->rx_dma_req.source_bus_width = 32;
+	tspi->rx_dma_req.source_wrap = 4;
+	tspi->rx_dma_req.req_sel = spi_tegra_req_sels[pdev->id];
+	tspi->rx_dma_req.dev = tspi;
+
+	ret = spi_register_master(master);
+
+	if (ret < 0)
+		goto err5;
+
+	return ret;
+
+err5:
+	dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
+			  tspi->rx_bb, tspi->rx_bb_phys);
+err4:
+	tegra_dma_free_channel(tspi->rx_dma);
+err3:
+	clk_put(tspi->clk);
+err2:
+	iounmap(tspi->base);
+err1:
+	release_mem_region(r->start, (r->end - r->start) + 1);
+err0:
+	spi_master_put(master);
+	return ret;
+}
+
+static int __devexit spi_tegra_remove(struct platform_device *pdev)
+{
+	struct spi_master	*master;
+	struct spi_tegra_data	*tspi;
+	struct resource		*r;
+
+	master = dev_get_drvdata(&pdev->dev);
+	tspi = spi_master_get_devdata(master);
+
+	tegra_dma_free_channel(tspi->rx_dma);
+
+	dma_free_coherent(&pdev->dev, sizeof(u32) * BB_LEN,
+			  tspi->rx_bb, tspi->rx_bb_phys);
+
+	clk_put(tspi->clk);
+	iounmap(tspi->base);
+
+	spi_master_put(master);
+	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	release_mem_region(r->start, (r->end - r->start) + 1);
+
+	return 0;
+}
+
+MODULE_ALIAS("platform:spi_tegra");
+
+static struct platform_driver spi_tegra_driver = {
+	.driver = {
+		.name =		"spi_tegra",
+		.owner =	THIS_MODULE,
+	},
+	.remove =	__devexit_p(spi_tegra_remove),
+};
+
+static int __init spi_tegra_init(void)
+{
+	return platform_driver_probe(&spi_tegra_driver, spi_tegra_probe);
+}
+module_init(spi_tegra_init);
+
+static void __exit spi_tegra_exit(void)
+{
+	platform_driver_unregister(&spi_tegra_driver);
+}
+module_exit(spi_tegra_exit);
+
+MODULE_LICENSE("GPL");