Merge branch 'for-next' of git://android.git.kernel.org/kernel/tegra
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 26 Oct 2010 01:42:06 +0000 (18:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 26 Oct 2010 01:42:06 +0000 (18:42 -0700)
* 'for-next' of git://android.git.kernel.org/kernel/tegra:
  spi: tegra: fix error setting on timeout
  spi: add spi_tegra driver
  tegra: harmony: enable PCI Express
  tegra: add PCI Express support
  tegra: add PCI Express clocks
  [ARM] tegra: Add APB DMA support
  [ARM] tegra: Add cpufreq support
  [ARM] tegra: common: Update common clock init table
  [ARM] tegra: clock: Add dvfs support, bug fixes, and cleanups
  [ARM] tegra: Add support for reading fuses
  [ARM] tegra: gpio: Add suspend and wake support
  [ARM] tegra: pinmux: add safe values, move tegra2, add suspend
  [ARM] tegra: add suspend and mirror irqs to legacy controller
  [ARM] tegra: Add legacy irq support
  [ARM] tegra: update iomap

35 files changed:
arch/arm/Kconfig
arch/arm/mach-tegra/Kconfig
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/board-harmony-pcie.c [new file with mode: 0644]
arch/arm/mach-tegra/board.h
arch/arm/mach-tegra/clock.c
arch/arm/mach-tegra/clock.h
arch/arm/mach-tegra/common.c
arch/arm/mach-tegra/cpu-tegra.c [new file with mode: 0644]
arch/arm/mach-tegra/dma.c [new file with mode: 0644]
arch/arm/mach-tegra/fuse.c [new file with mode: 0644]
arch/arm/mach-tegra/fuse.h [new file with mode: 0644]
arch/arm/mach-tegra/gpio.c
arch/arm/mach-tegra/include/mach/clk.h
arch/arm/mach-tegra/include/mach/dma.h [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/gpio.h
arch/arm/mach-tegra/include/mach/hardware.h
arch/arm/mach-tegra/include/mach/io.h
arch/arm/mach-tegra/include/mach/iomap.h
arch/arm/mach-tegra/include/mach/irqs.h
arch/arm/mach-tegra/include/mach/legacy_irq.h [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/pinmux-t2.h [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/pinmux.h
arch/arm/mach-tegra/io.c
arch/arm/mach-tegra/irq.c
arch/arm/mach-tegra/legacy_irq.c [new file with mode: 0644]
arch/arm/mach-tegra/pcie.c [new file with mode: 0644]
arch/arm/mach-tegra/pinmux-t2-tables.c [new file with mode: 0644]
arch/arm/mach-tegra/pinmux.c
arch/arm/mach-tegra/tegra2_clocks.c
arch/arm/mach-tegra/tegra2_dvfs.c [new file with mode: 0644]
arch/arm/mach-tegra/tegra2_dvfs.h [new file with mode: 0644]
drivers/spi/Kconfig
drivers/spi/Makefile
drivers/spi/spi_tegra.c [new file with mode: 0644]

index f401b92..bf7273f 100644 (file)
@@ -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).
index a57713c..acd9552 100644 (file)
@@ -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
index 51e9370..cdbc68e 100644 (file)
@@ -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 (file)
index 0000000..f7e7d45
--- /dev/null
@@ -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
index 3d06354..0de565c 100644 (file)
@@ -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
index 03ad578..ae19f95 100644 (file)
 #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)
index af7c70e..94fd859 100644 (file)
 #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);
 
index 039a514..7c91e2b 100644 (file)
 
 #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 (file)
index 0000000..fea5719
--- /dev/null
@@ -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 (file)
index 0000000..edda6ec
--- /dev/null
@@ -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 (file)
index 0000000..1fa26d9
--- /dev/null
@@ -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 (file)
index 0000000..584b2e2
--- /dev/null
@@ -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);
index fe78fba..0775265 100644 (file)
@@ -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;
index 2896f25..d772395 100644 (file)
@@ -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 (file)
index 0000000..39011bd
--- /dev/null
@@ -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
index 540e822..e31f486 100644 (file)
@@ -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;
 }
index 6014edf..56e43b3 100644 (file)
@@ -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
index 35edfc3..f0981b1 100644 (file)
@@ -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
  *
  */
 
+#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)
 
index 1741f7d..44a4f4b 100644 (file)
 
 #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
 
 #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
 #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
 #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
 
index 20f640e..71bbf34 100644 (file)
@@ -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)
 #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 (file)
index 0000000..db1eb3d
--- /dev/null
@@ -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 (file)
index 0000000..e5b9d74
--- /dev/null
@@ -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
+
index 41c8ce5..defd877 100644 (file)
 #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
 
index 9fe2c5c..31848a9 100644 (file)
@@ -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)
index 1fdbe70..50a8dfb 100644 (file)
@@ -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.
 
 #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 (file)
index 0000000..7cc8601
--- /dev/null
@@ -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 (file)
index 0000000..53f5fa3
--- /dev/null
@@ -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 (file)
index 0000000..a6ea34e
--- /dev/null
@@ -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
index 13ae102..f80d507 100644 (file)
@@ -14,7 +14,8 @@
  *
  */
 
-
+#include <linux/init.h>
+#include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/spinlock.h>
 #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)
 #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));
index 4261632..ae3b308 100644 (file)
 #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)
 #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
 #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 (file)
index 0000000..5529c23
--- /dev/null
@@ -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 (file)
index 0000000..f8c1adb
--- /dev/null
@@ -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;
index 4b9eec6..78f9fd0 100644 (file)
@@ -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
index 557aaad..8bc1a5a 100644 (file)
@@ -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 (file)
index 0000000..bb7df02
--- /dev/null
@@ -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");