[ARM/tegra] Add Tegra3 support
Scott Williams [Tue, 7 Dec 2010 19:19:20 +0000 (11:19 -0800)]
Bug 764354

Original-Change-Id: I8a390eb4dae87dceacb97461f23d13554868b046
Reviewed-on: http://git-master/r/12228
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Tested-by: Scott Williams <scwilliams@nvidia.com>
Original-Change-Id: I8e6b8303898796419fb5a759cd16edff9aeac081

Rebase-Id: R2866240384c6c24f46bd7ef54bc3dc9140d9e96b

50 files changed:
arch/arm/mach-tegra/Kconfig
arch/arm/mach-tegra/Makefile
arch/arm/mach-tegra/Makefile.boot
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
arch/arm/mach-tegra/cpuidle.c
arch/arm/mach-tegra/devices.c
arch/arm/mach-tegra/devices.h
arch/arm/mach-tegra/dma.c
arch/arm/mach-tegra/dvfs.h
arch/arm/mach-tegra/fuse.c
arch/arm/mach-tegra/gpio-names.h
arch/arm/mach-tegra/headsmp-t3.S [new file with mode: 0644]
arch/arm/mach-tegra/headsmp.S
arch/arm/mach-tegra/hotplug.c
arch/arm/mach-tegra/include/mach/dma.h
arch/arm/mach-tegra/include/mach/iomap.h
arch/arm/mach-tegra/include/mach/iovmm.h
arch/arm/mach-tegra/include/mach/irqs.h
arch/arm/mach-tegra/include/mach/mc.h
arch/arm/mach-tegra/include/mach/memory.h
arch/arm/mach-tegra/include/mach/pinmux-t3.h [new file with mode: 0644]
arch/arm/mach-tegra/include/mach/pinmux.h
arch/arm/mach-tegra/include/mach/powergate.h
arch/arm/mach-tegra/include/mach/uncompress.h
arch/arm/mach-tegra/iovmm-gart.c
arch/arm/mach-tegra/iovmm-smmu.c [new file with mode: 0644]
arch/arm/mach-tegra/iovmm.c
arch/arm/mach-tegra/irq.c
arch/arm/mach-tegra/mc.c
arch/arm/mach-tegra/pinmux-t2-tables.c
arch/arm/mach-tegra/pinmux-t3-tables.c [new file with mode: 0644]
arch/arm/mach-tegra/pinmux.c
arch/arm/mach-tegra/platsmp.c
arch/arm/mach-tegra/pm-t3.c [new file with mode: 0644]
arch/arm/mach-tegra/pm.c
arch/arm/mach-tegra/pm.h
arch/arm/mach-tegra/powergate.c
arch/arm/mach-tegra/sysfs-cluster.c [new file with mode: 0644]
arch/arm/mach-tegra/sysfs-dcc.c [new file with mode: 0644]
arch/arm/mach-tegra/tegra2_clocks.c
arch/arm/mach-tegra/tegra2_dvfs.c
arch/arm/mach-tegra/tegra3_clocks.c [new file with mode: 0644]
arch/arm/mach-tegra/tegra3_dvfs.c [new file with mode: 0644]
arch/arm/mach-tegra/tegra3_save.S [new file with mode: 0644]
arch/arm/mach-tegra/timer.c
arch/arm/mach-tegra/wakeups-t3.c [new file with mode: 0644]
drivers/gpio/gpio-tegra.c

index d74e2fa..3c295bf 100644 (file)
@@ -21,6 +21,20 @@ config ARCH_TEGRA_2x_SOC
          Support for NVIDIA Tegra AP20 and T20 processors, based on the
          ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
 
+config ARCH_TEGRA_3x_SOC
+       bool "Tegra 3 family"
+       select CPU_V7
+       select ARM_GIC
+       select ARCH_REQUIRE_GPIOLIB
+       select TEGRA_IOVMM
+       select USB_ARCH_HAS_EHCI if USB_SUPPORT
+       select USB_EHCI_TEGRA if USB_SUPPORT
+       select USB_ULPI if USB_SUPPORT
+       select USB_ULPI_VIEWPORT if USB_SUPPORT
+       help
+         Support for NVIDIA Tegra 3 family of SoCs, based upon the
+         ARM CortexA9MP CPU and the ARM PL310 L2 cache controller
+
 endchoice
 
 config TEGRA_PCI
@@ -86,6 +100,9 @@ config MACH_WARIO
 
 # Enterprise
 
+config TEGRA_FPGA_PLATFORM
+       bool
+
 choice
         prompt "Low-level debug console UART"
         default TEGRA_DEBUG_UART_NONE
@@ -152,6 +169,16 @@ config TEGRA_IOVMM_GART
          shared with the operating system into contiguous I/O virtual
          space through the GART hardware included on Tegra SoCs
 
+config TEGRA_IOVMM_SMMU
+       bool "Enable I/O virtual memory manager for SMMU"
+       depends on ARCH_TEGRA_3x_SOC
+       default y
+       select TEGRA_IOVMM
+       help
+         Enables support for remapping discontiguous physical memory
+         shared with the operating system into contiguous I/O virtual
+         space through the SMMU hardware included on Tegra SoCs
+
 config TEGRA_IOVMM
        bool
 
index 7756063..d94bccb 100644 (file)
@@ -24,21 +24,28 @@ obj-$(CONFIG_FIQ)                       += fiq.o
 obj-$(CONFIG_TEGRA_PWM)                 += pwm.o
 obj-$(CONFIG_TEGRA_ARB_SEMAPHORE)      += arb_sema.o
 
-obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += clock.o
+obj-y                                   += clock.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_clocks.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += tegra3_clocks.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_dvfs.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += tegra3_dvfs.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_fuse.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += tegra2_speedo.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += tegra3_save.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += tegra2_emc.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += wakeups-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)                += wakeups-t3.o
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)         += pm-t2.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += pm-t3.o
 
 obj-$(CONFIG_ARCH_TEGRA_2x_SOC)                += pinmux-t2-tables.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += pinmux-t3-tables.o
 obj-$(CONFIG_LOCAL_TIMERS)             += localtimer.o
 obj-$(CONFIG_SMP)                      += platsmp.o
 obj-$(CONFIG_HOTPLUG_CPU)              += hotplug.o
 obj-$(CONFIG_SMP)                      += headsmp.o
+obj-$(CONFIG_ARCH_TEGRA_3x_SOC)         += headsmp-t3.o
 obj-$(CONFIG_TEGRA_SYSTEM_DMA)         += dma.o
 obj-$(CONFIG_CPU_FREQ)                  += cpu-tegra.o
 obj-$(CONFIG_TEGRA_PCI)                        += pcie.o
@@ -46,6 +53,7 @@ obj-$(CONFIG_USB_SUPPORT)             += usb_phy.o
 obj-$(CONFIG_CPU_IDLE)                 += cpuidle.o
 obj-$(CONFIG_TEGRA_IOVMM)               += iovmm.o
 obj-$(CONFIG_TEGRA_IOVMM_GART)          += iovmm-gart.o
+obj-$(CONFIG_TEGRA_IOVMM_SMMU)          += iovmm-smmu.o
 obj-$(CONFIG_TEGRA_MC_PROFILE)          += tegra2_mc.o
 
 obj-${CONFIG_TEGRA_SPI_SLAVE}          += spi_tegra_slave.o
index 428ad12..d8cb917 100644 (file)
@@ -2,5 +2,9 @@ zreladdr-$(CONFIG_ARCH_TEGRA_2x_SOC)    := 0x00008000
 params_phys-$(CONFIG_ARCH_TEGRA_2x_SOC)        := 0x00000100
 initrd_phys-$(CONFIG_ARCH_TEGRA_2x_SOC)        := 0x00800000
 
+zreladdr-$(CONFIG_ARCH_TEGRA_3x_SOC)   := 0x80008000
+params_phys-$(CONFIG_ARCH_TEGRA_3x_SOC)        := 0x80000100
+initrd_phys-$(CONFIG_ARCH_TEGRA_3x_SOC)        := 0x80800000
+
 dtb-$(CONFIG_MACH_HARMONY) += tegra-harmony.dtb
 dtb-$(CONFIG_MACH_SEABOARD) += tegra-seaboard.dtb
index 3c09fbe..0096f6f 100644 (file)
@@ -103,6 +103,9 @@ static unsigned long clk_predict_rate_from_parent(struct clk *c, struct clk *p)
 
        rate = clk_get_rate(p);
 
+       if (c->ops && c->ops->recalculate_rate)
+               c->ops->recalculate_rate(c);
+
        if (c->mul != 0 && c->div != 0) {
                rate *= c->mul;
                rate += c->div - 1; /* round up */
@@ -311,13 +314,16 @@ EXPORT_SYMBOL(clk_get_parent);
 int clk_set_rate_locked(struct clk *c, unsigned long rate)
 {
        int ret = 0;
-       unsigned long old_rate;
+       unsigned long old_rate, max_rate;
        long new_rate;
 
        old_rate = clk_get_rate_locked(c);
 
-       if (rate > c->max_rate)
-               rate = c->max_rate;
+       max_rate = c->max_rate;
+       if (c->ops && c->ops->get_max_rate)
+               max_rate = c->ops->get_max_rate(c);
+       if (rate > max_rate)
+               rate = max_rate;
 
        if (c->ops && c->ops->round_rate) {
                new_rate = c->ops->round_rate(c, rate);
@@ -374,6 +380,8 @@ unsigned long clk_get_rate_all_locked(struct clk *c)
 
        while (p) {
                c = p;
+               if (c->ops && c->ops->recalculate_rate)
+                       c->ops->recalculate_rate(c);
                if (c->mul != 0 && c->div != 0) {
                        mul *= c->mul;
                        div *= c->div;
@@ -390,7 +398,7 @@ unsigned long clk_get_rate_all_locked(struct clk *c)
 
 long clk_round_rate(struct clk *c, unsigned long rate)
 {
-       unsigned long flags;
+       unsigned long flags, max_rate;
        long ret;
 
        clk_lock_save(c, &flags);
@@ -400,8 +408,11 @@ long clk_round_rate(struct clk *c, unsigned long rate)
                goto out;
        }
 
-       if (rate > c->max_rate)
-               rate = c->max_rate;
+       max_rate = c->max_rate;
+       if (c->ops && c->ops->get_max_rate)
+               max_rate = c->ops->get_max_rate(c);
+       if (rate > max_rate)
+               rate = max_rate;
 
        ret = c->ops->round_rate(c, rate);
 
@@ -474,20 +485,22 @@ EXPORT_SYMBOL(tegra_clk_init_from_table);
 
 void tegra_periph_reset_deassert(struct clk *c)
 {
-       tegra2_periph_reset_deassert(c);
+       BUG_ON(!c->ops->reset);
+       c->ops->reset(c, false);
 }
 EXPORT_SYMBOL(tegra_periph_reset_deassert);
 
 void tegra_periph_reset_assert(struct clk *c)
 {
-       tegra2_periph_reset_assert(c);
+       BUG_ON(!c->ops->reset);
+       c->ops->reset(c, true);
 }
 EXPORT_SYMBOL(tegra_periph_reset_assert);
 
 void __init tegra_init_clock(void)
 {
-       tegra2_init_clocks();
-       tegra2_init_dvfs();
+       tegra_soc_init_clocks();
+       tegra_soc_init_dvfs();
 }
 
 /*
@@ -676,6 +689,11 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
        struct clk *child;
        const char *state = "uninit";
        char div[8] = {0};
+       unsigned long rate = clk_get_rate_all_locked(c);
+       unsigned long max_rate = c->max_rate;
+
+       if (c->ops && c->ops->get_max_rate)
+               max_rate = c->ops->get_max_rate(c);
 
        if (c->state == ON)
                state = "on";
@@ -701,10 +719,10 @@ static void clock_tree_show_one(struct seq_file *s, struct clk *c, int level)
 
        seq_printf(s, "%*s%c%c%-*s %-6s %-3d %-8s %-10lu\n",
                level * 3 + 1, "",
-               c->rate > c->max_rate ? '!' : ' ',
+               rate > max_rate ? '!' : ' ',
                !c->set ? '*' : ' ',
                30 - level * 3, c->name,
-               state, c->refcnt, div, clk_get_rate_all_locked(c));
+               state, c->refcnt, div, rate);
 
        if (c->dvfs)
                dvfs_show_one(s, c->dvfs, level + 1);
index fed02b2..4f32594 100644 (file)
 #ifndef __MACH_TEGRA_CLOCK_H
 #define __MACH_TEGRA_CLOCK_H
 
-#include <linux/clkdev.h>
-#include <linux/list.h>
-#include <linux/mutex.h>
-#include <linux/spinlock.h>
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+#define USE_PLL_LOCK_BITS 0    /* Never use lock bits on Tegra2 */
+#else
+/* !!!FIXME!!! PLL lock bits should work on Tegra3 */
+#define USE_PLL_LOCK_BITS 0    /* Use lock bits for PLL stabiliation */
+#endif
 
 #define DIV_BUS                        (1 << 0)
 #define DIV_U71                        (1 << 1)
 #define PERIPH_MANUAL_RESET    (1 << 12)
 #define PLL_ALT_MISC_REG       (1 << 13)
 #define PLLU                   (1 << 14)
+#define PLLX                   (1 << 15)
+#define MUX_PWM                        (1 << 16)
+#define MUX8                   (1 << 17)
 #define ENABLE_ON_INIT         (1 << 28)
 
+#ifndef __ASSEMBLY__
+
+#include <linux/clkdev.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+
 #define MAX_SAME_LIMIT_SKU_IDS 16
 
 struct clk;
@@ -67,6 +79,8 @@ struct clk_ops {
        int             (*set_parent)(struct clk *, struct clk *);
        int             (*set_rate)(struct clk *, unsigned long);
        long            (*round_rate)(struct clk *, unsigned long);
+       unsigned long   (*get_max_rate)(struct clk *);
+       void            (*recalculate_rate)(struct clk *);
        void            (*reset)(struct clk *, bool);
 };
 
@@ -121,6 +135,7 @@ struct clk {
                        unsigned long                   vco_max;
                        const struct clk_pll_freq_table *freq_table;
                        int                             lock_delay;
+                       unsigned long                   fixed_rate;
                } pll;
                struct {
                        u32                             sel;
@@ -129,6 +144,7 @@ struct clk {
                struct {
                        struct clk                      *main;
                        struct clk                      *backup;
+                       unsigned long                   lp_max_rate;
                } cpu;
                struct {
                        struct list_head                node;
@@ -159,6 +175,7 @@ struct tegra_sku_rate_limit {
        int sku_ids[MAX_SAME_LIMIT_SKU_IDS];
 };
 
+void tegra_soc_init_clocks(void);
 void tegra2_init_clocks(void);
 void tegra2_periph_reset_deassert(struct clk *c);
 void tegra2_periph_reset_assert(struct clk *c);
@@ -223,3 +240,4 @@ struct tegra_cpufreq_table_data *tegra_cpufreq_table_get(void);
 #endif
 
 #endif
+#endif
index ee23ecd..7a24565 100644 (file)
@@ -40,7 +40,7 @@
 #include "fuse.h"
 #include "pm.h"
 
-#define MC_SECURITY_CFG2 0x7c
+#define MC_SECURITY_CFG2       0x7c
 
 unsigned long tegra_bootloader_fb_start;
 unsigned long tegra_bootloader_fb_size;
@@ -80,7 +80,7 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
        /* name         parent          rate            enabled */
        { "clk_m",      NULL,           0,              true },
        { "pll_m",      "clk_m",        600000000,      true },
-       { "pll_p",      "clk_m",        216000000,      true },
+       { "pll_p",      NULL,           216000000,      true },
        { "pll_p_out1", "pll_p",        28800000,       true },
        { "pll_p_out2", "pll_p",        48000000,       true },
        { "pll_p_out3", "pll_p",        72000000,       true },
@@ -93,7 +93,7 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = {
        { "emc",        NULL,           0,              true },
        { "cpu",        NULL,           0,              true },
        { "kfuse",      NULL,           0,              true },
-       { "pll_u",      "clk_m",        480000000,      false },
+       { "pll_u",      NULL,           480000000,      false },
        { "sdmmc1",     "pll_p",        48000000,       false},
        { "sdmmc2",     "pll_p",        48000000,       false},
        { "sdmmc3",     "pll_p",        48000000,       false},
@@ -106,9 +106,38 @@ void tegra_init_cache(void)
 #ifdef CONFIG_CACHE_L2X0
        void __iomem *p = IO_ADDRESS(TEGRA_ARM_PERIF_BASE) + 0x3000;
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
        writel_relaxed(0x331, p + L2X0_TAG_LATENCY_CTRL);
        writel_relaxed(0x441, p + L2X0_DATA_LATENCY_CTRL);
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#ifdef CONFIG_TEGRA_FPGA_PLATFORM
+       writel(0x770, p + L2X0_TAG_LATENCY_CTRL);
+       writel(0x770, p + L2X0_DATA_LATENCY_CTRL);
+       {
+               void __iomem *misc = IO_ADDRESS(TEGRA_APB_MISC_BASE);
+               u32 val = readl(misc + APB_MISC_HIDREV);
+               u32 major = (val>>4) & 0xf;
+               u32 netlist = readl(misc + 0x860);
+
+               if ((major == 0) && ((netlist & 0xFFFF) >= 12)) {
+                       /* Enable PL310 double line fill feature. */
+                       writel(((1<<30) | 7), p + L2X0_PREFETCH_CTRL);
+               } else {
+                       writel(7, p + L2X0_PREFETCH_CTRL);
+               }
+       }
+#else
+       /* FIXME: Need characterized timing parameters for real silicon */
+       writel(0x331, p + L2X0_TAG_LATENCY_CTRL);
+       writel(0x441, p + L2X0_DATA_LATENCY_CTRL);
+       writel(7, p + L2X0_PREFETCH_CTRL);
+       writel(2, p + L2X0_PWR_CTRL);
+#endif
+
+       /* Enable PL310 double line fill feature. */
+       writel(((1<<30) | 7), p + L2X0_PREFETCH_CTRL);
+#endif
        l2x0_init(p, 0x6C480001, 0x8200c3fe);
 #endif
 
index 5ffb854..3da60ac 100644 (file)
 
 static struct cpufreq_frequency_table *freq_table;
 
-#define NUM_CPUS       2
 
 static struct clk *cpu_clk;
 static struct clk *emc_clk;
 
-static unsigned long target_cpu_speed[NUM_CPUS];
+static unsigned long target_cpu_speed[CONFIG_NR_CPUS];
 static DEFINE_MUTEX(tegra_cpu_lock);
 static bool is_suspended;
 
@@ -194,7 +193,7 @@ unsigned int tegra_getspeed(unsigned int cpu)
 {
        unsigned long rate;
 
-       if (cpu >= NUM_CPUS)
+       if (cpu >= CONFIG_NR_CPUS)
                return 0;
 
        rate = clk_get_rate(cpu_clk) / 1000;
@@ -311,7 +310,7 @@ static struct notifier_block tegra_cpu_pm_notifier = {
 
 static int tegra_cpu_init(struct cpufreq_policy *policy)
 {
-       if (policy->cpu >= NUM_CPUS)
+       if (policy->cpu >= CONFIG_NR_CPUS)
                return -EINVAL;
 
        cpu_clk = clk_get_sys(NULL, "cpu");
index 18861b3..7eed5c1 100644 (file)
 #include "pm.h"
 #include "sleep.h"
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #define TEGRA_CPUIDLE_BOTH_IDLE                INT_QUAD_RES_24
 #define TEGRA_CPUIDLE_TEAR_DOWN                INT_QUAD_RES_25
+#else
+/* !!!FIXME!!! THIS MODEL IS BROKEN ON T30 -- 4 CPUS BREAKS THE "BOTH" IDLE CONCEPT .....*/
+#define TEGRA_CPUIDLE_BOTH_IDLE                INT_QUINT_RES_24
+#define TEGRA_CPUIDLE_TEAR_DOWN                INT_QUINT_RES_25
+#endif
 
 static bool lp2_in_idle __read_mostly = true;
 module_param(lp2_in_idle, bool, 0644);
index 6597465..a89d210 100644 (file)
@@ -70,6 +70,7 @@ static struct resource i2c_resource3[] = {
        },
 };
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 static struct resource i2c_resource4[] = {
        [0] = {
                .start  = INT_DVC,
@@ -83,6 +84,34 @@ static struct resource i2c_resource4[] = {
        },
 };
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static struct resource i2c_resource4[] = {
+       [0] = {
+               .start  = INT_I2C4,
+               .end    = INT_I2C4,
+               .flags  = IORESOURCE_IRQ,
+       },
+       [1] = {
+               .start  = TEGRA_I2C4_BASE,
+               .end    = TEGRA_I2C4_BASE + TEGRA_I2C4_SIZE-1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+
+static struct resource i2c_resource5[] = {
+       [0] = {
+               .start  = INT_I2C5,
+               .end    = INT_I2C5,
+               .flags  = IORESOURCE_IRQ,
+       },
+       [1] = {
+               .start  = TEGRA_I2C5_BASE,
+               .end    = TEGRA_I2C5_BASE + TEGRA_I2C5_SIZE-1,
+               .flags  = IORESOURCE_MEM,
+       },
+};
+#endif
+
 static struct tegra_i2c_platform_data tegra_i2c1_platform_data = {
        .bus_clk_rate   = { 400000 },
 };
@@ -139,10 +168,22 @@ struct platform_device tegra_i2c_device4 = {
        },
 };
 
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+struct platform_device tegra_i2c_device5 = {
+       .name           = "tegra-i2c",
+       .id             = 4,
+       .resource       = i2c_resource5,
+       .num_resources  = ARRAY_SIZE(i2c_resource5),
+       .dev = {
+               .platform_data = 0,
+       },
+};
+#endif
+
 static struct resource spi_resource1[] = {
        [0] = {
-               .start  = INT_S_LINK1,
-               .end    = INT_S_LINK1,
+               .start  = INT_SPI_1,
+               .end    = INT_SPI_1,
                .flags  = IORESOURCE_IRQ,
        },
        [1] = {
@@ -425,6 +466,18 @@ static struct resource tegra_pmu_resources[] = {
                .end    = INT_CPU1_PMU_INTR,
                .flags  = IORESOURCE_IRQ,
        },
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       [2] = {
+               .start  = INT_CPU2_PMU_INTR,
+               .end    = INT_CPU2_PMU_INTR,
+               .flags  = IORESOURCE_IRQ,
+       },
+       [3] = {
+               .start  = INT_CPU3_PMU_INTR,
+               .end    = INT_CPU3_PMU_INTR,
+               .flags  = IORESOURCE_IRQ,
+       },
+#endif
 };
 
 struct platform_device tegra_pmu_device = {
@@ -549,6 +602,7 @@ struct platform_device tegra_uarte_device = {
        },
 };
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 static struct resource i2s_resource1[] = {
        [0] = {
                .start  = INT_I2S1,
@@ -567,6 +621,13 @@ static struct resource i2s_resource1[] = {
        }
 };
 
+struct platform_device tegra_i2s_device1 = {
+       .name           = "tegra-i2s",
+       .id             = 0,
+       .resource       = i2s_resource1,
+       .num_resources  = ARRAY_SIZE(i2s_resource1),
+};
+
 static struct resource i2s_resource2[] = {
        [0] = {
                .start  = INT_I2S2,
@@ -585,13 +646,6 @@ static struct resource i2s_resource2[] = {
        }
 };
 
-struct platform_device tegra_i2s_device1 = {
-       .name           = "tegra-i2s",
-       .id             = 0,
-       .resource       = i2s_resource1,
-       .num_resources  = ARRAY_SIZE(i2s_resource1),
-};
-
 struct platform_device tegra_i2s_device2 = {
        .name           = "tegra-i2s",
        .id             = 1,
@@ -619,6 +673,62 @@ struct platform_device tegra_pcm_device = {
        .id = -1,
 };
 
+
+static struct resource spdif_resource[] = {
+       [0] = {
+               .start  = INT_SPDIF,
+               .end    = INT_SPDIF,
+               .flags  = IORESOURCE_IRQ
+       },
+       [1] = {
+               .start  = TEGRA_DMA_REQ_SEL_SPD_I,
+               .end    = TEGRA_DMA_REQ_SEL_SPD_I,
+               .flags  = IORESOURCE_DMA
+       },
+       [2] = {
+               .start  = TEGRA_SPDIF_BASE,
+               .end    = TEGRA_SPDIF_BASE + TEGRA_SPDIF_SIZE - 1,
+               .flags  = IORESOURCE_MEM
+       }
+};
+
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+static struct resource audio_resource[] = {
+       [0] = {
+               .start  = TEGRA_AUDIO_CLUSTER_BASE,
+               .end    = TEGRA_AUDIO_CLUSTER_BASE + TEGRA_AUDIO_CLUSTER_SIZE - 1,
+               .flags  = IORESOURCE_MEM
+       }
+};
+
+struct platform_device tegra_audio_device = {
+       .name           = "audio",
+       .id             = -1,
+       .resource       = audio_resource,
+       .num_resources  = ARRAY_SIZE(audio_resource),
+};
+
+static struct resource hda_resource[] = {
+       [0] = {
+               .start  = INT_HDA,
+               .end    = INT_HDA,
+               .flags  = IORESOURCE_IRQ
+       },
+       [1] = {
+               .start  = TEGRA_HDA_BASE,
+               .end    = TEGRA_HDA_BASE + TEGRA_HDA_SIZE - 1,
+               .flags  = IORESOURCE_MEM
+       }
+};
+
+struct platform_device tegra_hda_device = {
+       .name           = "hda",
+       .id             = -1,
+       .resource       = hda_resource,
+       .num_resources  = ARRAY_SIZE(hda_resource),
+};
+#endif
+
 static struct resource w1_resources[] = {
        [0] = {
                .start = INT_OWR,
@@ -691,24 +801,6 @@ struct platform_device tegra_otg_device = {
        .num_resources  = ARRAY_SIZE(tegra_otg_resources),
 };
 
-static struct resource spdif_resource[] = {
-       [0] = {
-               .start  = INT_SPDIF,
-               .end    = INT_SPDIF,
-               .flags  = IORESOURCE_IRQ
-       },
-       [1] = {
-               .start  = TEGRA_DMA_REQ_SEL_SPD_I,
-               .end    = TEGRA_DMA_REQ_SEL_SPD_I,
-               .flags  = IORESOURCE_DMA
-       },
-       [2] = {
-               .start  = TEGRA_SPDIF_BASE,
-               .end    = TEGRA_SPDIF_BASE + TEGRA_SPDIF_SIZE - 1,
-               .flags  = IORESOURCE_MEM
-       }
-};
-
 struct platform_device tegra_spdif_device = {
        .name           = "spdif_out",
        .id             = -1,
@@ -716,6 +808,7 @@ struct platform_device tegra_spdif_device = {
        .num_resources  = ARRAY_SIZE(spdif_resource),
 };
 
+#if defined(CONFIG_TEGRA_IOVMM_GART)
 static struct resource tegra_gart_resources[] = {
        [0] = {
                .name   = "mc",
@@ -737,6 +830,31 @@ struct platform_device tegra_gart_device = {
        .num_resources  = ARRAY_SIZE(tegra_gart_resources),
        .resource       = tegra_gart_resources
 };
+#endif
+
+#if defined(CONFIG_TEGRA_IOVMM_SMMU)
+static struct resource tegra_smmu_resources[] = {
+       [0] = {
+               .name   = "mc",
+               .flags  = IORESOURCE_MEM,
+               .start  = TEGRA_MC_BASE,
+               .end    = TEGRA_MC_BASE + TEGRA_MC_SIZE - 1,
+       },
+       [1] = {
+               .name   = "smmu",
+               .flags  = IORESOURCE_MEM,
+               .start  = TEGRA_SMMU_BASE,
+               .end    = TEGRA_SMMU_BASE + TEGRA_SMMU_SIZE - 1,
+       }
+};
+
+struct platform_device tegra_smmu_device = {
+       .name           = "tegra_smmu",
+       .id             = -1,
+       .num_resources  = ARRAY_SIZE(tegra_smmu_resources),
+       .resource       = tegra_smmu_resources
+};
+#endif
 
 static struct resource tegra_wdt_resources[] = {
        [0] = {
index be76fe1..7118f99 100644 (file)
@@ -29,6 +29,9 @@ extern struct platform_device tegra_i2c_device1;
 extern struct platform_device tegra_i2c_device2;
 extern struct platform_device tegra_i2c_device3;
 extern struct platform_device tegra_i2c_device4;
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+extern struct platform_device tegra_i2c_device5;
+#endif
 extern struct platform_device tegra_spi_device1;
 extern struct platform_device tegra_spi_device2;
 extern struct platform_device tegra_spi_device3;
@@ -53,7 +56,22 @@ extern struct platform_device tegra_ehci2_device;
 extern struct platform_device tegra_ehci3_device;
 extern struct platform_device tegra_i2s_device1;
 extern struct platform_device tegra_i2s_device2;
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+extern struct platform_device tegra_i2s_device0;
+extern struct platform_device tegra_i2s_device3;
+extern struct platform_device tegra_i2s_device4;
+extern struct platform_device tegra_apbif0_device;
+extern struct platform_device tegra_apbif1_device;
+extern struct platform_device tegra_apbif2_device;
+extern struct platform_device tegra_apbif3_device;
+extern struct platform_device tegra_hda_device;
+extern struct platform_device tegra_ahub_device;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 extern struct platform_device tegra_gart_device;
+#else
+extern struct platform_device tegra_smmu_device;
+#endif
 extern struct platform_device tegra_wdt_device;
 extern struct platform_device tegra_pwfm0_device;
 extern struct platform_device tegra_pwfm1_device;
index eb1c7ca..e9c2846 100644 (file)
@@ -96,7 +96,7 @@
 #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_CH_NR                 16      /* !!!FIXME!!! T30 has 32 channels .............. */
 #define TEGRA_SYSTEM_DMA_AVP_CH_NUM            4
 #define TEGRA_SYSTEM_DMA_CH_MIN                        0
 #define TEGRA_SYSTEM_DMA_CH_MAX        \
@@ -887,7 +887,12 @@ int __init tegra_dma_init(void)
                spin_lock_init(&ch->lock);
                INIT_LIST_HEAD(&ch->list);
 
-               irq = INT_APB_DMA_CH0 + i;
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+               if (i >= 16)
+                       irq = INT_APB_DMA_CH16 + i - 16;
+               else
+#endif
+                       irq = INT_APB_DMA_CH0 + i;
                ret = request_irq(irq, dma_isr, 0, dma_channels[i].name, ch);
                if (ret) {
                        pr_err("Failed to register IRQ %d for DMA %d\n",
index a785a2e..6fbcad6 100644 (file)
@@ -82,7 +82,7 @@ struct dvfs {
        struct list_head reg_node;
 };
 
-void tegra2_init_dvfs(void);
+void tegra_soc_init_dvfs(void);
 int tegra_enable_dvfs_on_clk(struct clk *c, struct dvfs *d);
 int dvfs_debugfs_init(struct dentry *clk_debugfs_root);
 int tegra_dvfs_late_init(void);
index 1aa393c..dad92e4 100644 (file)
 #include "fuse.h"
 #include "apbio.h"
 
+#define FUSE_SKU_INFO          0x110
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #define FUSE_UID_LOW           0x108
 #define FUSE_UID_HIGH          0x10c
-#define FUSE_SKU_INFO          0x110
 #define FUSE_SPARE_BIT         0x200
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#define FUSE_VENDOR_CODE       0x200
+#define FUSE_VENDOR_CODE_MASK  0xf
+#define FUSE_FAB_CODE          0x204
+#define FUSE_FAB_CODE_MASK     0x3f
+#define FUSE_LOT_CODE_0                0x208
+#define FUSE_LOT_CODE_1                0x20c
+#define FUSE_WAFER_ID          0x210
+#define FUSE_WAFER_ID_MASK     0x3f
+#define FUSE_X_COORDINATE      0x214
+#define FUSE_X_COORDINATE_MASK 0x1ff
+#define FUSE_Y_COORDINATE      0x218
+#define FUSE_Y_COORDINATE_MASK 0x1ff
+#define FUSE_SPARE_BIT         0x244
+#endif
 
 static const char *tegra_revision_name[TEGRA_REVISION_MAX] = {
        [TEGRA_REVISION_UNKNOWN] = "unknown",
@@ -67,11 +83,95 @@ void tegra_init_fuse(void)
 
 unsigned long long tegra_chip_uid(void)
 {
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
        unsigned long long lo, hi;
 
        lo = tegra_fuse_readl(FUSE_UID_LOW);
        hi = tegra_fuse_readl(FUSE_UID_HIGH);
        return (hi << 32ull) | lo;
+#else
+       u64 uid = 0ull;
+#if 0 // !!!FIXME!!! FOR SOME REASON THIS IS GENERATING BAD CODE .......................................
+       u32 reg;
+       u32 cid;
+       u32 vendor;
+       u32 fab;
+       u32 lot;
+       u32 wafer;
+       u32 x;
+       u32 y;
+       u32 i;
+
+       /* This used to be so much easier in prior chips. Unfortunately, there
+          is no one-stop shopping for the unique id anymore. It must be
+          constructed from various bits of information burned into the fuses
+          during the manufacturing process. The 64-bit unique id is formed
+          by concatenating several bit fields. The notation used for the
+          various fields is <fieldname:size_in_bits> with the UID composed
+          thusly:
+
+            <CID:4><VENDOR:4><FAB:6><LOT:26><WAFER:6><X:9><Y:9>
+
+          Where:
+
+               Field    Bits  Position Data
+               -------  ----  -------- ----------------------------------------
+               CID        4     60     Chip id (encoded as zero for T30)
+               VENDOR     4     56     Vendor code
+               FAB        6     50     FAB code
+               LOT       26     24     Lot code (5-digit base-36-coded-decimal,
+                                           re-encoded to 26 bits binary)
+               WAFER      6     18     Wafer id
+               X          9      9     Wafer X-coordinate
+               Y          9      0     Wafer Y-coordinate
+               -------  ----
+               Total     64
+       */
+
+       /* Get the chip id and encode each chip variant as a unique value. */
+       reg = readl(IO_TO_VIRT(TEGRA_APB_MISC_BASE + 0x804));
+       reg = (reg >> 8) && 0xFF;
+
+       switch (reg) {
+       case 0x30:
+               cid = 0;
+               break;
+
+       default:
+               BUG();
+               break;
+       }
+
+       vendor = fuse_readl(FUSE_VENDOR_CODE) & FUSE_VENDOR_CODE_MASK;
+       fab = fuse_readl(FUSE_FAB_CODE) & FUSE_FAB_CODE_MASK;
+
+       /* Lot code must be re-encoded from a 5 digit base-36 'BCD' number
+          to a binary number. */
+       lot = 0;
+       reg = fuse_readl(FUSE_LOT_CODE_1) << 2;
+
+       for (i = 0; i < 5; ++i) {
+               u32 digit = (reg & 0xFC000000) >> 26;
+               BUG_ON(digit >= 36);
+               lot *= 36;
+               lot += digit;
+               reg <<= 6;
+       }
+
+       wafer = fuse_readl(FUSE_WAFER_ID) & FUSE_WAFER_ID_MASK;
+       x = fuse_readl(FUSE_X_COORDINATE) & FUSE_X_COORDINATE_MASK;
+       y = fuse_readl(FUSE_Y_COORDINATE) & FUSE_Y_COORDINATE_MASK;
+
+       uid = ((unsigned long long)cid  << 60ull)
+           | ((unsigned long long)vendor << 56ull)
+           | ((unsigned long long)fab << 50ull)
+           | ((unsigned long long)lot << 24ull)
+           | ((unsigned long long)wafer << 18ull)
+           | ((unsigned long long)x << 9ull)
+           | ((unsigned long long)y << 0ull);
+#endif
+       return uid;
+#endif
 }
 
 unsigned int tegra_spare_fuse(int bit)
index f28220a..8441b21 100644 (file)
 #define TEGRA_GPIO_PBB5                221
 #define TEGRA_GPIO_PBB6                222
 #define TEGRA_GPIO_PBB7                223
-
+#define TEGRA_GPIO_PCC0                224
+#define TEGRA_GPIO_PCC1                225
+#define TEGRA_GPIO_PCC2                226
+#define TEGRA_GPIO_PCC3                227
+#define TEGRA_GPIO_PCC4                228
+#define TEGRA_GPIO_PCC5                229
+#define TEGRA_GPIO_PCC6                230
+#define TEGRA_GPIO_PCC7                231
+#define TEGRA_GPIO_PDD0                232
+#define TEGRA_GPIO_PDD1                233
+#define TEGRA_GPIO_PDD2                234
+#define TEGRA_GPIO_PDD3                235
+#define TEGRA_GPIO_PDD4                236
+#define TEGRA_GPIO_PDD5                237
+#define TEGRA_GPIO_PDD6                238
+#define TEGRA_GPIO_PDD7                239
+#define TEGRA_GPIO_PEE0                240
+#define TEGRA_GPIO_PEE1                241
+#define TEGRA_GPIO_PEE2                242
 #endif
diff --git a/arch/arm/mach-tegra/headsmp-t3.S b/arch/arm/mach-tegra/headsmp-t3.S
new file mode 100644 (file)
index 0000000..5ee115f
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * arch/arm/mach-tegra/headsmp-t2.S
+ *
+ * SMP initialization routines for Tegra3 SoCs
+ *
+ * Copyright (c) 2009-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/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "power-macros.S"
+
+
+#define DEBUG_HOTPLUG_STARTUP  0       /* Nonzero for hotplug startup debug */
+#define DEBUG_LP2_STARTUP      0       /* Nonzero for LP2 startup debug */
+
+
+#ifdef CONFIG_HOTPLUG_CPU
+/*
+ *     tegra_hotplug_startup
+ *
+ *       Secondary CPU boot vector when restarting a CPU following a
+ *       hot-unplug. Uses the page table created by smp_prepare_cpus and
+ *       stored in tegra_pgd_phys as the safe page table for
+ *       __return_to_virtual, and jumps directly to __cortex_a9_restore.
+ */
+       .align L1_CACHE_SHIFT
+ENTRY(tegra_hotplug_startup)
+#if    DEBUG_HOTPLUG_STARTUP
+       b       .
+#endif
+       setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9
+       bl      __invalidate_cpu_state
+       enable_coresite r1
+       cpu_id  r0
+       subs    r1, r0, #1
+#ifdef DEBUG
+       /* !!!CHECKME!!! THIS MAY NOW BE OBSOLETE */
+       bmi     .                       @ should never come here for CPU0
+#endif
+       mov     r3, r1, lsl #3
+       add     r3, r3, #0x18           @ CPUn CSR offset, n>0
+       mov32   r2, TEGRA_FLOW_CTRL_BASE
+
+       @ Clear the flow controller flags for this CPU.
+       ldr     r1, [r2, r3]
+       orr     r1, r1, #(1 << 15) | (1 << 14)  @ write to clear event & intr
+       movw    r0, #0x0FFD     @ enable, cluster_switch, immed, & bitmaps
+       bic     r1, r1, r0
+       str     r1, [r2, r3]
+
+       /* most of the below is a retread of what happens in __v7_setup and
+        * secondary_startup, to get the MMU re-enabled and to branch
+        * to secondary_kernel_startup */
+       mrc     p15, 0, r0, c1, c0, 1
+       orr     r0, r0, #(1 << 6) | (1 << 0)    @ re-enable coherency
+       mcr     p15, 0, r0, c1, c0, 1
+
+       adr     r4, __tegra_hotplug_data
+       ldmia   r4, {r5, r7, r12}
+       mov     r1, r12                 @ ctx_restore = __cortex_a9_restore
+       sub     r4, r4, r5
+       ldr     r0, [r7, r4]            @ pgdir = secondary_data.pgdir
+       b       __return_to_virtual
+ENDPROC(tegra_hotplug_startup)
+
+
+       .type   __tegra_hotplug_data, %object
+__tegra_hotplug_data:
+       .long   .
+       .long   tegra_pgd_phys
+       .long   __cortex_a9_restore
+       .size   __tegra_hotplug_data, . - __tegra_hotplug_data
+#endif
index 1ddfb28..34e521b 100644 (file)
@@ -68,6 +68,16 @@ ENTRY(tegra_resume)
        bl      tegra_invalidate_l1
        bl      tegra_enable_coresite
 
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+       @ Clear the flow controller flags for this CPU.
+       mov32   r2, TEGRA_FLOW_CTRL_BASE+8      @ CPU0 CSR
+       ldr     r1, [r2]
+       orr     r1, r1, #(1 << 15) | (1 << 14)  @ write to clear event & intr
+       movw    r0, #0x0FFD     @ enable, cluster_switch, immed, & bitmaps
+       bic     r1, r1, r0
+       str     r1, [r2]
+#endif
+
        /* enable SCU */
        ldr     r0, =TEGRA_ARM_PERIF_BASE
        ldr     r1, [r0]
index 97d9746..d54b8f9 100644 (file)
@@ -17,6 +17,8 @@
 
 #include "sleep.h"
 
+#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
+
 #define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x4c)
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
 
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+/* For Tegra2 use the software-written value of the reset register for status.*/
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET
+#else
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
+#endif
+
 int platform_cpu_kill(unsigned int cpu)
 {
        unsigned int reg;
 
        do {
-               reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+               reg = readl(CLK_RST_CONTROLLER_CPU_CMPLX_STATUS);
                cpu_relax();
        } while (!(reg & (1<<cpu)));
 
        reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-       writel(reg | (1<<(8+cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+       writel(reg | CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
 
        return 1;
 }
index cb0248c..a1a73d9 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * arch/arm/mach-tegra/include/mach/dma.h
  *
- * Copyright (c) 2008-2009, NVIDIA Corporation.
+ * Copyright (c) 2008-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
@@ -30,9 +30,13 @@ struct tegra_dma_channel;
 
 #define TEGRA_DMA_REQ_SEL_CNTR                 0
 #define TEGRA_DMA_REQ_SEL_I2S_2                        1
+#define TEGRA_DMA_REQ_SEL_APBIF_CH0            TEGRA_DMA_REQ_SEL_I2S_2
 #define TEGRA_DMA_REQ_SEL_I2S_1                        2
+#define TEGRA_DMA_REQ_SEL_APBIF_CH1            TEGRA_DMA_REQ_SEL_I2S_1
 #define TEGRA_DMA_REQ_SEL_SPD_I                        3
+#define TEGRA_DMA_REQ_SEL_APBIF_CH2            TEGRA_DMA_REQ_SEL_SPD_I
 #define TEGRA_DMA_REQ_SEL_UI_I                 4
+#define TEGRA_DMA_REQ_SEL_APBIF_CH3            TEGRA_DMA_REQ_SEL_UI_I
 #define TEGRA_DMA_REQ_SEL_MIPI                 5
 #define TEGRA_DMA_REQ_SEL_I2S2_2               6
 #define TEGRA_DMA_REQ_SEL_I2S2_1               7
@@ -40,6 +44,7 @@ struct tegra_dma_channel;
 #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_DTV                  TEGRA_DMA_REQ_SEL_SPI
 #define TEGRA_DMA_REQ_SEL_AC97                 12
 #define TEGRA_DMA_REQ_SEL_ACMODEM              13
 #define TEGRA_DMA_REQ_SEL_SL4B                 14
@@ -54,6 +59,10 @@ struct tegra_dma_channel;
 #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_OWR                  25
+#define TEGRA_DMA_REQ_SEL_I2C4                 26
+#define TEGRA_DMA_REQ_SEL_SL2B5                        27
+#define TEGRA_DMA_REQ_SEL_SL2B6                        28
 #define TEGRA_DMA_REQ_SEL_INVALID              31
 
 #define TEGRA_DMA_MAX_TRANSFER_SIZE            0x10000
index d10a033..5680dab 100644 (file)
 #define TEGRA_DSI_BASE                 0x54300000
 #define TEGRA_DSI_SIZE                 SZ_256K
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
 #define TEGRA_GART_BASE                        0x58000000
 #define TEGRA_GART_SIZE                        SZ_32M
 
-#define TEGRA_RES_SEMA_BASE            0x60001000
+#endif
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_SMMU_BASE                        0xe0000000
+#define TEGRA_SMMU_SIZE                        SZ_256M
+
+#endif
+
 #define TEGRA_RES_SEMA_SIZE            SZ_4K
+#define TEGRA_RES_SEMA_BASE            0x60001000
 
 #define TEGRA_ARB_SEMA_BASE            0x60002000
 #define TEGRA_ARB_SEMA_SIZE            SZ_4K
 #define TEGRA_QUATERNARY_ICTLR_BASE    0x60004300
 #define TEGRA_QUATERNARY_ICTLR_SIZE    64
 
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_QUINARY_ICTLR_BASE       0x60004400
+#define TEGRA_QUINARY_ICTLR_SIZE       SZ_64
+
+#endif
+
 #define TEGRA_TMR1_BASE                        0x60005000
 #define TEGRA_TMR1_SIZE                        SZ_8
 
 #define TEGRA_TMR4_BASE                        0x60005058
 #define TEGRA_TMR4_SIZE                        SZ_8
 
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_TMR5_BASE                        0x60005060
+#define TEGRA_TMR5_SIZE                        SZ_8
+
+#define TEGRA_TMR6_BASE                        0x60005068
+#define TEGRA_TMR6_SIZE                        SZ_8
+
+#define TEGRA_TMR7_BASE                        0x60005070
+#define TEGRA_TMR7_SIZE                        SZ_8
+
+#define TEGRA_TMR8_BASE                        0x60005078
+#define TEGRA_TMR8_SIZE                        SZ_8
+
+#define TEGRA_TMR9_BASE                        0x60005080
+#define TEGRA_TMR9_SIZE                        SZ_8
+
+#define TEGRA_TMR10_BASE               0x60005088
+#define TEGRA_TMR10_SIZE               SZ_8
+
+#endif
+
 #define TEGRA_CLK_RESET_BASE           0x60006000
 #define TEGRA_CLK_RESET_SIZE           SZ_4K
 
 #define TEGRA_APB_MISC_DAS_BASE                0x70000c00
 #define TEGRA_APB_MISC_DAS_SIZE                SZ_128
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
 #define TEGRA_AC97_BASE                        0x70002000
 #define TEGRA_AC97_SIZE                        SZ_512
 
 #define TEGRA_I2S2_BASE                        0x70002A00
 #define TEGRA_I2S2_SIZE                        SZ_256
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_HDA_BASE                 0x70030000
+#define TEGRA_HDA_SIZE                 SZ_64K
+
+#define TEGRA_AUDIO_CLUSTER_BASE       0x70080000
+#define TEGRA_AUDIO_CLUSTER_SIZE       SZ_4K
+
+#define TEGRA_APBIF0_BASE              TEGRA_AUDIO_CLUSTER_BASE
+#define TEGRA_APBIF0_SIZE              32
+
+#define TEGRA_APBIF1_BASE              0x70080020
+#define TEGRA_APBIF1_SIZE              32
+
+#define TEGRA_APBIF2_BASE              0x70080040
+#define TEGRA_APBIF2_SIZE              32
+
+#define TEGRA_APBIF3_BASE              0x70080060
+#define TEGRA_APBIF3_SIZE              32
+
+#define TEGRA_AHUB_BASE                        0x70080200
+#define TEGRA_AHUB_SIZE                        SZ_256
+
+#define TEGRA_I2S0_BASE                        0x70080300
+#define TEGRA_I2S0_SIZE                        SZ_256
+
+#define TEGRA_I2S1_BASE                        0x70080400
+#define TEGRA_I2S1_SIZE                        SZ_256
+
+#define TEGRA_I2S2_BASE                        0x70080500
+#define TEGRA_I2S2_SIZE                        SZ_256
+
+#define TEGRA_I2S3_BASE                        0x70080600
+#define TEGRA_I2S3_SIZE                        SZ_256
+
+#define TEGRA_I2S4_BASE                        0x70080700
+#define TEGRA_I2S4_SIZE                        SZ_256
+
+#define TEGRA_DAM0_BASE                        0x70080800
+#define TEGRA_DAM0_SIZE                        SZ_256
+
+#define TEGRA_DAM1_BASE                        0x70080900
+#define TEGRA_DAM1_SIZE                        SZ_256
+
+#define TEGRA_DAM2_BASE                        0x70080A00
+#define TEGRA_DAM2_SIZE                        SZ_256
+
+#define TEGRA_SPDIF_BASE               0x70080B00
+#define TEGRA_SPDIF_SIZE               SZ_256
+
+#endif
+
 #define TEGRA_UARTA_BASE               0x70006000
 #define TEGRA_UARTA_SIZE               64
 
 #define TEGRA_TWC_BASE                 0x7000C100
 #define TEGRA_TWC_SIZE                 SZ_256
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
 #define TEGRA_SPI_BASE                 0x7000C380
 #define TEGRA_SPI_SIZE                 48
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_DTV_BASE                 0x7000C300
+#define TEGRA_DTV_SIZE                 SZ_256
+
+#endif
+
 #define TEGRA_I2C2_BASE                        0x7000C400
 #define TEGRA_I2C2_SIZE                        SZ_256
 
 #define TEGRA_OWR_BASE                 0x7000C600
 #define TEGRA_OWR_SIZE                 80
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
 #define TEGRA_DVC_BASE                 0x7000D000
 #define TEGRA_DVC_SIZE                 SZ_512
 
+#else
+
+#define TEGRA_I2C4_BASE                        0x7000C700
+#define TEGRA_I2C4_SIZE                        SZ_512
+
+#define TEGRA_I2C5_BASE                        0x7000D000
+#define TEGRA_I2C5_SIZE                        SZ_512
+
+#endif
+
 #define TEGRA_SPI1_BASE                        0x7000D400
 #define TEGRA_SPI1_SIZE                        SZ_512
 
 #define TEGRA_SPI4_BASE                        0x7000DA00
 #define TEGRA_SPI4_SIZE                        SZ_512
 
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_SPI5_BASE                        0x7000DC00
+#define TEGRA_SPI5_SIZE                        SZ_512
+
+#define TEGRA_SPI6_BASE                        0x7000DE00
+#define TEGRA_SPI6_SIZE                        SZ_512
+
+#endif
+
 #define TEGRA_RTC_BASE                 0x7000E000
 #define TEGRA_RTC_SIZE                 SZ_256
 
 #define TEGRA_CSITE_BASE               0x70040000
 #define TEGRA_CSITE_SIZE               SZ_256K
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+
 #define TEGRA_USB_BASE                 0xC5000000
 #define TEGRA_USB_SIZE                 SZ_16K
 
 #define TEGRA_SDMMC4_BASE              0xC8000600
 #define TEGRA_SDMMC4_SIZE              SZ_512
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+#define TEGRA_SATA_BASE                        0x70020000
+#define TEGRA_SATA_SIZE                        SZ_64K
+
+#define TEGRA_SATA_CONFIG_BASE         0x70021000
+#define TEGRA_SATA_CONFIG_SIZE         SZ_4K
+
+#define TEGRA_SATA_BAR5_BASE           0x70027000
+#define TEGRA_SATA_BAR5_SIZE           SZ_8K
+
+#define TEGRA_SDMMC1_BASE              0x78000000
+#define TEGRA_SDMMC1_SIZE              SZ_512
+
+#define TEGRA_SDMMC2_BASE              0x78000200
+#define TEGRA_SDMMC2_SIZE              SZ_512
+
+#define TEGRA_SDMMC3_BASE              0x78000400
+#define TEGRA_SDMMC3_SIZE              SZ_512
+
+#define TEGRA_SDMMC4_BASE              0x78000600
+#define TEGRA_SDMMC4_SIZE              SZ_512
+
+#define TEGRA_USB_BASE                 0x7D000000
+#define TEGRA_USB_SIZE                 SZ_16K
+
+#define TEGRA_USB2_BASE                        0x7D004000
+#define TEGRA_USB2_SIZE                        SZ_16K
+
+#define TEGRA_USB3_BASE                        0x7D008000
+#define TEGRA_USB3_SIZE                        SZ_16K
+
+#endif
+
 #if defined(CONFIG_TEGRA_DEBUG_UART_NONE)
 # define TEGRA_DEBUG_UART_BASE 0
 #elif defined(CONFIG_TEGRA_DEBUG_UARTA)
index 98c9888..77ec387 100644 (file)
@@ -20,6 +20,7 @@
 
 #include <linux/list.h>
 #include <linux/platform_device.h>
+#include <linux/miscdevice.h>
 #include <linux/rbtree.h>
 #include <linux/rwsem.h>
 #include <linux/spinlock.h>
@@ -28,7 +29,7 @@
 #ifndef _MACH_TEGRA_IOVMM_H_
 #define _MACH_TEGRA_IOVMM_H_
 
-#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC) || defined(CONFIG_ARCH_TEGRA_3x_SOC)
 typedef u32 tegra_iovmm_addr_t;
 #else
 #error "Unsupported tegra architecture family"
@@ -73,6 +74,7 @@ struct tegra_iovmm_client {
        unsigned long                   flags;
        struct iovmm_share_group        *group;
        struct tegra_iovmm_domain       *domain;
+       struct miscdevice               *misc_dev;
        struct list_head                list;
 };
 
@@ -90,28 +92,28 @@ struct tegra_iovmm_area {
 
 struct tegra_iovmm_device_ops {
        /* maps a VMA using the page residency functions provided by the VMA */
-       int (*map)(struct tegra_iovmm_device *dev,
+       int (*map)(struct tegra_iovmm_domain *domain,
                struct tegra_iovmm_area *io_vma);
        /* marks all PTEs in a VMA as invalid; decommits the virtual addres
         * space (potentially freeing PDEs when decommit is true.) */
-       void (*unmap)(struct tegra_iovmm_device *dev,
+       void (*unmap)(struct tegra_iovmm_domain *domain,
                struct tegra_iovmm_area *io_vma, bool decommit);
-       void (*map_pfn)(struct tegra_iovmm_device *dev,
+       void (*map_pfn)(struct tegra_iovmm_domain *domain,
                struct tegra_iovmm_area *io_vma,
                tegra_iovmm_addr_t offs, unsigned long pfn);
        /* ensures that a domain is resident in the hardware's mapping region
         * so that it may be used by a client */
-       int (*lock_domain)(struct tegra_iovmm_device *dev,
-               struct tegra_iovmm_domain *domain);
-       void (*unlock_domain)(struct tegra_iovmm_device *dev,
-               struct tegra_iovmm_domain *domain);
+       int (*lock_domain)(struct tegra_iovmm_domain *domain,
+               struct tegra_iovmm_client *client);
+       void (*unlock_domain)(struct tegra_iovmm_domain *domain,
+               struct tegra_iovmm_client *client);
        /* allocates a vmm_domain for the specified client; may return the same
         * domain for multiple clients */
        struct tegra_iovmm_domain* (*alloc_domain)(
                struct tegra_iovmm_device *dev,
                struct tegra_iovmm_client *client);
-       void (*free_domain)(struct tegra_iovmm_device *dev,
-               struct tegra_iovmm_domain *domain);
+       void (*free_domain)(struct tegra_iovmm_domain *domain,
+               struct tegra_iovmm_client *client);
        int (*suspend)(struct tegra_iovmm_device *dev);
        void (*resume)(struct tegra_iovmm_device *dev);
 };
@@ -131,7 +133,7 @@ struct tegra_iovmm_area_ops {
 /* called by clients to allocate an I/O VMM client mapping context which
  * will be shared by all clients in the same share_group */
 struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
-       const char *share_group);
+       const char *share_group, struct miscdevice *misc_dev);
 
 size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client);
 
@@ -195,7 +197,7 @@ int tegra_iovmm_unregister(struct tegra_iovmm_device *dev);
 #else /* CONFIG_TEGRA_IOVMM */
 
 static inline struct tegra_iovmm_client *tegra_iovmm_alloc_client(
-       const char *name, const char *share_group)
+       const char *name, const char *share_group, struct miscdevice *misc_dev)
 {
        return NULL;
 }
index 2352c2c..58e07d7 100644 (file)
@@ -91,6 +91,7 @@
 #define INT_CPU1_PMU_INTR              (INT_SEC_BASE + 25)
 #define INT_SEC_RES_26                 (INT_SEC_BASE + 26)
 #define INT_S_LINK1                    (INT_SEC_BASE + 27)
+#define INT_SPI_1                      INT_S_LINK1
 #define INT_APB_DMA_COP                        (INT_SEC_BASE + 28)
 #define INT_AHB_DMA_COP                        (INT_SEC_BASE + 29)
 #define INT_DMA_TX                     (INT_SEC_BASE + 30)
                                         INT_SYNCPT_THRESH_NR)
 #define INT_GPIO_NR                    (28 * 8)
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+/* Primary Interrupt Controller */
+#define INT_PRI_BASE                   (INT_GIC_BASE + 32)
+#define INT_TMR1                       (INT_PRI_BASE + 0)
+#define INT_TMR2                       (INT_PRI_BASE + 1)
+#define INT_RTC                                (INT_PRI_BASE + 2)
+#define INT_CEC                                (INT_PRI_BASE + 3)
+#define INT_SHR_SEM_INBOX_IBF          (INT_PRI_BASE + 4)
+#define INT_SHR_SEM_INBOX_IBE          (INT_PRI_BASE + 5)
+#define INT_SHR_SEM_OUTBOX_IBF         (INT_PRI_BASE + 6)
+#define INT_SHR_SEM_OUTBOX_IBE         (INT_PRI_BASE + 7)
+#define INT_VDE_UCQ_ERROR              (INT_PRI_BASE + 8)
+#define INT_VDE_SYNC_TOKEN             (INT_PRI_BASE + 9)
+#define INT_VDE_BSE_V                  (INT_PRI_BASE + 10)
+#define INT_VDE_BSE_A                  (INT_PRI_BASE + 11)
+#define INT_VDE_SXE                    (INT_PRI_BASE + 12)
+#define INT_SATA_RX_STAT               (INT_PRI_BASE + 13)
+#define INT_SDMMC1                     (INT_PRI_BASE + 14)
+#define INT_SDMMC2                     (INT_PRI_BASE + 15)
+#define INT_XIO                                (INT_PRI_BASE + 16)
+#define INT_VDE                                (INT_PRI_BASE + 17)
+#define INT_AVP_UCQ                    (INT_PRI_BASE + 18)
+#define INT_SDMMC3                     (INT_PRI_BASE + 19)
+#define INT_USB                                (INT_PRI_BASE + 20)
+#define INT_USB2                       (INT_PRI_BASE + 21)
+#define INT_HSMMC                      (INT_PRI_BASE + 22)
+#define INT_SATA_CTL                   (INT_PRI_BASE + 23)
+#define INT_NANDFLASH                  (INT_PRI_BASE + 24)
+#define INT_VCP                                (INT_PRI_BASE + 25)
+#define INT_APB_DMA                    (INT_PRI_BASE + 26)
+#define INT_AHB_DMA                    (INT_PRI_BASE + 27)
+#define INT_GNT_0                      (INT_PRI_BASE + 28)
+#define INT_GNT_1                      (INT_PRI_BASE + 29)
+#define INT_OWR                                (INT_PRI_BASE + 30)
+#define INT_SDMMC4                     (INT_PRI_BASE + 31)
+
+/* Secondary Interrupt Controller */
+#define INT_SEC_BASE                   (INT_PRI_BASE + 32)
+#define INT_GPIO1                      (INT_SEC_BASE + 0)
+#define INT_GPIO2                      (INT_SEC_BASE + 1)
+#define INT_GPIO3                      (INT_SEC_BASE + 2)
+#define INT_GPIO4                      (INT_SEC_BASE + 3)
+#define INT_UARTA                      (INT_SEC_BASE + 4)
+#define INT_UARTB                      (INT_SEC_BASE + 5)
+#define INT_I2C                                (INT_SEC_BASE + 6)
+#define INT_SPI                                (INT_SEC_BASE + 7)
+#define INT_TWC                                (INT_SEC_BASE + 8)
+#define INT_TMR3                       (INT_SEC_BASE + 9)
+#define INT_TMR4                       (INT_SEC_BASE + 10)
+#define INT_FLOW_RSM0                  (INT_SEC_BASE + 11)
+#define INT_FLOW_RSM1                  (INT_SEC_BASE + 12)
+#define INT_ACTMON                     (INT_SEC_BASE + 13)
+#define INT_UARTC                      (INT_SEC_BASE + 14)
+#define INT_MIPI                       (INT_SEC_BASE + 15)
+#define INT_EVENTA                     (INT_SEC_BASE + 16)
+#define INT_EVENTB                     (INT_SEC_BASE + 17)
+#define INT_EVENTC                     (INT_SEC_BASE + 18)
+#define INT_EVENTD                     (INT_SEC_BASE + 19)
+#define INT_VFIR                       (INT_SEC_BASE + 20)
+#define INT_I2C5                       (INT_SEC_BASE + 21)
+#define INT_SYS_STATS_MON              (INT_SEC_BASE + 22)
+#define INT_GPIO5                      (INT_SEC_BASE + 23)
+#define INT_SPEEDO_PMON_0              (INT_SEC_BASE + 24)
+#define INT_SPEEDO_PMON_1              (INT_SEC_BASE + 25)
+#define INT_SE                         (INT_SEC_BASE + 26)
+#define INT_SPI_1                      (INT_SEC_BASE + 27)
+#define INT_APB_DMA_COP                        (INT_SEC_BASE + 28)
+#define INT_AHB_DMA_COP                        (INT_SEC_BASE + 29)
+#define INT_DMA_TX                     (INT_SEC_BASE + 30)
+#define INT_DMA_RX                     (INT_SEC_BASE + 31)
+
+/* Tertiary Interrupt Controller */
+#define INT_TRI_BASE                   (INT_SEC_BASE + 32)
+#define INT_HOST1X_COP_SYNCPT          (INT_TRI_BASE + 0)
+#define INT_HOST1X_MPCORE_SYNCPT       (INT_TRI_BASE + 1)
+#define INT_HOST1X_COP_GENERAL         (INT_TRI_BASE + 2)
+#define INT_HOST1X_MPCORE_GENERAL      (INT_TRI_BASE + 3)
+#define INT_MPE_GENERAL                        (INT_TRI_BASE + 4)
+#define INT_VI_GENERAL                 (INT_TRI_BASE + 5)
+#define INT_EPP_GENERAL                        (INT_TRI_BASE + 6)
+#define INT_ISP_GENERAL                        (INT_TRI_BASE + 7)
+#define INT_2D_GENERAL                 (INT_TRI_BASE + 8)
+#define INT_DISPLAY_GENERAL            (INT_TRI_BASE + 9)
+#define INT_DISPLAY_B_GENERAL          (INT_TRI_BASE + 10)
+#define INT_HDMI                       (INT_TRI_BASE + 11)
+#define INT_TVO_GENERAL                        (INT_TRI_BASE + 12)
+#define INT_MC_GENERAL                 (INT_TRI_BASE + 13)
+#define INT_EMC_GENERAL                        (INT_TRI_BASE + 14)
+#define INT_SPI_6                      (INT_SEC_BASE + 15)
+#define INT_NOR_FLASH                  (INT_TRI_BASE + 16)
+#define INT_HDA                                (INT_TRI_BASE + 17)
+#define INT_SPI_2                      (INT_TRI_BASE + 18)
+#define INT_SPI_3                      (INT_TRI_BASE + 19)
+#define INT_I2C2                       (INT_TRI_BASE + 20)
+#define INT_KBC                                (INT_TRI_BASE + 21)
+#define INT_EXTERNAL_PMU               (INT_TRI_BASE + 22)
+#define INT_GPIO6                      (INT_TRI_BASE + 23)
+#define INT_TVDAC                      (INT_TRI_BASE + 24)
+#define INT_GPIO7                      (INT_TRI_BASE + 25)
+#define INT_UARTD                      (INT_TRI_BASE + 26)
+#define INT_UARTE                      (INT_TRI_BASE + 27)
+#define INT_I2C3                       (INT_TRI_BASE + 28)
+#define INT_SPI_4                      (INT_TRI_BASE + 29)
+#define INT_SPI_5                      (INT_TRI_BASE + 30)
+#define INT_SW_RESERVED                        (INT_TRI_BASE + 31)
+
+/* Quaternary Interrupt Controller */
+#define INT_QUAD_BASE                  (INT_TRI_BASE + 32)
+#define INT_SNOR                       (INT_QUAD_BASE + 0)
+#define INT_USB3                       (INT_QUAD_BASE + 1)
+#define INT_PCIE_INTR                  (INT_QUAD_BASE + 2)
+#define INT_PCIE_MSI                   (INT_QUAD_BASE + 3)
+#define INT_PCIE                       (INT_QUAD_BASE + 4)
+#define INT_AVP_CACHE                  (INT_QUAD_BASE + 5)
+#define INT_TSENSOR                    (INT_QUAD_BASE + 6)
+#define INT_AUDIO_CLUSTER              (INT_QUAD_BASE + 7)
+#define INT_APB_DMA_CH0                        (INT_QUAD_BASE + 8)
+#define INT_APB_DMA_CH1                        (INT_QUAD_BASE + 9)
+#define INT_APB_DMA_CH2                        (INT_QUAD_BASE + 10)
+#define INT_APB_DMA_CH3                        (INT_QUAD_BASE + 11)
+#define INT_APB_DMA_CH4                        (INT_QUAD_BASE + 12)
+#define INT_APB_DMA_CH5                        (INT_QUAD_BASE + 13)
+#define INT_APB_DMA_CH6                        (INT_QUAD_BASE + 14)
+#define INT_APB_DMA_CH7                        (INT_QUAD_BASE + 15)
+#define INT_APB_DMA_CH8                        (INT_QUAD_BASE + 16)
+#define INT_APB_DMA_CH9                        (INT_QUAD_BASE + 17)
+#define INT_APB_DMA_CH10               (INT_QUAD_BASE + 18)
+#define INT_APB_DMA_CH11               (INT_QUAD_BASE + 19)
+#define INT_APB_DMA_CH12               (INT_QUAD_BASE + 20)
+#define INT_APB_DMA_CH13               (INT_QUAD_BASE + 21)
+#define INT_APB_DMA_CH14               (INT_QUAD_BASE + 22)
+#define INT_APB_DMA_CH15               (INT_QUAD_BASE + 23)
+#define INT_I2C4                       (INT_QUAD_BASE + 24)
+#define INT_TMR5                       (INT_QUAD_BASE + 25)
+#define INT_TMR_SHARED                 (INT_QUAD_BASE + 26)
+#define INT_WTD_CPU                    (INT_QUAD_BASE + 27)
+#define INT_WDT_AVP                    (INT_QUAD_BASE + 28)
+#define INT_GPIO8                      (INT_QUAD_BASE + 29)
+#define INT_CAR                                (INT_QUAD_BASE + 30)
+#define INT_QUAD_RES_31                        (INT_QUAD_BASE + 31)
+
+/* Quintary Interrupt Controller */
+#define INT_QUINT_BASE                 (INT_QUAD_BASE + 32)
+#define INT_APB_DMA_CH16               (INT_QUINT_BASE + 0)
+#define INT_APB_DMA_CH17               (INT_QUINT_BASE + 1)
+#define INT_APB_DMA_CH18               (INT_QUINT_BASE + 2)
+#define INT_APB_DMA_CH19               (INT_QUINT_BASE + 3)
+#define INT_APB_DMA_CH20               (INT_QUINT_BASE + 4)
+#define INT_APB_DMA_CH21               (INT_QUINT_BASE + 5)
+#define INT_APB_DMA_CH22               (INT_QUINT_BASE + 6)
+#define INT_APB_DMA_CH23               (INT_QUINT_BASE + 7)
+#define INT_APB_DMA_CH24               (INT_QUINT_BASE + 8)
+#define INT_APB_DMA_CH25               (INT_QUINT_BASE + 9)
+#define INT_APB_DMA_CH26               (INT_QUINT_BASE + 10)
+#define INT_APB_DMA_CH27               (INT_QUINT_BASE + 11)
+#define INT_APB_DMA_CH28               (INT_QUINT_BASE + 12)
+#define INT_APB_DMA_CH29               (INT_QUINT_BASE + 13)
+#define INT_APB_DMA_CH30               (INT_QUINT_BASE + 14)
+#define INT_APB_DMA_CH31               (INT_QUINT_BASE + 15)
+#define INT_CPU0_PMU_INTR              (INT_QUINT_BASE + 16)
+#define INT_CPU1_PMU_INTR              (INT_QUINT_BASE + 17)
+#define INT_CPU2_PMU_INTR              (INT_QUINT_BASE + 18)
+#define INT_CPU3_PMU_INTR              (INT_QUINT_BASE + 19)
+#define INT_CPU4_PMU_INTR              (INT_QUINT_BASE + 20)
+#define INT_CPU5_PMU_INTR              (INT_QUINT_BASE + 21)
+#define INT_CPU6_PMU_INTR              (INT_QUINT_BASE + 22)
+#define INT_CPU7_PMU_INTR              (INT_QUINT_BASE + 23)
+#define INT_QUINT_RES_24               (INT_QUINT_BASE + 24)
+#define INT_QUINT_RES_25               (INT_QUINT_BASE + 25)
+#define INT_QUINT_RES_26               (INT_QUINT_BASE + 26)
+#define INT_QUINT_RES_27               (INT_QUINT_BASE + 27)
+#define INT_QUINT_RES_28               (INT_QUINT_BASE + 28)
+#define INT_QUINT_RES_29               (INT_QUINT_BASE + 29)
+#define INT_QUINT_RES_30               (INT_QUINT_BASE + 30)
+#define INT_QUINT_RES_31               (INT_QUINT_BASE + 31)
+
+#define INT_MAIN_NR                    (INT_QUINT_BASE + 32 - INT_PRI_BASE)
+
+#define INT_SYNCPT_THRESH_BASE         (INT_QUINT_BASE + 32)
+#define INT_SYNCPT_THRESH_NR           32
+
+#define INT_GPIO_BASE                  (INT_SYNCPT_THRESH_BASE + \
+                                        INT_SYNCPT_THRESH_NR)
+#define INT_GPIO_NR                    (32 * 8)
+
+#endif
+
 #define FIQ_START                      INT_GIC_BASE
 
 #define TEGRA_NR_IRQS                  (INT_GPIO_BASE + INT_GPIO_NR)
 
 #define INT_BOARD_BASE                 TEGRA_NR_IRQS
+
 #define NR_BOARD_IRQS                  32
 
 #define NR_IRQS                                (INT_BOARD_BASE + NR_BOARD_IRQS)
-#endif
 
 #endif
index 1670810..5b909a5 100644 (file)
@@ -20,6 +20,7 @@
 #ifndef __MACH_TEGRA_MC_H
 #define __MACH_TEGRA_MC_H
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #define TEGRA_MC_FPRI_CTRL_AVPC                0x17c
 #define TEGRA_MC_FPRI_CTRL_DC          0x180
 #define TEGRA_MC_FPRI_CTRL_DCB         0x184
 
 void tegra_mc_set_priority(unsigned long client, unsigned long prio);
 
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       /* !!!FIXME!!! IMPLEMENT ME */
+#define tegra_mc_set_priority(client, prio) \
+       do { /* nothing for now */ } while (0)
+#endif
+
 #endif
index 1cdcb28..4f3a24d 100644 (file)
 #define __MACH_TEGRA_MEMORY_H
 
 /* physical offset of RAM */
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #define PLAT_PHYS_OFFSET               UL(0)
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#define PLAT_PHYS_OFFSET               UL(0x80000000)
+#else
+#error "Invalid Tegra SoC family selection"
+#endif
 
+/*
+ * Unaligned DMA causes tegra dma to place data on 4-byte boundary after
+ * expected address. Call to skb_reserve(skb, NET_IP_ALIGN) was causing skb
+ * buffers in usbnet.c to become unaligned.
+ */
 #define NET_IP_ALIGN   0
 #define NET_SKB_PAD    L1_CACHE_BYTES
 
diff --git a/arch/arm/mach-tegra/include/mach/pinmux-t3.h b/arch/arm/mach-tegra/include/mach/pinmux-t3.h
new file mode 100644 (file)
index 0000000..921e3e8
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * linux/arch/arm/mach-tegra/include/mach/pinmux-t3.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_T3_H
+#define __MACH_TEGRA_PINMUX_T3_H
+
+#define TEGRA_PINMUX_HAS_IO_DIRECTION  1
+
+enum tegra_pingroup {
+       TEGRA_PINGROUP_ULPI_DATA0 = 0,
+       TEGRA_PINGROUP_ULPI_DATA1,
+       TEGRA_PINGROUP_ULPI_DATA2,
+       TEGRA_PINGROUP_ULPI_DATA3,
+       TEGRA_PINGROUP_ULPI_DATA4,
+       TEGRA_PINGROUP_ULPI_DATA5,
+       TEGRA_PINGROUP_ULPI_DATA6,
+       TEGRA_PINGROUP_ULPI_DATA7,
+       TEGRA_PINGROUP_ULPI_CLK,
+       TEGRA_PINGROUP_ULPI_DIR,
+       TEGRA_PINGROUP_ULPI_NXT,
+       TEGRA_PINGROUP_ULPI_STP,
+       TEGRA_PINGROUP_DAP3_FS,
+       TEGRA_PINGROUP_DAP3_DIN,
+       TEGRA_PINGROUP_DAP3_DOUT,
+       TEGRA_PINGROUP_DAP3_SCLK,
+       TEGRA_PINGROUP_GPIO_PV0,
+       TEGRA_PINGROUP_GPIO_PV1,
+       TEGRA_PINGROUP_SDMMC1_CLK,
+       TEGRA_PINGROUP_SDMMC1_CMD,
+       TEGRA_PINGROUP_SDMMC1_DAT3,
+       TEGRA_PINGROUP_SDMMC1_DAT2,
+       TEGRA_PINGROUP_SDMMC1_DAT1,
+       TEGRA_PINGROUP_SDMMC1_DAT0,
+       TEGRA_PINGROUP_GPIO_PV2,
+       TEGRA_PINGROUP_GPIO_PV3,
+       TEGRA_PINGROUP_CLK2_OUT,
+       TEGRA_PINGROUP_CLK2_REQ,
+       TEGRA_PINGROUP_LCD_PWR1,
+       TEGRA_PINGROUP_LCD_PWR2,
+       TEGRA_PINGROUP_LCD_SDIN,
+       TEGRA_PINGROUP_LCD_SDOUT,
+       TEGRA_PINGROUP_LCD_WR_N,
+       TEGRA_PINGROUP_LCD_CS0_N,
+       TEGRA_PINGROUP_LCD_DC0,
+       TEGRA_PINGROUP_LCD_SCK,
+       TEGRA_PINGROUP_LCD_PWR0,
+       TEGRA_PINGROUP_LCD_PCLK,
+       TEGRA_PINGROUP_LCD_DE,
+       TEGRA_PINGROUP_LCD_HSYNC,
+       TEGRA_PINGROUP_LCD_VSYNC,
+       TEGRA_PINGROUP_LCD_D0,
+       TEGRA_PINGROUP_LCD_D1,
+       TEGRA_PINGROUP_LCD_D2,
+       TEGRA_PINGROUP_LCD_D3,
+       TEGRA_PINGROUP_LCD_D4,
+       TEGRA_PINGROUP_LCD_D5,
+       TEGRA_PINGROUP_LCD_D6,
+       TEGRA_PINGROUP_LCD_D7,
+       TEGRA_PINGROUP_LCD_D8,
+       TEGRA_PINGROUP_LCD_D9,
+       TEGRA_PINGROUP_LCD_D10,
+       TEGRA_PINGROUP_LCD_D11,
+       TEGRA_PINGROUP_LCD_D12,
+       TEGRA_PINGROUP_LCD_D13,
+       TEGRA_PINGROUP_LCD_D14,
+       TEGRA_PINGROUP_LCD_D15,
+       TEGRA_PINGROUP_LCD_D16,
+       TEGRA_PINGROUP_LCD_D17,
+       TEGRA_PINGROUP_LCD_D18,
+       TEGRA_PINGROUP_LCD_D19,
+       TEGRA_PINGROUP_LCD_D20,
+       TEGRA_PINGROUP_LCD_D21,
+       TEGRA_PINGROUP_LCD_D22,
+       TEGRA_PINGROUP_LCD_D23,
+       TEGRA_PINGROUP_LCD_CS1_N,
+       TEGRA_PINGROUP_LCD_M1,
+       TEGRA_PINGROUP_LCD_DC1,
+       TEGRA_PINGROUP_HDMI_INT,
+       TEGRA_PINGROUP_DDC_SCL,
+       TEGRA_PINGROUP_DDC_SDA,
+       TEGRA_PINGROUP_CRT_HSYNC,
+       TEGRA_PINGROUP_CRT_VSYNC,
+       TEGRA_PINGROUP_VI_D0,
+       TEGRA_PINGROUP_VI_D1,
+       TEGRA_PINGROUP_VI_D2,
+       TEGRA_PINGROUP_VI_D3,
+       TEGRA_PINGROUP_VI_D4,
+       TEGRA_PINGROUP_VI_D5,
+       TEGRA_PINGROUP_VI_D6,
+       TEGRA_PINGROUP_VI_D7,
+       TEGRA_PINGROUP_VI_D8,
+       TEGRA_PINGROUP_VI_D9,
+       TEGRA_PINGROUP_VI_D10,
+       TEGRA_PINGROUP_VI_D11,
+       TEGRA_PINGROUP_VI_PCLK,
+       TEGRA_PINGROUP_VI_MCLK,
+       TEGRA_PINGROUP_VI_VSYNC,
+       TEGRA_PINGROUP_VI_HSYNC,
+       TEGRA_PINGROUP_UART2_RXD,
+       TEGRA_PINGROUP_UART2_TXD,
+       TEGRA_PINGROUP_UART2_RTS_N,
+       TEGRA_PINGROUP_UART2_CTS_N,
+       TEGRA_PINGROUP_UART3_TXD,
+       TEGRA_PINGROUP_UART3_RXD,
+       TEGRA_PINGROUP_UART3_CTS_N,
+       TEGRA_PINGROUP_UART3_RTS_N,
+       TEGRA_PINGROUP_GPIO_PU0,
+       TEGRA_PINGROUP_GPIO_PU1,
+       TEGRA_PINGROUP_GPIO_PU2,
+       TEGRA_PINGROUP_GPIO_PU3,
+       TEGRA_PINGROUP_GPIO_PU4,
+       TEGRA_PINGROUP_GPIO_PU5,
+       TEGRA_PINGROUP_GPIO_PU6,
+       TEGRA_PINGROUP_GEN1_I2C_SDA,
+       TEGRA_PINGROUP_GEN1_I2C_SCL,
+       TEGRA_PINGROUP_DAP4_FS,
+       TEGRA_PINGROUP_DAP4_DIN,
+       TEGRA_PINGROUP_DAP4_DOUT,
+       TEGRA_PINGROUP_DAP4_SCLK,
+       TEGRA_PINGROUP_CLK3_OUT,
+       TEGRA_PINGROUP_CLK3_REQ,
+       TEGRA_PINGROUP_GMI_WP_N,
+       TEGRA_PINGROUP_GMI_IORDY,
+       TEGRA_PINGROUP_GMI_WAIT,
+       TEGRA_PINGROUP_GMI_ADV_N,
+       TEGRA_PINGROUP_GMI_CLK,
+       TEGRA_PINGROUP_GMI_CS0_N,
+       TEGRA_PINGROUP_GMI_CS1_N,
+       TEGRA_PINGROUP_GMI_CS2_N,
+       TEGRA_PINGROUP_GMI_CS3_N,
+       TEGRA_PINGROUP_GMI_CS4_N,
+       TEGRA_PINGROUP_GMI_CS6_N,
+       TEGRA_PINGROUP_GMI_CS7_N,
+       TEGRA_PINGROUP_GMI_AD0,
+       TEGRA_PINGROUP_GMI_AD1,
+       TEGRA_PINGROUP_GMI_AD2,
+       TEGRA_PINGROUP_GMI_AD3,
+       TEGRA_PINGROUP_GMI_AD4,
+       TEGRA_PINGROUP_GMI_AD5,
+       TEGRA_PINGROUP_GMI_AD6,
+       TEGRA_PINGROUP_GMI_AD7,
+       TEGRA_PINGROUP_GMI_AD8,
+       TEGRA_PINGROUP_GMI_AD9,
+       TEGRA_PINGROUP_GMI_AD10,
+       TEGRA_PINGROUP_GMI_AD11,
+       TEGRA_PINGROUP_GMI_AD12,
+       TEGRA_PINGROUP_GMI_AD13,
+       TEGRA_PINGROUP_GMI_AD14,
+       TEGRA_PINGROUP_GMI_AD15,
+       TEGRA_PINGROUP_GMI_A16,
+       TEGRA_PINGROUP_GMI_A17,
+       TEGRA_PINGROUP_GMI_A18,
+       TEGRA_PINGROUP_GMI_A19,
+       TEGRA_PINGROUP_GMI_WR_N,
+       TEGRA_PINGROUP_GMI_OE_N,
+       TEGRA_PINGROUP_GMI_DQS,
+       TEGRA_PINGROUP_GMI_RST_N,
+       TEGRA_PINGROUP_GEN2_I2C_SCL,
+       TEGRA_PINGROUP_GEN2_I2C_SDA,
+       TEGRA_PINGROUP_SDMMC4_CLK,
+       TEGRA_PINGROUP_SDMMC4_CMD,
+       TEGRA_PINGROUP_SDMMC4_DAT0,
+       TEGRA_PINGROUP_SDMMC4_DAT1,
+       TEGRA_PINGROUP_SDMMC4_DAT2,
+       TEGRA_PINGROUP_SDMMC4_DAT3,
+       TEGRA_PINGROUP_SDMMC4_DAT4,
+       TEGRA_PINGROUP_SDMMC4_DAT5,
+       TEGRA_PINGROUP_SDMMC4_DAT6,
+       TEGRA_PINGROUP_SDMMC4_DAT7,
+       TEGRA_PINGROUP_SDMMC4_RST_N,
+       TEGRA_PINGROUP_CAM_MCLK,
+       TEGRA_PINGROUP_GPIO_PCC1,
+       TEGRA_PINGROUP_GPIO_PBB0,
+       TEGRA_PINGROUP_CAM_I2C_SCL,
+       TEGRA_PINGROUP_CAM_I2C_SDA,
+       TEGRA_PINGROUP_GPIO_PBB3,
+       TEGRA_PINGROUP_GPIO_PBB4,
+       TEGRA_PINGROUP_GPIO_PBB5,
+       TEGRA_PINGROUP_GPIO_PBB6,
+       TEGRA_PINGROUP_GPIO_PBB7,
+       TEGRA_PINGROUP_GPIO_PCC2,
+       TEGRA_PINGROUP_JTAG_RTCK,
+       TEGRA_PINGROUP_PWR_I2C_SCL,
+       TEGRA_PINGROUP_PWR_I2C_SDA,
+       TEGRA_PINGROUP_KB_ROW0,
+       TEGRA_PINGROUP_KB_ROW1,
+       TEGRA_PINGROUP_KB_ROW2,
+       TEGRA_PINGROUP_KB_ROW3,
+       TEGRA_PINGROUP_KB_ROW4,
+       TEGRA_PINGROUP_KB_ROW5,
+       TEGRA_PINGROUP_KB_ROW6,
+       TEGRA_PINGROUP_KB_ROW7,
+       TEGRA_PINGROUP_KB_ROW8,
+       TEGRA_PINGROUP_KB_ROW9,
+       TEGRA_PINGROUP_KB_ROW10,
+       TEGRA_PINGROUP_KB_ROW11,
+       TEGRA_PINGROUP_KB_ROW12,
+       TEGRA_PINGROUP_KB_ROW13,
+       TEGRA_PINGROUP_KB_ROW14,
+       TEGRA_PINGROUP_KB_ROW15,
+       TEGRA_PINGROUP_KB_COL0,
+       TEGRA_PINGROUP_KB_COL1,
+       TEGRA_PINGROUP_KB_COL2,
+       TEGRA_PINGROUP_KB_COL3,
+       TEGRA_PINGROUP_KB_COL4,
+       TEGRA_PINGROUP_KB_COL5,
+       TEGRA_PINGROUP_KB_COL6,
+       TEGRA_PINGROUP_KB_COL7,
+       TEGRA_PINGROUP_CLK_32K_OUT,
+       TEGRA_PINGROUP_SYS_CLK_REQ,
+       TEGRA_PINGROUP_CORE_PWR_REQ,
+       TEGRA_PINGROUP_CPU_PWR_REQ,
+       TEGRA_PINGROUP_PWR_INT_N,
+       TEGRA_PINGROUP_CLK_32K_IN,
+       TEGRA_PINGROUP_OWR,
+       TEGRA_PINGROUP_DAP1_FS,
+       TEGRA_PINGROUP_DAP1_DIN,
+       TEGRA_PINGROUP_DAP1_DOUT,
+       TEGRA_PINGROUP_DAP1_SCLK,
+       TEGRA_PINGROUP_CLK1_REQ,
+       TEGRA_PINGROUP_CLK1_OUT,
+       TEGRA_PINGROUP_SPDIF_IN,
+       TEGRA_PINGROUP_SPDIF_OUT,
+       TEGRA_PINGROUP_DAP2_FS,
+       TEGRA_PINGROUP_DAP2_DIN,
+       TEGRA_PINGROUP_DAP2_DOUT,
+       TEGRA_PINGROUP_DAP2_SCLK,
+       TEGRA_PINGROUP_SPI2_MOSI,
+       TEGRA_PINGROUP_SPI2_MISO,
+       TEGRA_PINGROUP_SPI2_CS0_N,
+       TEGRA_PINGROUP_SPI2_SCK,
+       TEGRA_PINGROUP_SPI1_MOSI,
+       TEGRA_PINGROUP_SPI1_SCK,
+       TEGRA_PINGROUP_SPI1_CS0_N,
+       TEGRA_PINGROUP_SPI1_MISO,
+       TEGRA_PINGROUP_SPI2_CS1_N,
+       TEGRA_PINGROUP_SPI2_CS2_N,
+       TEGRA_PINGROUP_SDMMC3_CLK,
+       TEGRA_PINGROUP_SDMMC3_CMD,
+       TEGRA_PINGROUP_SDMMC3_DAT0,
+       TEGRA_PINGROUP_SDMMC3_DAT1,
+       TEGRA_PINGROUP_SDMMC3_DAT2,
+       TEGRA_PINGROUP_SDMMC3_DAT3,
+       TEGRA_PINGROUP_SDMMC3_DAT4,
+       TEGRA_PINGROUP_SDMMC3_DAT5,
+       TEGRA_PINGROUP_SDMMC3_DAT6,
+       TEGRA_PINGROUP_SDMMC3_DAT7,
+       TEGRA_PINGROUP_PEX_L0_PRSNT_N,
+       TEGRA_PINGROUP_PEX_L0_RST_N,
+       TEGRA_PINGROUP_PEX_L0_CLKREQ_N,
+       TEGRA_PINGROUP_PEX_WAKE_N,
+       TEGRA_PINGROUP_PEX_L1_PRSNT_N,
+       TEGRA_PINGROUP_PEX_L1_RST_N,
+       TEGRA_PINGROUP_PEX_L1_CLKREQ_N,
+       TEGRA_PINGROUP_PEX_L2_PRSNT_N,
+       TEGRA_PINGROUP_PEX_L2_RST_N,
+       TEGRA_PINGROUP_PEX_L2_CLKREQ_N,
+       TEGRA_PINGROUP_HDMI_CEC,
+       TEGRA_MAX_PINGROUP,
+};
+
+#endif
+enum tegra_drive_pingroup {
+       TEGRA_DRIVE_PINGROUP_AO1 = 0,
+       TEGRA_DRIVE_PINGROUP_AO2,
+       TEGRA_DRIVE_PINGROUP_AT1,
+       TEGRA_DRIVE_PINGROUP_AT2,
+       TEGRA_DRIVE_PINGROUP_AT3,
+       TEGRA_DRIVE_PINGROUP_AT4,
+       TEGRA_DRIVE_PINGROUP_AT5,
+       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_SDIO2,
+       TEGRA_DRIVE_PINGROUP_SDIO3,
+       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_SDIO1,
+       TEGRA_DRIVE_PINGROUP_CRT,
+       TEGRA_DRIVE_PINGROUP_DDC,
+       TEGRA_DRIVE_PINGROUP_GMA,
+       TEGRA_DRIVE_PINGROUP_GMB,
+       TEGRA_DRIVE_PINGROUP_GMC,
+       TEGRA_DRIVE_PINGROUP_GMD,
+       TEGRA_DRIVE_PINGROUP_GME,
+       TEGRA_DRIVE_PINGROUP_GMF,
+       TEGRA_DRIVE_PINGROUP_GMG,
+       TEGRA_DRIVE_PINGROUP_GMH,
+       TEGRA_DRIVE_PINGROUP_OWR,
+       TEGRA_DRIVE_PINGROUP_UAD,
+       TEGRA_DRIVE_PINGROUP_GPV,
+       TEGRA_DRIVE_PINGROUP_DEV3,
+       TEGRA_DRIVE_PINGROUP_CEC,
+       TEGRA_MAX_DRIVE_PINGROUP,
+};
+
index 2207d0e..47056ac 100644 (file)
 
 #if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #include "pinmux-t2.h"
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#include "pinmux-t3.h"
 #else
 #error "Undefined Tegra architecture"
 #endif
 
 enum tegra_mux_func {
        TEGRA_MUX_RSVD = 0x8000,
+       TEGRA_MUX_RSVD0 = TEGRA_MUX_RSVD,
        TEGRA_MUX_RSVD1 = 0x8000,
        TEGRA_MUX_RSVD2 = 0x8001,
        TEGRA_MUX_RSVD3 = 0x8002,
        TEGRA_MUX_RSVD4 = 0x8003,
-       TEGRA_MUX_NONE = -1,
+       TEGRA_MUX_NONE = 0,
        TEGRA_MUX_AHB_CLK,
        TEGRA_MUX_APB_CLK,
        TEGRA_MUX_AUDIO_SYNC,
@@ -47,6 +50,7 @@ enum tegra_mux_func {
        TEGRA_MUX_GMI_INT,
        TEGRA_MUX_HDMI,
        TEGRA_MUX_I2C,
+       TEGRA_MUX_I2C1 = TEGRA_MUX_I2C,
        TEGRA_MUX_I2C2,
        TEGRA_MUX_I2C3,
        TEGRA_MUX_IDE,
@@ -69,9 +73,13 @@ enum tegra_mux_func {
        TEGRA_MUX_PWR_ON,
        TEGRA_MUX_RTCK,
        TEGRA_MUX_SDIO1,
+       TEGRA_MUX_SDMMC1 = TEGRA_MUX_SDIO1,
        TEGRA_MUX_SDIO2,
+       TEGRA_MUX_SDMMC2 = TEGRA_MUX_SDIO2,
        TEGRA_MUX_SDIO3,
+       TEGRA_MUX_SDMMC3 = TEGRA_MUX_SDIO3,
        TEGRA_MUX_SDIO4,
+       TEGRA_MUX_SDMMC4 = TEGRA_MUX_SDIO4,
        TEGRA_MUX_SFLASH,
        TEGRA_MUX_SPDIF,
        TEGRA_MUX_SPI1,
@@ -90,7 +98,52 @@ enum tegra_mux_func {
        TEGRA_MUX_VI,
        TEGRA_MUX_VI_SENSOR_CLK,
        TEGRA_MUX_XIO,
-       TEGRA_MUX_SAFE,
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       TEGRA_MUX_BLINK,
+       TEGRA_MUX_CEC,
+       TEGRA_MUX_CLK12,
+       TEGRA_MUX_DAP,
+       TEGRA_MUX_DAPSDMMC2,
+       TEGRA_MUX_DDR,
+       TEGRA_MUX_DEV3,
+       TEGRA_MUX_DTV,
+       TEGRA_MUX_VI_ALT1,
+       TEGRA_MUX_VI_ALT2,
+       TEGRA_MUX_VI_ALT3,
+       TEGRA_MUX_EMC_DLL,
+       TEGRA_MUX_EXTPERIPH1,
+       TEGRA_MUX_EXTPERIPH2,
+       TEGRA_MUX_EXTPERIPH3,
+       TEGRA_MUX_GMI_ALT,
+       TEGRA_MUX_HDA,
+       TEGRA_MUX_HSI,
+       TEGRA_MUX_I2C4,
+       TEGRA_MUX_I2C5,
+       TEGRA_MUX_I2CPWR,
+       TEGRA_MUX_I2S0,
+       TEGRA_MUX_I2S1,
+       TEGRA_MUX_I2S2,
+       TEGRA_MUX_I2S3,
+       TEGRA_MUX_I2S4,
+       TEGRA_MUX_NAND_ALT,
+       TEGRA_MUX_POPSDIO4,
+       TEGRA_MUX_POPSDMMC4,
+       TEGRA_MUX_PWM0,
+       TEGRA_MUX_PWM1,
+       TEGRA_MUX_PWM2,
+       TEGRA_MUX_PWM3,
+       TEGRA_MUX_SATA,
+       TEGRA_MUX_SPI5,
+       TEGRA_MUX_SPI6,
+       TEGRA_MUX_SYSCLK,
+       TEGRA_MUX_VGP1,
+       TEGRA_MUX_VGP2,
+       TEGRA_MUX_VGP3,
+       TEGRA_MUX_VGP4,
+       TEGRA_MUX_VGP5,
+       TEGRA_MUX_VGP6,
+#endif
+        TEGRA_MUX_SAFE,
        TEGRA_MAX_MUX,
 };
 
@@ -105,6 +158,11 @@ enum tegra_tristate {
        TEGRA_TRI_TRISTATE = 1,
 };
 
+enum tegra_pin_io {
+       TEGRA_PIN_OUTPUT = 0,
+       TEGRA_PIN_INPUT = 1,
+};
+
 enum tegra_vddio {
        TEGRA_VDDIO_BB = 0,
        TEGRA_VDDIO_LCD,
@@ -115,6 +173,14 @@ enum tegra_vddio {
        TEGRA_VDDIO_SYS,
        TEGRA_VDDIO_AUDIO,
        TEGRA_VDDIO_SD,
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       TEGRA_VDDIO_CAM,
+       TEGRA_VDDIO_GMI,
+       TEGRA_VDDIO_PEXCTL,
+       TEGRA_VDDIO_SDMMC1,
+       TEGRA_VDDIO_SDMMC3,
+       TEGRA_VDDIO_SDMMC4,
+#endif
 };
 
 struct tegra_pingroup_config {
@@ -122,6 +188,7 @@ struct tegra_pingroup_config {
        enum tegra_mux_func     func;
        enum tegra_pullupdown   pupd;
        enum tegra_tristate     tristate;
+       enum tegra_pin_io       io;
 };
 
 enum tegra_slew {
@@ -213,6 +280,7 @@ struct tegra_pingroup_desc {
        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 */
+       s8 io_default;
 };
 
 extern const struct tegra_pingroup_desc tegra_soc_pingroups[];
index 401d1b7..dd636a7 100644 (file)
 #define _MACH_TEGRA_POWERGATE_H_
 
 #define TEGRA_POWERGATE_CPU    0
+#define TEGRA_POWERGATE_CPU0   TEGRA_POWERGATE_CPU
 #define TEGRA_POWERGATE_3D     1
+#define TEGRA_POWERGATE_3D0    TEGRA_POWERGATE_3D
 #define TEGRA_POWERGATE_VENC   2
 #define TEGRA_POWERGATE_PCIE   3
 #define TEGRA_POWERGATE_VDEC   4
 #define TEGRA_POWERGATE_L2     5
 #define TEGRA_POWERGATE_MPE    6
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 #define TEGRA_NUM_POWERGATE    7
+#define TEGRA_CPU_POWERGATE_ID(cpu)    (TEGRA_POWERGATE_CPU)
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+#define TEGRA_POWERGATE_HEG    7
+#define TEGRA_POWERGATE_SATA   8
+#define TEGRA_POWERGATE_CPU1   9
+#define TEGRA_POWERGATE_CPU2   10
+#define TEGRA_POWERGATE_CPU3   11
+#define TEGRA_POWERGATE_A9LP   12
+#define TEGRA_POWERGATE_3D1    13
+#define TEGRA_NUM_POWERGATE    14
+#define TEGRA_CPU_POWERGATE_ID(cpu)    ((cpu == 0) ? TEGRA_POWERGATE_CPU0 : \
+                                               (cpu + TEGRA_POWERGATE_CPU1 - 1))
+#endif
+
+struct clk;
 
 int tegra_powergate_power_on(int id);
 int tegra_powergate_power_off(int id);
index 4e83237..b400f92 100644 (file)
 
 #include <mach/iomap.h>
 
+#if defined(CONFIG_TEGRA_DEBUG_UARTA)
+#define DEBUG_UART_CLK_SRC             (TEGRA_CLK_RESET_BASE + 0x178)
+#define DEBUG_UART_CLK_ENB_SET_REG     (TEGRA_CLK_RESET_BASE + 0x320)
+#define DEBUG_UART_CLK_ENB_SET_BIT     (1 << 6)
+#define DEBUG_UART_RST_CLR_REG         (TEGRA_CLK_RESET_BASE + 0x304)
+#define DEBUG_UART_RST_CLR_BIT         (1 << 6)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTB)
+#define DEBUG_UART_CLK_SRC             (TEGRA_CLK_RESET_BASE + 0x17c)
+#define DEBUG_UART_CLK_ENB_SET_REG     (TEGRA_CLK_RESET_BASE + 0x320)
+#define DEBUG_UART_CLK_ENB_SET_BIT     (1 << 7)
+#define DEBUG_UART_RST_CLR_REG         (TEGRA_CLK_RESET_BASE + 0x304)
+#define DEBUG_UART_RST_CLR_BIT         (1 << 7)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTC)
+#define DEBUG_UART_CLK_SRC             (TEGRA_CLK_RESET_BASE + 0x1a0)
+#define DEBUG_UART_CLK_ENB_SET_REG     (TEGRA_CLK_RESET_BASE + 0x328)
+#define DEBUG_UART_CLK_ENB_SET_BIT     (1 << 23)
+#define DEBUG_UART_RST_CLR_REG         (TEGRA_CLK_RESET_BASE + 0x30C)
+#define DEBUG_UART_RST_CLR_BIT         (1 << 23)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTD)
+#define DEBUG_UART_CLK_SRC             (TEGRA_CLK_RESET_BASE + 0x1c0)
+#define DEBUG_UART_CLK_ENB_SET_REG     (TEGRA_CLK_RESET_BASE + 0x330)
+#define DEBUG_UART_CLK_ENB_SET_BIT     (1 << 1)
+#define DEBUG_UART_RST_CLR_REG         (TEGRA_CLK_RESET_BASE + 0x314)
+#define DEBUG_UART_RST_CLR_BIT         (1 << 1)
+#elif defined(CONFIG_TEGRA_DEBUG_UARTE)
+#define DEBUG_UART_CLK_SRC             (TEGRA_CLK_RESET_BASE + 0x1c4)
+#define DEBUG_UART_CLK_ENB_SET_REG     (TEGRA_CLK_RESET_BASE + 0x330)
+#define DEBUG_UART_CLK_ENB_SET_BIT     (1 << 2)
+#define DEBUG_UART_RST_CLR_REG         (TEGRA_CLK_RESET_BASE + 0x314)
+#define DEBUG_UART_RST_CLR_BIT         (1 << 2)
+#else
+#define DEBUG_UART_CLK_SRC             0
+#define DEBUG_UART_CLK_ENB_SET_REG     0
+#define DEBUG_UART_CLK_ENB_SET_BIT     0
+#define DEBUG_UART_RST_CLR_REG         0
+#define DEBUG_UART_RST_CLR_BIT         0
+#endif
+
 static void putc(int c)
 {
        volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
@@ -43,14 +81,42 @@ static inline void flush(void)
 {
 }
 
+static inline void konk_delay(int delay)
+{
+       int i;
+
+       for (i = 0; i < (1000 * delay); i++) {
+               barrier();
+       }
+}
+
+
 static inline void arch_decomp_setup(void)
 {
        volatile u8 *uart = (volatile u8 *)TEGRA_DEBUG_UART_BASE;
        int shift = 2;
+       volatile u32 *addr;
 
        if (uart == NULL)
                return;
 
+       /* Debug UART clock source is PLLP_OUT0. */
+       addr = (volatile u32 *)DEBUG_UART_CLK_SRC;
+       *addr = 0;
+
+       /* Enable clock to debug UART. */
+       addr = (volatile u32 *)DEBUG_UART_CLK_ENB_SET_REG;
+       *addr = DEBUG_UART_CLK_ENB_SET_BIT;
+
+       konk_delay(5);
+
+       /* Deassert reset to debug UART. */
+       addr = (volatile u32 *)DEBUG_UART_RST_CLR_REG;
+       *addr = DEBUG_UART_RST_CLR_BIT;
+
+       konk_delay(5);
+
+       /* Set up debug UART. */
        uart[UART_LCR << shift] |= UART_LCR_DLAB;
        uart[UART_DLL << shift] = 0x75;
        uart[UART_DLM << shift] = 0x0;
index fbab036..adf4699 100644 (file)
@@ -56,10 +56,10 @@ struct gart_device {
        bool                    needs_barrier; /* emulator WAR */
 };
 
-static int gart_map(struct tegra_iovmm_device *, struct tegra_iovmm_area *);
-static void gart_unmap(struct tegra_iovmm_device *,
+static int gart_map(struct tegra_iovmm_domain *, struct tegra_iovmm_area *);
+static void gart_unmap(struct tegra_iovmm_domain *,
        struct tegra_iovmm_area *, bool);
-static void gart_map_pfn(struct tegra_iovmm_device *,
+static void gart_map_pfn(struct tegra_iovmm_domain *,
        struct tegra_iovmm_area *, tegra_iovmm_addr_t, unsigned long);
 static struct tegra_iovmm_domain *gart_alloc_domain(
        struct tegra_iovmm_device *, struct tegra_iovmm_client *);
@@ -258,10 +258,10 @@ static void __exit gart_exit(void)
 #define GART_PTE(_pfn) (0x80000000ul | ((_pfn)<<PAGE_SHIFT))
 
 
-static int gart_map(struct tegra_iovmm_device *dev,
+static int gart_map(struct tegra_iovmm_domain *domain,
        struct tegra_iovmm_area *iovma)
 {
-       struct gart_device *gart = container_of(dev, struct gart_device, iovmm);
+       struct gart_device *gart = container_of(domain, struct gart_device, domain);
        unsigned long gart_page, count;
        unsigned int i;
 
@@ -301,10 +301,10 @@ fail:
        return -ENOMEM;
 }
 
-static void gart_unmap(struct tegra_iovmm_device *dev,
+static void gart_unmap(struct tegra_iovmm_domain *domain,
        struct tegra_iovmm_area *iovma, bool decommit)
 {
-       struct gart_device *gart = container_of(dev, struct gart_device, iovmm);
+       struct gart_device *gart = container_of(domain, struct gart_device, domain);
        unsigned long gart_page, count;
        unsigned int i;
 
@@ -325,11 +325,11 @@ static void gart_unmap(struct tegra_iovmm_device *dev,
        wmb();
 }
 
-static void gart_map_pfn(struct tegra_iovmm_device *dev,
+static void gart_map_pfn(struct tegra_iovmm_domain *domain,
        struct tegra_iovmm_area *iovma, tegra_iovmm_addr_t offs,
        unsigned long pfn)
 {
-       struct gart_device *gart = container_of(dev, struct gart_device, iovmm);
+       struct gart_device *gart = container_of(domain, struct gart_device, domain);
 
        BUG_ON(!pfn_valid(pfn));
        spin_lock(&gart->pte_lock);
diff --git a/arch/arm/mach-tegra/iovmm-smmu.c b/arch/arm/mach-tegra/iovmm-smmu.c
new file mode 100644 (file)
index 0000000..f899d72
--- /dev/null
@@ -0,0 +1,1167 @@
+/*
+ * arch/arm/mach-tegra/iovmm-smmu.c
+ *
+ * Tegra I/O VMM implementation for SMMU devices for Tegra 3 series
+ * systems-on-a-chip.
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/sysfs.h>
+#include <linux/slab.h>
+
+#include <asm/io.h>
+#include <asm/page.h>
+#include <asm/cacheflush.h>
+
+#include <mach/iovmm.h>
+#include <mach/iomap.h>
+
+// For debugging
+//#define HIT_MISS_STAT
+//#define SMMU_SYSFS
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+//
+// ALL-CAP macros have been copied from t30/armc.h
+//
+#define MC_SMMU_CONFIG_0                               0x10
+#define MC_SMMU_CONFIG_0_SMMU_ENABLE_DISABLE           0
+#define MC_SMMU_CONFIG_0_SMMU_ENABLE_ENABLE            1
+
+#define MC_SMMU_TLB_CONFIG_0                           0x14
+#define MC_SMMU_TLB_CONFIG_0_TLB_STATS_enable          (1<<31)
+#define MC_SMMU_TLB_CONFIG_0_TLB_HIT_UNDER_MISS_enable (1<<29)
+#define MC_SMMU_TLB_CONFIG_0_TLB_ACTIVE_LINES_value    0x10
+#define MC_SMMU_TLB_CONFIG_0_RESET_VAL                 0x20000010
+
+#define MC_SMMU_PTC_CONFIG_0                           0x18
+#define MC_SMMU_PTC_CONFIG_0_PTC_STATS_enable          (1<<31)
+#define MC_SMMU_PTC_CONFIG_0_PTC_CACHE_enable          (1<<29)
+#define MC_SMMU_PTC_CONFIG_0_PTC_INDEX_MAP_pattern     0x3f
+#define MC_SMMU_PTC_CONFIG_0_RESET_VAL                 0x2000003f
+
+#define MC_SMMU_PTB_ASID_0                             0x1c
+#define MC_SMMU_PTB_ASID_0_CURRENT_ASID_SHIFT          0
+
+#define MC_SMMU_PTB_DATA_0                             0x20
+#define MC_SMMU_PTB_DATA_0_RESET_VAL                   0
+#define MC_SMMU_PTB_DATA_0_ASID_NONSECURE_SHIFT                29
+#define MC_SMMU_PTB_DATA_0_ASID_WRITABLE_SHIFT         30
+#define MC_SMMU_PTB_DATA_0_ASID_READABLE_SHIFT         31
+
+#define MC_SMMU_TLB_FLUSH_0                            0x30
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_ALL             0
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_SECTION         2
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_GROUP           3
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_SHIFT               29
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_DISABLE       0
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_ENABLE                1
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_SHIFT         31
+
+#define MC_SMMU_PTC_FLUSH_0                            0x34
+#define MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ALL         0
+#define MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ADR         1
+#define MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_ADR_SHIFT                4
+
+#define MC_SMMU_ASID_SECURITY_0                                0x38
+
+#define MC_SMMU_STATS_TLB_HIT_COUNT_0                  0x1f0
+#define MC_SMMU_STATS_TLB_MISS_COUNT_0                 0x1f4
+#define MC_SMMU_STATS_PTC_HIT_COUNT_0                  0x1f8
+#define MC_SMMU_STATS_PTC_MISS_COUNT_0                 0x1fc
+
+#define MC_SMMU_TRANSLATION_ENABLE_0_0                 0x228
+#define MC_SMMU_TRANSLATION_ENABLE_1_0                 0x22c
+#define MC_SMMU_TRANSLATION_ENABLE_2_0                 0x230
+
+#define MC_SMMU_AFI_ASID_0              0x238   // PCIE
+#define MC_SMMU_AVPC_ASID_0             0x23c   // AVP
+#define MC_SMMU_DC_ASID_0               0x240   // Display controller
+#define MC_SMMU_DCB_ASID_0              0x244   // Display controller B
+#define MC_SMMU_EPP_ASID_0              0x248   // Encoder pre-processor
+#define MC_SMMU_G2_ASID_0               0x24c   // 2D engine
+#define MC_SMMU_HC_ASID_0               0x250   // Host1x
+#define MC_SMMU_HDA_ASID_0              0x254   // High-def audio
+#define MC_SMMU_ISP_ASID_0              0x258   // Image signal processor
+#define MC_SMMU_MPE_ASID_0              0x264   // MPEG encoder
+#define MC_SMMU_NV_ASID_0               0x268   // (3D)
+#define MC_SMMU_NV2_ASID_0              0x26c   // (3D)
+#define MC_SMMU_PPCS_ASID_0             0x270   // AHB
+#define MC_SMMU_SATA_ASID_0             0x278   // SATA
+#define MC_SMMU_VDE_ASID_0              0x27c   // Video decoder
+#define MC_SMMU_VI_ASID_0               0x280   // Video input
+
+#define SMMU_PDE_NEXT_SHIFT            28
+#endif
+
+#define MC_SMMU_NUM_ASIDS      4
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_GROUP_mask            0xffffc000
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_GROUP_shift   12      // right shift
+#define MC_SMMU_PTB_ASID_0_CURRENT_ASID(n)     \
+               ((n) << MC_SMMU_PTB_ASID_0_CURRENT_ASID_SHIFT)
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_disable               \
+               (MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_DISABLE <<    \
+                       MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_SHIFT)
+#define MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_enable                        \
+               (MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_ENABLE <<     \
+                       MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_SHIFT)
+
+#define VMM_NAME "iovmm-smmu"
+#define DRIVER_NAME "tegra_smmu"
+
+#define SMMU_PAGE_SHIFT 12
+#define SMMU_PAGE_SIZE         (1 << SMMU_PAGE_SHIFT)
+
+typedef unsigned long smmu_pde_t;
+typedef unsigned long smmu_pte_t;
+
+#define SMMU_PDIR_COUNT        1024
+#define SMMU_PDIR_SIZE (sizeof(smmu_pde_t) * SMMU_PDIR_COUNT)
+#define SMMU_PTBL_COUNT        1024
+#define SMMU_PTBL_SIZE (sizeof(smmu_pte_t) * SMMU_PTBL_COUNT)
+#define SMMU_PDIR_SHIFT        12
+#define SMMU_PDE_SHIFT 12
+#define SMMU_PTE_SHIFT 12
+#define SMMU_PFN_MASK  0x000fffff
+
+#define SMMU_ADDR_TO_PFN(addr) ((addr)>>12)
+#define SMMU_ADDR_TO_PDN(addr) ((addr)>>22)
+#define SMMU_PDN_TO_ADDR(addr) ((pdn)<<22)
+
+#define _READABLE      (1<<MC_SMMU_PTB_DATA_0_ASID_READABLE_SHIFT)
+#define _WRITABLE      (1<<MC_SMMU_PTB_DATA_0_ASID_WRITABLE_SHIFT)
+#define _NONSECURE     (1<<MC_SMMU_PTB_DATA_0_ASID_NONSECURE_SHIFT)
+#define _PDE_NEXT      (1<<SMMU_PDE_NEXT_SHIFT)
+
+#define _PDIR_ATTR     (_READABLE|_WRITABLE|_NONSECURE)
+
+#define _PDE_ATTR      (_READABLE|_WRITABLE|_NONSECURE)
+#define _PDE_ATTR_N    (_PDE_ATTR|_PDE_NEXT)
+#define _PDE_VACANT(pdn)       (((pdn)<<10)|_PDE_ATTR)
+
+#define _PTE_ATTR      (_READABLE|_WRITABLE|_NONSECURE)
+#define _PTE_VACANT(addr)      (((addr)>>SMMU_PAGE_SHIFT)|_PTE_ATTR)
+
+#define SMMU_MK_PDIR(page, attr)       \
+               ((page_to_phys(page)>>SMMU_PDIR_SHIFT)|(attr))
+#define SMMU_MK_PDE(page, attr)                \
+               (smmu_pde_t)((page_to_phys(page)>>SMMU_PDE_SHIFT)|(attr))
+#define SMMU_EX_PTBL_PAGE(pde)         \
+               pfn_to_page((unsigned long)(pde) & SMMU_PFN_MASK)
+#define SMMU_PFN_TO_PTE(pfn, attr)     (smmu_pte_t)((pfn)|(attr))
+
+#define SMMU_ASID_ENABLE(asid) ((asid)|(1<<31))
+#define SMMU_ASID_DISABLE      0
+#define SMMU_ASID_ASID(n)      ((n)&~SMMU_ASID_ENABLE(0))
+
+// Keep this as a "natural" enumeration (no assignments)
+enum smmu_hwclient {
+       HWC_AFI,
+       HWC_AVPC,
+       HWC_DC,
+       HWC_DCB,
+       HWC_EPP,
+       HWC_G2,
+       HWC_HC,
+       HWC_HDA,
+       HWC_ISP,
+       HWC_MPE,
+       HWC_NV,
+       HWC_NV2,
+       HWC_PPCS,
+       HWC_SATA,
+       HWC_VDE,
+       HWC_VI,
+
+       HWC_COUNT
+};
+
+struct smmu_hwc_state {
+       unsigned long reg;
+       unsigned long enable_disable;
+};
+
+// Hardware client mapping initializer
+#define HWC_INIT(client)       \
+       [HWC_##client] = {MC_SMMU_##client##_ASID_0, SMMU_ASID_DISABLE},
+
+static const struct smmu_hwc_state smmu_hwc_state_init[] = {
+       HWC_INIT(AFI)
+       HWC_INIT(AVPC)
+       HWC_INIT(DC)
+       HWC_INIT(DCB)
+       HWC_INIT(EPP)
+       HWC_INIT(G2)
+       HWC_INIT(HC)
+       HWC_INIT(HDA)
+       HWC_INIT(ISP)
+       HWC_INIT(MPE)
+       HWC_INIT(NV)
+       HWC_INIT(NV2)
+       HWC_INIT(PPCS)
+       HWC_INIT(SATA)
+       HWC_INIT(VDE)
+       HWC_INIT(VI)
+};
+
+
+struct domain_hwc_map {
+       const char *dev_name;
+       const enum smmu_hwclient *hwcs;
+       const unsigned int nr_hwcs;
+};
+
+// Enable all hardware clients for SMMU translation
+static const enum smmu_hwclient nvmap_hwcs[] = {
+       HWC_AFI,
+       HWC_AVPC,
+       HWC_DC,
+       HWC_DCB,
+       HWC_EPP,
+       HWC_G2,
+       HWC_HC,
+       HWC_HDA,
+       HWC_ISP,
+       HWC_MPE,
+       HWC_NV,
+       HWC_NV2,
+       HWC_PPCS,
+       HWC_SATA,
+       HWC_VDE,
+       HWC_VI
+};
+
+static const struct domain_hwc_map smmu_hwc_map[] = {
+       {
+               .dev_name = "nvmap",
+               .hwcs = nvmap_hwcs,
+               .nr_hwcs = ARRAY_SIZE(nvmap_hwcs),
+       },
+};
+
+//
+// Per address space
+//
+struct smmu_as {
+       struct smmu_device      *smmu;  /* back pointer to container */
+       unsigned int            asid;
+       const struct domain_hwc_map     *hwclients;
+       struct semaphore        sem;
+       struct tegra_iovmm_domain domain;
+       bool            needs_barrier;  /* emulator WAR */
+       struct page     *pdir_page;
+       unsigned long   pte_attr;
+       unsigned int    *pte_count;
+       struct device   sysfs_dev;
+       int             sysfs_use_count;
+};
+
+//
+// Per SMMU device
+//
+struct smmu_device {
+       void __iomem    *regs;
+       tegra_iovmm_addr_t      iovmm_base;     /* remappable base address */
+       unsigned long   page_count;             /* total remappable size */
+       spinlock_t      lock;
+       char            *name;
+       struct tegra_iovmm_device iovmm_dev;
+       int             num_ases;
+       struct smmu_as  *as;                    /* Run-time allocated array */
+       struct smmu_hwc_state   hwc_state[HWC_COUNT];
+       struct device   sysfs_dev;
+       int             sysfs_use_count;
+       bool            enable;
+       //
+       // Register image savers for suspend/resume
+       //
+       unsigned long translation_enable_0_0;
+       unsigned long translation_enable_1_0;
+       unsigned long translation_enable_2_0;
+       unsigned long asid_security_0;
+
+       int lowest_asid;        // Variable for hardware testing
+};
+
+#define VA_PAGE_TO_PA(va, page)        \
+       (page_to_phys(page) + ((unsigned long)(va) & ~PAGE_MASK))
+
+#define FLUSH_CPU_DCACHE(va, page, size)       \
+       do {    \
+               unsigned long _pa_ = VA_PAGE_TO_PA(va, page);           \
+               __cpuc_flush_dcache_area((void *)(va), (size_t)(size)); \
+               outer_flush_range(_pa_, _pa_+(size_t)(size));           \
+       } while (0)
+
+#define FLUSH_SMMU_REGS(smmu)  \
+       do { wmb(); (void)readl((smmu)->regs + MC_SMMU_CONFIG_0); } while(0)
+
+//
+// Flush all TLB entries and all PTC entries
+// Caller must lock smmu
+//
+static void smmu_flush_regs(struct smmu_device *smmu, int enable)
+{
+       writel(MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_ALL |
+                       MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_disable,
+               smmu->regs + MC_SMMU_TLB_FLUSH_0);
+       writel(MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ALL,
+               smmu->regs + MC_SMMU_PTC_FLUSH_0);
+
+       if (enable)
+               writel(MC_SMMU_CONFIG_0_SMMU_ENABLE_ENABLE,
+                       smmu->regs + MC_SMMU_CONFIG_0);
+       FLUSH_SMMU_REGS(smmu);
+}
+
+static void smmu_setup_regs(struct smmu_device *smmu)
+{
+       int i;
+
+       if (smmu->as) {
+               int asid;
+
+               // Set/restore page directory for each AS
+               for (asid = 0; asid < smmu->num_ases; asid++) {
+                       struct smmu_as *as = &smmu->as[asid];
+
+                       spin_lock(&smmu->lock);
+                       writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(as->asid),
+                               as->smmu->regs + MC_SMMU_PTB_ASID_0);
+                       writel(as->pdir_page
+                               ? SMMU_MK_PDIR(as->pdir_page, _PDIR_ATTR)
+                               : MC_SMMU_PTB_DATA_0_RESET_VAL,
+                               as->smmu->regs + MC_SMMU_PTB_DATA_0);
+                       spin_unlock(&smmu->lock);
+               }
+       }
+
+       // Set/restore ASID for each hardware client
+       for (i = 0; i < HWC_COUNT; i++) {
+               struct smmu_hwc_state *hwcst = &smmu->hwc_state[i];
+               writel(hwcst->enable_disable, smmu->regs + hwcst->reg);
+       }
+
+       writel(smmu->translation_enable_0_0,
+               smmu->regs + MC_SMMU_TRANSLATION_ENABLE_0_0);
+       writel(smmu->translation_enable_1_0,
+               smmu->regs + MC_SMMU_TRANSLATION_ENABLE_1_0);
+       writel(smmu->translation_enable_2_0,
+               smmu->regs + MC_SMMU_TRANSLATION_ENABLE_2_0);
+       writel(smmu->asid_security_0,
+               smmu->regs + MC_SMMU_ASID_SECURITY_0);
+#ifdef HIT_MISS_STAT
+       writel(
+               MC_SMMU_TLB_CONFIG_0_TLB_STATS_enable |
+               MC_SMMU_TLB_CONFIG_0_TLB_HIT_UNDER_MISS_enable |
+               MC_SMMU_TLB_CONFIG_0_TLB_ACTIVE_LINES_value,
+               smmu->regs + MC_SMMU_TLB_CONFIG_0);
+
+       writel(
+               MC_SMMU_PTC_CONFIG_0_PTC_STATS_enable |
+               MC_SMMU_PTC_CONFIG_0_PTC_CACHE_enable |
+               MC_SMMU_PTC_CONFIG_0_PTC_INDEX_MAP_pattern,
+               smmu->regs + MC_SMMU_PTC_CONFIG_0);
+#else
+       writel(MC_SMMU_TLB_CONFIG_0_RESET_VAL,
+               smmu->regs + MC_SMMU_TLB_CONFIG_0);
+       writel(MC_SMMU_PTC_CONFIG_0_RESET_VAL,
+               smmu->regs + MC_SMMU_PTC_CONFIG_0);
+#endif
+
+       smmu_flush_regs(smmu, 1);
+}
+
+static int smmu_suspend(struct tegra_iovmm_device *dev)
+{
+       struct smmu_device *smmu =
+               container_of(dev, struct smmu_device, iovmm_dev);
+
+       smmu->translation_enable_0_0 =
+               readl(smmu->regs + MC_SMMU_TRANSLATION_ENABLE_0_0);
+       smmu->translation_enable_1_0 =
+               readl(smmu->regs + MC_SMMU_TRANSLATION_ENABLE_1_0);
+       smmu->translation_enable_2_0 =
+               readl(smmu->regs + MC_SMMU_TRANSLATION_ENABLE_2_0);
+       smmu->asid_security_0 =
+               readl(smmu->regs + MC_SMMU_ASID_SECURITY_0);
+       return 0;
+}
+
+static void smmu_resume(struct tegra_iovmm_device *dev)
+{
+       struct smmu_device *smmu =
+               container_of(dev, struct smmu_device, iovmm_dev);
+
+       if (!smmu->enable)
+               return;
+
+       spin_lock(&smmu->lock);
+       smmu_setup_regs(smmu);
+       spin_unlock(&smmu->lock);
+}
+
+static void free_ptbl(struct smmu_as *as, unsigned long page_addr)
+{
+       unsigned long pdn = SMMU_ADDR_TO_PDN(page_addr);
+       unsigned long *pdir = (unsigned long *)kmap(as->pdir_page);
+
+       if (pdir[pdn] != _PDE_VACANT(pdn)) {
+               ClearPageReserved(SMMU_EX_PTBL_PAGE(pdir[pdn]));
+               __free_page(SMMU_EX_PTBL_PAGE(pdir[pdn]));
+               pdir[pdn] = _PDE_VACANT(pdn);
+               FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]);
+       }
+       kunmap(as->pdir_page);
+}
+
+static void free_pdir(struct smmu_as *as)
+{
+       if (as->pdir_page) {
+               unsigned addr = as->smmu->iovmm_base;
+               int count = as->smmu->page_count;
+
+               while (count-- > 0) {
+                       free_ptbl(as, addr);
+                       addr += SMMU_PAGE_SIZE * SMMU_PTBL_COUNT;
+               }
+               ClearPageReserved(as->pdir_page);
+               __free_page(as->pdir_page);
+               as->pdir_page = NULL;
+               kfree(as->pte_count);
+               as->pte_count = NULL;
+       }
+}
+
+static int smmu_remove(struct platform_device *pdev)
+{
+       struct smmu_device *smmu = platform_get_drvdata(pdev);
+
+       if (!smmu)
+               return 0;
+
+       if (smmu->enable) {
+               writel(MC_SMMU_CONFIG_0_SMMU_ENABLE_DISABLE,
+                       smmu->regs + MC_SMMU_CONFIG_0);
+               smmu->enable = 0;
+       }
+       platform_set_drvdata(pdev, NULL);
+
+       if (smmu->as) {
+               int asid;
+
+               for (asid = 0; asid < smmu->num_ases; asid++)
+                       free_pdir(&smmu->as[asid]);
+               kfree(smmu->as);
+       }
+
+       if (smmu->regs)
+               iounmap(smmu->regs);
+       tegra_iovmm_unregister(&smmu->iovmm_dev);
+       kfree(smmu);
+       return 0;
+}
+
+//
+// Maps PTBL for given page_addr and returns the PTE address
+// Caller must unmap the mapped PTBL returned in *ptbl_page_p
+//
+static smmu_pte_t *locate_pte(struct smmu_as *as,
+               unsigned long page_addr, bool allocate,
+               struct page **ptbl_page_p,
+               unsigned int **pte_counter)
+{
+       unsigned long ptn = SMMU_ADDR_TO_PFN(page_addr);
+       unsigned long pdn = SMMU_ADDR_TO_PDN(page_addr);
+       smmu_pde_t *pdir = kmap(as->pdir_page);
+       smmu_pte_t *ptbl;
+
+       if (pdir[pdn] != _PDE_VACANT(pdn)) {
+               // Mapped entry table already exists
+               *ptbl_page_p = SMMU_EX_PTBL_PAGE(pdir[pdn]);
+               ptbl = kmap(*ptbl_page_p);
+       } else if (!allocate) {
+               kunmap(as->pdir_page);
+               return NULL;
+       } else {
+               // Vacant - allocate a new page table
+               *ptbl_page_p = alloc_page(GFP_KERNEL|__GFP_DMA);
+               if (!*ptbl_page_p) {
+                       kunmap(as->pdir_page);
+                       pr_err(DRIVER_NAME
+                       ": failed to allocate tegra_iovmm_device page table\n");
+                       return NULL;
+               }
+               SetPageReserved(*ptbl_page_p);
+               ptbl = (unsigned long *)kmap(*ptbl_page_p);
+               {
+                       int pn;
+                       unsigned long addr = SMMU_PDN_TO_ADDR(pdn);
+                       for (pn = 0; pn < SMMU_PTBL_COUNT;
+                               pn++, addr += SMMU_PAGE_SIZE) {
+                               ptbl[pn] = _PTE_VACANT(addr);
+                       }
+               }
+               FLUSH_CPU_DCACHE(ptbl, *ptbl_page_p, SMMU_PTBL_SIZE);
+               pdir[pdn] = SMMU_MK_PDE(*ptbl_page_p, _PDE_ATTR_N);
+               FLUSH_CPU_DCACHE(&pdir[pdn], as->pdir_page, sizeof pdir[pdn]);
+       }
+       *pte_counter = &as->pte_count[pdn];
+
+       kunmap(as->pdir_page);
+       return &ptbl[ptn % SMMU_PTBL_COUNT];
+}
+
+static void flush_tlb_and_ptc(struct smmu_device *smmu,
+               struct smmu_as *as, unsigned long iova,
+               smmu_pte_t *pte, struct page *ptpage)
+{
+       writel(MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_MATCH_GROUP |
+               ((iova & MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_GROUP_mask) >>
+                       MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_VA_GROUP_shift) |
+               MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_MATCH_enable |
+               (as->asid << MC_SMMU_TLB_FLUSH_0_TLB_FLUSH_ASID_SHIFT),
+               smmu->regs + MC_SMMU_TLB_FLUSH_0);
+       writel(MC_SMMU_PTC_FLUSH_0_PTC_FLUSH_TYPE_ADR |
+               VA_PAGE_TO_PA(pte, ptpage),
+               smmu->regs + MC_SMMU_PTC_FLUSH_0);
+       FLUSH_SMMU_REGS(smmu);
+}
+
+static int smmu_map(struct tegra_iovmm_domain *domain,
+               struct tegra_iovmm_area *iovma)
+{
+       struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+       unsigned long addr = iovma->iovm_start;
+       unsigned long pcount = iovma->iovm_length >> SMMU_PAGE_SHIFT;
+       int i;
+
+       for (i = 0; i < pcount; i++) {
+               unsigned long pfn;
+               smmu_pte_t *pte;
+               unsigned int *pte_counter;
+               struct page *ptpage;
+
+               pfn = iovma->ops->lock_makeresident(iovma, i<<PAGE_SHIFT);
+               if (!pfn_valid(pfn))
+                       goto fail;
+
+               down(&as->sem);
+
+               if (!(pte = locate_pte(as, addr, true, &ptpage, &pte_counter)))
+                       goto fail2;
+
+               if (*pte == _PTE_VACANT(addr))
+                       (*pte_counter)++;
+               *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr);
+               if (unlikely((*pte == _PTE_VACANT(addr))))
+                       (*pte_counter)--;
+               FLUSH_CPU_DCACHE(pte, ptpage, sizeof *pte);
+               kunmap(ptpage);
+               up(&as->sem);
+               flush_tlb_and_ptc(as->smmu, as, addr, pte, ptpage);
+               addr += SMMU_PAGE_SIZE;
+       }
+       return 0;
+
+fail:
+       down(&as->sem);
+fail2:
+
+       while (i-- > 0) {
+               smmu_pte_t *pte;
+               unsigned int *pte_counter;
+               struct page *page;
+
+               iovma->ops->release(iovma, i<<PAGE_SHIFT);
+               addr -= SMMU_PAGE_SIZE;
+               if ((pte = locate_pte(as, addr, false, &page, &pte_counter))) {
+                       if (*pte != _PTE_VACANT(addr)) {
+                               *pte = _PTE_VACANT(addr);
+                               FLUSH_CPU_DCACHE(pte, page, sizeof *pte);
+                               kunmap(page);
+                               if (!--(*pte_counter))
+                                       free_ptbl(as, addr);
+                       } else {
+                               kunmap(page);
+                       }
+               }
+       }
+       up(&as->sem);
+       return -ENOMEM;
+}
+
+static void smmu_unmap(struct tegra_iovmm_domain *domain,
+       struct tegra_iovmm_area *iovma, bool decommit)
+{
+       struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+       unsigned long addr = iovma->iovm_start;
+       unsigned int pcount = iovma->iovm_length >> SMMU_PAGE_SHIFT;
+       unsigned int i, *pte_counter;
+
+       down(&as->sem);
+       for (i = 0; i < pcount; i++) {
+               unsigned long *pte;
+               struct page *page;
+
+               if (iovma->ops && iovma->ops->release)
+                       iovma->ops->release(iovma, i<<PAGE_SHIFT);
+
+               if ((pte = locate_pte(as, addr, false, &page, &pte_counter))) {
+                       if (*pte != _PTE_VACANT(addr)) {
+                               *pte = _PTE_VACANT(addr);
+                               FLUSH_CPU_DCACHE(pte, page, sizeof *pte);
+                               kunmap(page);
+                               if (!--(*pte_counter) && decommit) {
+                                       free_ptbl(as, addr);
+                                       smmu_flush_regs(as->smmu, 0);
+                               }
+                       }
+               }
+               addr += SMMU_PAGE_SIZE;
+       }
+       up(&as->sem);
+}
+
+static void smmu_map_pfn(struct tegra_iovmm_domain *domain,
+       struct tegra_iovmm_area *iovma, tegra_iovmm_addr_t addr,
+       unsigned long pfn)
+{
+       struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+       struct smmu_device *smmu = as->smmu;
+       smmu_pte_t *pte;
+       unsigned int *pte_counter;
+       struct page *ptpage;
+
+       BUG_ON(!pfn_valid(pfn));
+       down(&as->sem);
+       if ((pte = locate_pte(as, addr, true, &ptpage, &pte_counter))) {
+               if (*pte == _PTE_VACANT(addr))
+                       (*pte_counter)++;
+               *pte = SMMU_PFN_TO_PTE(pfn, as->pte_attr);
+               if (unlikely((*pte == _PTE_VACANT(addr))))
+                       (*pte_counter)--;
+               FLUSH_CPU_DCACHE(pte, ptpage, sizeof *pte);
+               wmb();
+
+               kunmap(ptpage);
+               flush_tlb_and_ptc(smmu, as, addr, pte, ptpage);
+       }
+       up(&as->sem);
+}
+
+//
+// Caller must lock/unlock as
+//
+static int alloc_pdir(struct smmu_as *as)
+{
+       unsigned long *pdir;
+       int pdn;
+
+       if (as->pdir_page)
+               return 0;
+
+       as->pte_count = kzalloc(sizeof(as->pte_count[0]) * SMMU_PDIR_COUNT,
+                               GFP_KERNEL);
+       if (!as->pte_count) {
+               pr_err(DRIVER_NAME
+               ": failed to allocate tegra_iovmm_device PTE cunters\n");
+               return -ENOMEM;
+       }
+       as->pdir_page = alloc_page(GFP_KERNEL|__GFP_DMA);
+       if (!as->pdir_page) {
+               pr_err(DRIVER_NAME
+               ": failed to allocate tegra_iovmm_device page directory\n");
+               kfree(as->pte_count);
+               as->pte_count = NULL;
+               return -ENOMEM;
+       }
+       SetPageReserved(as->pdir_page);
+       pdir = kmap(as->pdir_page);
+
+       for (pdn = 0; pdn < SMMU_PDIR_COUNT; pdn++)
+               pdir[pdn] = _PDE_VACANT(pdn);
+       FLUSH_CPU_DCACHE(pdir, as->pdir_page, SMMU_PDIR_SIZE);
+       kunmap(as->pdir_page);
+
+       return 0;
+}
+
+static void _sysfs_create(struct smmu_as *as, struct device *sysfs_parent);
+
+//
+// Allocate resources for an AS
+//     TODO: split into "alloc" and "lock"
+//
+static struct tegra_iovmm_domain *smmu_alloc_domain(
+       struct tegra_iovmm_device *dev, struct tegra_iovmm_client *client)
+{
+       struct smmu_device *smmu =
+               container_of(dev, struct smmu_device, iovmm_dev);
+       struct smmu_as *as = NULL;
+       const struct domain_hwc_map *map = NULL;
+       int asid, i;
+
+       // Look for a free AS
+       for  (asid = smmu->lowest_asid; asid < smmu->num_ases; asid++) {
+               down(&smmu->as[asid].sem);
+               if (!smmu->as[asid].hwclients) {
+                       as = &smmu->as[asid];
+                       break;
+               }
+               up(&smmu->as[asid].sem);
+       }
+
+       if (!as) {
+               pr_err(DRIVER_NAME ": no free AS\n");
+               return NULL;
+       }
+
+       if (alloc_pdir(as) < 0)
+               goto bad3;
+
+       // Look for a matching hardware client group
+       for (i = 0; ARRAY_SIZE(smmu_hwc_map); i++) {
+               if (!strcmp(smmu_hwc_map[i].dev_name, client->misc_dev->name)) {
+                       map = &smmu_hwc_map[i];
+                       break;
+               }
+       }
+
+       if (!map) {
+               pr_err(DRIVER_NAME ": no SMMU resource for %s (%s)\n",
+                       client->name, client->misc_dev->name);
+               goto bad2;
+       }
+
+       spin_lock(&smmu->lock);
+       // Update PDIR register
+       writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(as->asid),
+               as->smmu->regs + MC_SMMU_PTB_ASID_0);
+       writel(SMMU_MK_PDIR(as->pdir_page, _PDIR_ATTR),
+               as->smmu->regs + MC_SMMU_PTB_DATA_0);
+       FLUSH_SMMU_REGS(smmu);
+
+       // Put each hardware client in the group into the address space
+       for (i = 0; i < map->nr_hwcs; i++) {
+               struct smmu_hwc_state *hwcst = &smmu->hwc_state[map->hwcs[i]];
+
+               // Is the hardware client busy?
+               if (hwcst->enable_disable != SMMU_ASID_DISABLE &&
+                       hwcst->enable_disable != SMMU_ASID_ENABLE(as->asid)) {
+                       pr_err(DRIVER_NAME
+                               ": HW 0x%lx busy for ASID %ld (client!=%s)\n",
+                               hwcst->reg,
+                               SMMU_ASID_ASID(hwcst->enable_disable),
+                               client->name);
+                       goto bad;
+               }
+               hwcst->enable_disable = SMMU_ASID_ENABLE(as->asid);
+               writel(hwcst->enable_disable, smmu->regs + hwcst->reg);
+       }
+       FLUSH_SMMU_REGS(smmu);
+       spin_unlock(&smmu->lock);
+
+       as->hwclients = map;
+       _sysfs_create(as, client->misc_dev->this_device);
+       up(&as->sem);
+       return &as->domain;
+
+bad:
+       // Reset hardware clients that have been enabled
+       while (--i >= 0) {
+               struct smmu_hwc_state *hwcst = &smmu->hwc_state[map->hwcs[i]];
+
+               hwcst->enable_disable = SMMU_ASID_DISABLE;
+               writel(hwcst->enable_disable, smmu->regs + hwcst->reg);
+       }
+       FLUSH_SMMU_REGS(smmu);
+       spin_unlock(&as->smmu->lock);
+bad2:
+       free_pdir(as);
+bad3:
+       up(&as->sem);
+       return NULL;
+
+}
+
+//
+// Release resources for an AS
+//     TODO: split into "unlock" and "free"
+//
+static void smmu_free_domain(
+       struct tegra_iovmm_domain *domain, struct tegra_iovmm_client *client)
+{
+       struct smmu_as *as = container_of(domain, struct smmu_as, domain);
+       struct smmu_device *smmu = as->smmu;
+       const struct domain_hwc_map *map = NULL;
+       int i;
+
+       down(&as->sem);
+       map = as->hwclients;
+
+       spin_lock(&smmu->lock);
+       for (i = 0; i < map->nr_hwcs; i++) {
+               struct smmu_hwc_state *hwcst = &smmu->hwc_state[map->hwcs[i]];
+
+               hwcst->enable_disable = SMMU_ASID_DISABLE;
+               writel(SMMU_ASID_DISABLE, smmu->regs + hwcst->reg);
+       }
+       FLUSH_SMMU_REGS(smmu);
+       spin_unlock(&smmu->lock);
+
+       as->hwclients = NULL;
+       if (as->pdir_page) {
+               spin_lock(&smmu->lock);
+               writel(MC_SMMU_PTB_ASID_0_CURRENT_ASID(as->asid),
+                       smmu->regs + MC_SMMU_PTB_ASID_0);
+               writel(MC_SMMU_PTB_DATA_0_RESET_VAL,
+                       smmu->regs + MC_SMMU_PTB_DATA_0);
+               FLUSH_SMMU_REGS(smmu);
+               spin_unlock(&smmu->lock);
+
+               free_pdir(as);
+       }
+       up(&as->sem);
+}
+
+static struct tegra_iovmm_device_ops tegra_iovmm_smmu_ops = {
+       .map = smmu_map,
+       .unmap = smmu_unmap,
+       .map_pfn = smmu_map_pfn,
+       .alloc_domain = smmu_alloc_domain,
+       .free_domain = smmu_free_domain,
+       .suspend = smmu_suspend,
+       .resume = smmu_resume,
+};
+
+static int smmu_probe(struct platform_device *pdev)
+{
+       struct smmu_device *smmu = NULL;
+       struct resource *regs = NULL, *window = NULL;
+       int e, asid;
+
+       if (!pdev) {
+               pr_err(DRIVER_NAME ": platform_device required\n");
+               return -ENODEV;
+       }
+
+       if (PAGE_SHIFT != SMMU_PAGE_SHIFT) {
+               pr_err(DRIVER_NAME ": SMMU and CPU page sizes must match\n");
+               return -ENXIO;
+       }
+
+       if (ARRAY_SIZE(smmu_hwc_state_init) != HWC_COUNT) {
+               pr_err(DRIVER_NAME
+                       ": sizeof smmu_hwc_state_init != enum smmu_hwclient\n");
+               return -ENXIO;
+       }
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       window = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+
+       if (!regs || !window) {
+               pr_err(DRIVER_NAME ": No SMMU resources\n");
+               return -ENODEV;
+       }
+       smmu = kzalloc(sizeof(*smmu), GFP_KERNEL);
+       if (!smmu) {
+               pr_err(DRIVER_NAME ": failed to allocate smmu_device\n");
+               return -ENOMEM;
+       }
+
+       smmu->num_ases = MC_SMMU_NUM_ASIDS;
+       smmu->iovmm_base = (tegra_iovmm_addr_t)window->start;
+       smmu->page_count = (window->end + 1 - window->start) >> SMMU_PAGE_SHIFT;
+       smmu->regs = ioremap(regs->start, regs->end + 1 - regs->start);
+       if (!smmu->regs) {
+               pr_err(DRIVER_NAME ": failed to remap SMMU registers\n");
+               e = -ENXIO;
+               goto fail;
+       }
+
+       smmu->translation_enable_0_0 = ~0;
+       smmu->translation_enable_1_0 = ~0;
+       smmu->translation_enable_2_0 = ~0;
+       smmu->asid_security_0        = 0;
+
+       memcpy(smmu->hwc_state, smmu_hwc_state_init, sizeof(smmu->hwc_state));
+
+       smmu->iovmm_dev.name = VMM_NAME;
+       smmu->iovmm_dev.ops = &tegra_iovmm_smmu_ops;
+       smmu->iovmm_dev.pgsize_bits = SMMU_PAGE_SHIFT;
+
+       e = tegra_iovmm_register(&smmu->iovmm_dev);
+       if (e)
+               goto fail;
+
+       smmu->as = kzalloc(sizeof(smmu->as[0]) * smmu->num_ases, GFP_KERNEL);
+       if (!smmu->as) {
+               pr_err(DRIVER_NAME ": failed to allocate smmu_as\n");
+               e = -ENOMEM;
+               goto fail;
+       }
+
+       // Initialize address space structure array
+       for (asid = 0; asid < smmu->num_ases; asid++) {
+               struct smmu_as *as = &smmu->as[asid];
+
+               as->smmu = smmu;
+               as->asid = asid;
+               as->pte_attr = _PTE_ATTR;       // Default attributes
+
+               sema_init(&as->sem, 1);
+
+               e = tegra_iovmm_domain_init(&as->domain, &smmu->iovmm_dev,
+                       smmu->iovmm_base,
+                       smmu->iovmm_base +
+                               (smmu->page_count << SMMU_PAGE_SHIFT));
+               if (e)
+                       goto fail;
+       }
+       spin_lock_init(&smmu->lock);
+       smmu_setup_regs(smmu);
+       smmu->enable = 1;
+       platform_set_drvdata(pdev, smmu);
+       return 0;
+
+fail:
+       if (smmu->regs)
+               iounmap(smmu->regs);
+       if (smmu && smmu->as) {
+               for (asid = 0; asid < smmu->num_ases; asid++) {
+                       if (smmu->as[asid].pdir_page) {
+                               ClearPageReserved(smmu->as[asid].pdir_page);
+                               __free_page(smmu->as[asid].pdir_page);
+                       }
+               }
+               kfree(smmu->as);
+       }
+       kfree(smmu);
+       return e;
+}
+
+static struct platform_driver tegra_iovmm_smmu_drv = {
+       .probe = smmu_probe,
+       .remove = smmu_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+       },
+};
+
+static int __devinit smmu_init(void)
+{
+       return platform_driver_register(&tegra_iovmm_smmu_drv);
+}
+
+static void __exit smmu_exit(void)
+{
+       return platform_driver_unregister(&tegra_iovmm_smmu_drv);
+}
+
+subsys_initcall(smmu_init);
+module_exit(smmu_exit);
+
+#ifdef SMMU_SYSFS
+//
+// SMMU-global sysfs interface for debugging
+//
+static ssize_t _sysfs_show_reg(struct device *d,
+                               struct device_attribute *da, char *buf);
+static ssize_t _sysfs_store_reg(struct device *d,
+                               struct device_attribute *da, const char *buf,
+                               size_t count);
+
+#define _NAME_MAP(_name)       {       \
+       .name = __stringify(_name),     \
+       .offset = _name##_0,            \
+       .dev_attr = __ATTR(_name, S_IRUGO|S_IWUSR,      \
+                       _sysfs_show_reg, _sysfs_store_reg)      \
+}
+
+static
+struct _reg_name_map {
+       const char *name;
+       unsigned        offset;
+       struct device_attribute dev_attr;
+} _smmu_reg_name_map[] = {
+       _NAME_MAP(MC_SMMU_CONFIG),
+       _NAME_MAP(MC_SMMU_TLB_CONFIG),
+       _NAME_MAP(MC_SMMU_PTC_CONFIG),
+       _NAME_MAP(MC_SMMU_PTB_ASID),
+       _NAME_MAP(MC_SMMU_PTB_DATA),
+       _NAME_MAP(MC_SMMU_TLB_FLUSH),
+       _NAME_MAP(MC_SMMU_PTC_FLUSH),
+       _NAME_MAP(MC_SMMU_ASID_SECURITY),
+       _NAME_MAP(MC_SMMU_STATS_TLB_HIT_COUNT),
+       _NAME_MAP(MC_SMMU_STATS_TLB_MISS_COUNT),
+       _NAME_MAP(MC_SMMU_STATS_PTC_HIT_COUNT),
+       _NAME_MAP(MC_SMMU_STATS_PTC_MISS_COUNT),
+       _NAME_MAP(MC_SMMU_TRANSLATION_ENABLE_0),
+       _NAME_MAP(MC_SMMU_TRANSLATION_ENABLE_1),
+       _NAME_MAP(MC_SMMU_TRANSLATION_ENABLE_2),
+       _NAME_MAP(MC_SMMU_AFI_ASID),
+       _NAME_MAP(MC_SMMU_AVPC_ASID),
+       _NAME_MAP(MC_SMMU_DC_ASID),
+       _NAME_MAP(MC_SMMU_DCB_ASID),
+       _NAME_MAP(MC_SMMU_EPP_ASID),
+       _NAME_MAP(MC_SMMU_G2_ASID),
+       _NAME_MAP(MC_SMMU_HC_ASID),
+       _NAME_MAP(MC_SMMU_HDA_ASID),
+       _NAME_MAP(MC_SMMU_ISP_ASID),
+       _NAME_MAP(MC_SMMU_MPE_ASID),
+       _NAME_MAP(MC_SMMU_NV_ASID),
+       _NAME_MAP(MC_SMMU_NV2_ASID),
+       _NAME_MAP(MC_SMMU_PPCS_ASID),
+       _NAME_MAP(MC_SMMU_SATA_ASID),
+       _NAME_MAP(MC_SMMU_VDE_ASID),
+       _NAME_MAP(MC_SMMU_VI_ASID),
+};
+
+static struct attribute *_smmu_attrs[ARRAY_SIZE(_smmu_reg_name_map) + 3];
+static struct attribute_group _smmu_attr_group = {
+       .attrs = _smmu_attrs
+};
+
+static ssize_t lookup_reg(struct device_attribute *da)
+{
+       int i;
+       for (i = 0; i < ARRAY_SIZE(_smmu_reg_name_map); i++) {
+               if (!strcmp(_smmu_reg_name_map[i].name, da->attr.name))
+                       return _smmu_reg_name_map[i].offset;
+       }
+       return -ENODEV;
+}
+
+static ssize_t _sysfs_show_reg(struct device *d,
+                                       struct device_attribute *da, char *buf)
+{
+       struct smmu_device *smmu =
+               container_of(d, struct smmu_device, sysfs_dev);
+       ssize_t offset = lookup_reg(da);
+
+       if (offset < 0)
+               return offset;
+       return sprintf(buf, "%08lx\n",
+               (unsigned long)readl(smmu->regs + offset));
+}
+
+static ssize_t _sysfs_store_reg(struct device *d,
+                       struct device_attribute *da,
+                       const char *buf, size_t count)
+{
+       struct smmu_device *smmu =
+               container_of(d, struct smmu_device, sysfs_dev);
+       ssize_t offset = lookup_reg(da);
+       unsigned long value;
+
+       if (offset < 0)
+               return offset;
+       value = simple_strtoul(buf, NULL, 16);
+       writel(value, smmu->regs + offset);
+       return count;
+}
+
+static ssize_t _sysfs_show_smmu(struct device *d,
+                               struct device_attribute *da, char *buf)
+{
+       struct smmu_device *smmu =
+               container_of(d, struct smmu_device, sysfs_dev);
+       ssize_t rv = 0;
+
+       rv += sprintf(buf + rv , "      regs: %p\n", smmu->regs);
+       rv += sprintf(buf + rv , "iovmm_base: %p\n", (void *)smmu->iovmm_base);
+       rv += sprintf(buf + rv , "page_count: %lx\n", smmu->page_count);
+       rv += sprintf(buf + rv , "  num_ases: %d\n", smmu->num_ases);
+       rv += sprintf(buf + rv , "        as: %p\n", smmu->as);
+       rv += sprintf(buf + rv , "    enable: %s\n",
+                       smmu->enable ? "yes" : "no");
+       return rv;
+}
+
+static struct device_attribute _attr_show_smmu
+                = __ATTR(show_smmu, S_IRUGO, _sysfs_show_smmu, NULL);
+
+static ssize_t _sysfs_show_lowest_asid(struct device *d,
+                               struct device_attribute *da, char *buf)
+{
+       struct smmu_device *smmu =
+               container_of(d, struct smmu_device, sysfs_dev);
+       ssize_t rv = 0;
+
+       rv += sprintf(buf + rv, "%d\n", smmu->lowest_asid);
+       return rv;
+}
+
+static ssize_t _sysfs_set_lowest_asid(struct device *d,
+                               struct device_attribute *da,
+                               const char *buf, int count)
+{
+       struct smmu_device *smmu =
+               container_of(d, struct smmu_device, sysfs_dev);
+       int value = simple_strtoul(buf, NULL, 10);
+       if (0 <= value && value < MC_SMMU_NUM_ASIDS)
+               smmu->lowest_asid = value;
+       return count;
+}
+
+static struct device_attribute _attr_lowest_asid
+                = __ATTR(lowest_asid, S_IRUGO|S_IWUSR, _sysfs_show_lowest_asid,
+                       _sysfs_set_lowest_asid);
+
+static void _sysfs_smmu(struct smmu_device *smmu, struct device *parent)
+{
+       int i;
+
+       if (smmu->sysfs_use_count++ > 0)
+               return;
+       for (i = 0; i < ARRAY_SIZE(_smmu_reg_name_map); i++)
+               _smmu_attrs[i] = &_smmu_reg_name_map[i].dev_attr.attr;
+       _smmu_attrs[i++] = &_attr_show_smmu.attr;
+       _smmu_attrs[i++] = &_attr_lowest_asid.attr;
+       _smmu_attrs[ARRAY_SIZE(_smmu_attrs) - 1] = NULL;
+
+       dev_set_name(&smmu->sysfs_dev, "smmu");
+       smmu->sysfs_dev.parent = parent;
+       smmu->sysfs_dev.driver = NULL;
+       smmu->sysfs_dev.release = NULL;
+       if (device_register(&smmu->sysfs_dev)) {
+               pr_err("%s: failed to register smmu_sysfs_dev\n", __func__);
+               smmu->sysfs_use_count--;
+               return;
+       }
+       if (sysfs_create_group(&smmu->sysfs_dev.kobj, &_smmu_attr_group)) {
+               pr_err("%s: failed to create group for smmu_sysfs_dev\n",
+                       __func__);
+               smmu->sysfs_use_count--;
+               return;
+       }
+}
+#endif
+
+static void _sysfs_create(struct smmu_as *as, struct device *parent)
+{
+#ifdef SMMU_SYSFS
+       _sysfs_smmu(as->smmu, parent);
+#endif
+}
index 7a634dd..d492811 100644 (file)
@@ -282,11 +282,11 @@ static struct tegra_iovmm_block *iovmm_alloc_block(
 {
        struct rb_node *n;
        struct tegra_iovmm_block *b, *best;
-        static int splitting = 0;
+       static int splitting = 0;
 
        BUG_ON(!size);
        size = iovmm_align_up(domain->dev, size);
-        for (;;) {
+       for (;;) {
                spin_lock(&domain->block_lock);
                if (!splitting)
                        break;
@@ -297,8 +297,9 @@ static struct tegra_iovmm_block *iovmm_alloc_block(
        best = NULL;
        while (n) {
                b = rb_entry(n, struct tegra_iovmm_block, free_node);
-               if (iovmm_length(b) < size) n = n->rb_right;
-               else if (iovmm_length(b) == size) {
+               if (iovmm_length(b) < size) {
+                       n = n->rb_right;
+               } else if (iovmm_length(b) == size) {
                        best = b;
                        break;
                } else {
@@ -356,16 +357,16 @@ struct tegra_iovmm_area *tegra_iovmm_create_vm(
        unsigned long size, pgprot_t pgprot)
 {
        struct tegra_iovmm_block *b;
-       struct tegra_iovmm_device *dev;
+       struct tegra_iovmm_domain *domain;
 
        if (!client) return NULL;
 
-       dev = client->domain->dev;
+       domain = client->domain;
 
-       b = iovmm_alloc_block(client->domain, size);
+       b = iovmm_alloc_block(domain, size);
        if (!b) return NULL;
 
-       b->vm_area.domain = client->domain;
+       b->vm_area.domain = domain;
        b->vm_area.pgprot = pgprot;
        b->vm_area.ops = ops;
 
@@ -374,7 +375,7 @@ struct tegra_iovmm_area *tegra_iovmm_create_vm(
                set_bit(BK_map_dirty, &b->flags);
                set_bit(DM_map_dirty, &client->domain->flags);
        } else if (ops) {
-               if (dev->ops->map(dev, &b->vm_area))
+               if (domain->dev->ops->map(domain, &b->vm_area))
                        pr_err("%s failed to map locked domain\n", __func__);
        }
        up_read(&b->vm_area.domain->map_lock);
@@ -382,68 +383,66 @@ struct tegra_iovmm_area *tegra_iovmm_create_vm(
        return &b->vm_area;
 }
 
-void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *area,
+void tegra_iovmm_vm_insert_pfn(struct tegra_iovmm_area *vm,
        tegra_iovmm_addr_t vaddr, unsigned long pfn)
 {
-       struct tegra_iovmm_device *dev = area->domain->dev;
-       BUG_ON(vaddr & ((1<<dev->pgsize_bits)-1));
-       BUG_ON(vaddr >= area->iovm_start + area->iovm_length);
-       BUG_ON(vaddr < area->iovm_start);
-       BUG_ON(area->ops);
+       struct tegra_iovmm_domain *domain = vm->domain;
+       BUG_ON(vaddr & ((1<<domain->dev->pgsize_bits)-1));
+       BUG_ON(vaddr >= vm->iovm_start + vm->iovm_length);
+       BUG_ON(vaddr < vm->iovm_start);
+       BUG_ON(vm->ops);
 
-       dev->ops->map_pfn(dev, area, vaddr, pfn);
+       domain->dev->ops->map_pfn(domain, vm, vaddr, pfn);
 }
 
 void tegra_iovmm_zap_vm(struct tegra_iovmm_area *vm)
 {
        struct tegra_iovmm_block *b;
-       struct tegra_iovmm_device *dev;
+       struct tegra_iovmm_domain *domain;
 
        b = container_of(vm, struct tegra_iovmm_block, vm_area);
-       dev = vm->domain->dev;
+       domain = vm->domain;
        /* if the vm area mapping was deferred, don't unmap it since
         * the memory for the page tables it uses may not be allocated */
-       down_read(&vm->domain->map_lock);
+       down_read(&domain->map_lock);
        if (!test_and_clear_bit(BK_map_dirty, &b->flags))
-               dev->ops->unmap(dev, vm, false);
-       up_read(&vm->domain->map_lock);
+               domain->dev->ops->unmap(domain, vm, false);
+       up_read(&domain->map_lock);
 }
 
 void tegra_iovmm_unzap_vm(struct tegra_iovmm_area *vm)
 {
        struct tegra_iovmm_block *b;
-       struct tegra_iovmm_device *dev;
+       struct tegra_iovmm_domain *domain;
 
        b = container_of(vm, struct tegra_iovmm_block, vm_area);
-       dev = vm->domain->dev;
+       domain = vm->domain;
        if (!vm->ops) return;
 
-       down_read(&vm->domain->map_lock);
+       down_read(&domain->map_lock);
        if (vm->ops) {
-               if (atomic_read(&vm->domain->locks))
-                       dev->ops->map(dev, vm);
+               if (atomic_read(&domain->locks))
+                       domain->dev->ops->map(domain, vm);
                else {
                        set_bit(BK_map_dirty, &b->flags);
-                       set_bit(DM_map_dirty, &vm->domain->flags);
+                       set_bit(DM_map_dirty, &domain->flags);
                }
-        }
-       up_read(&vm->domain->map_lock);
+       }
+       up_read(&domain->map_lock);
 }
 
 void tegra_iovmm_free_vm(struct tegra_iovmm_area *vm)
 {
        struct tegra_iovmm_block *b;
-       struct tegra_iovmm_device *dev;
        struct tegra_iovmm_domain *domain;
 
        if (!vm) return;
 
        b = container_of(vm, struct tegra_iovmm_block, vm_area);
        domain = vm->domain;
-       dev = vm->domain->dev;
        down_read(&domain->map_lock);
        if (!test_and_clear_bit(BK_map_dirty, &b->flags))
-               dev->ops->unmap(dev, vm, true);
+               domain->dev->ops->unmap(domain, vm, true);
        iovmm_free_block(domain, b);
        up_read(&domain->map_lock);
 }
@@ -515,7 +514,7 @@ static int _iovmm_client_lock(struct tegra_iovmm_client *client)
        /* if the device doesn't export the lock_domain function, the device
         * must guarantee that any valid domain will be locked. */
        if (v==1 && dev->ops->lock_domain) {
-               if (dev->ops->lock_domain(dev, domain)) {
+               if (dev->ops->lock_domain(domain, client)) {
                        atomic_dec(&domain->locks);
                        up_write(&domain->map_lock);
                        return -EAGAIN;
@@ -538,7 +537,7 @@ static int _iovmm_client_lock(struct tegra_iovmm_client *client)
                                        pr_err("%s: vm_area ops must exist for lazy maps\n", __func__);
                                        continue;
                                }
-                               dev->ops->map(dev, &b->vm_area);
+                               dev->ops->map(domain, &b->vm_area);
                        }
                }
        }
@@ -582,9 +581,9 @@ void tegra_iovmm_client_unlock(struct tegra_iovmm_client *client)
        domain = client->domain;
        dev = domain->dev;
         down_write(&domain->map_lock);
-       if (!atomic_dec_return(&client->domain->locks)) {
+       if (!atomic_dec_return(&domain->locks)) {
                if (dev->ops->unlock_domain)
-                       dev->ops->unlock_domain(dev, domain);
+                       dev->ops->unlock_domain(domain, client);
                do_wake = 1;
        }
        up_write(&domain->map_lock);
@@ -617,26 +616,28 @@ size_t tegra_iovmm_get_vm_size(struct tegra_iovmm_client *client)
 void tegra_iovmm_free_client(struct tegra_iovmm_client *client)
 {
        struct tegra_iovmm_device *dev;
+       struct tegra_iovmm_domain *domain;
        if (!client) return;
 
        BUG_ON(!client->domain || !client->domain->dev);
 
-       dev = client->domain->dev;
+       domain = client->domain;
+       dev = domain->dev;
 
        if (test_and_clear_bit(CL_locked, &client->flags)) {
                pr_err("freeing locked client %s\n", client->name);
-               if (!atomic_dec_return(&client->domain->locks)) {
-                       down_write(&client->domain->map_lock);
+               if (!atomic_dec_return(&domain->locks)) {
+                       down_write(&domain->map_lock);
                        if (dev->ops->unlock_domain)
-                               dev->ops->unlock_domain(dev, client->domain);
-                       up_write(&client->domain->map_lock);
-                       wake_up(&client->domain->delay_lock);
+                               dev->ops->unlock_domain(domain, client);
+                       up_write(&domain->map_lock);
+                       wake_up(&domain->delay_lock);
                }
        }
        mutex_lock(&iovmm_group_list_lock);
-       if (!atomic_dec_return(&client->domain->clients))
+       if (!atomic_dec_return(&domain->clients))
                if (dev->ops->free_domain)
-                       dev->ops->free_domain(dev, client->domain);
+                       dev->ops->free_domain(domain, client);
        list_del(&client->list);
        if (list_empty(&client->group->client_list)) {
                list_del(&client->group->group_list);
@@ -649,7 +650,7 @@ void tegra_iovmm_free_client(struct tegra_iovmm_client *client)
 }
 
 struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
-       const char *share_group)
+       const char *share_group, struct miscdevice *misc_dev)
 {
        struct tegra_iovmm_client *c = kzalloc(sizeof(*c), GFP_KERNEL);
        struct iovmm_share_group *grp = NULL;
@@ -659,6 +660,7 @@ struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
        if (!c) return NULL;
        c->name = kstrdup(name, GFP_KERNEL);
        if (!c->name) goto fail;
+       c->misc_dev = misc_dev;
 
        mutex_lock(&iovmm_group_list_lock);
        if (share_group) {
@@ -689,7 +691,7 @@ struct tegra_iovmm_client *tegra_iovmm_alloc_client(const char *name,
                        kfree(grp);
                        grp = NULL;
                        goto fail_lock;
-                }
+               }
                spin_lock_init(&grp->lock);
                INIT_LIST_HEAD(&grp->client_list);
                list_add_tail(&grp->group_list, &iovmm_groups);
index 56c5c07..957338a 100644 (file)
@@ -49,7 +49,7 @@
 #define ICTLR_COP_IER_CLR      0x38
 #define ICTLR_COP_IEP_CLASS    0x3c
 
-#define NUM_ICTLRS 4
+#define NUM_ICTLRS (INT_MAIN_NR/32)
 #define FIRST_LEGACY_IRQ 32
 
 static void __iomem *ictlr_reg_base[] = {
@@ -57,6 +57,9 @@ static void __iomem *ictlr_reg_base[] = {
        IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE),
        IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE),
        IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE),
+#if (NUM_ICTLRS > 4)
+       IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE),
+#endif
 };
 
 #ifdef CONFIG_PM
index 513ac3f..53074ce 100644 (file)
@@ -23,6 +23,7 @@
 #include <mach/iomap.h>
 #include <mach/mc.h>
 
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
 static DEFINE_SPINLOCK(tegra_mc_lock);
 
 void tegra_mc_set_priority(unsigned long client, unsigned long prio)
@@ -39,4 +40,8 @@ void tegra_mc_set_priority(unsigned long client, unsigned long prio)
        val |= prio << field;
        writel(val, mc_base + reg);
        spin_unlock_irqrestore(&tegra_mc_lock, flags);
+
 }
+#elif defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       /* !!!FIXME!!! IMPLEMENT ME */
+#endif
index fd5e34d..a6fcd6c 100644 (file)
@@ -96,6 +96,7 @@ const struct tegra_drive_pingroup_desc tegra_soc_drive_pingroups[TEGRA_MAX_DRIVE
                .mux_bit = mux_b,                               \
                .pupd_reg = pupd_r,                             \
                .pupd_bit = pupd_b,                             \
+               .io_default = 0,                                \
        }
 
 const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
diff --git a/arch/arm/mach-tegra/pinmux-t3-tables.c b/arch/arm/mach-tegra/pinmux-t3-tables.c
new file mode 100644 (file)
index 0000000..798986b
--- /dev/null
@@ -0,0 +1,396 @@
+/*
+ * linux/arch/arm/mach-tegra/pinmux-t3-tables.c
+ *
+ * Common pinmux configurations for Tegra 3 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>
+#include <mach/suspend.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(AT3,             0x878),
+       DRIVE_PINGROUP(AT4,             0x87c),
+       DRIVE_PINGROUP(AT5,             0x880),
+       DRIVE_PINGROUP(CDEV1,           0x884),
+       DRIVE_PINGROUP(CDEV2,           0x888),
+       DRIVE_PINGROUP(CSUS,            0x88c),
+       DRIVE_PINGROUP(DAP1,            0x890),
+       DRIVE_PINGROUP(DAP2,            0x894),
+       DRIVE_PINGROUP(DAP3,            0x898),
+       DRIVE_PINGROUP(DAP4,            0x89c),
+       DRIVE_PINGROUP(DBG,             0x8a0),
+       DRIVE_PINGROUP(LCD1,            0x8a4),
+       DRIVE_PINGROUP(LCD2,            0x8a8),
+       DRIVE_PINGROUP(SDIO2,           0x8ac),
+       DRIVE_PINGROUP(SDIO3,           0x8b0),
+       DRIVE_PINGROUP(SPI,             0x8b4),
+       DRIVE_PINGROUP(UAA,             0x8b8),
+       DRIVE_PINGROUP(UAB,             0x8bc),
+       DRIVE_PINGROUP(UART2,           0x8c0),
+       DRIVE_PINGROUP(UART3,           0x8c4),
+       DRIVE_PINGROUP(VI1,             0x8c8),
+       DRIVE_PINGROUP(SDIO1,           0x8ec),
+       DRIVE_PINGROUP(CRT,             0x8f8),
+       DRIVE_PINGROUP(DDC,             0x8fc),
+       DRIVE_PINGROUP(GMA,             0x900),
+       DRIVE_PINGROUP(GMB,             0x904),
+       DRIVE_PINGROUP(GMC,             0x908),
+       DRIVE_PINGROUP(GMD,             0x90c),
+       DRIVE_PINGROUP(GME,             0x910),
+       DRIVE_PINGROUP(GMF,             0x914),
+       DRIVE_PINGROUP(GMG,             0x918),
+       DRIVE_PINGROUP(GMH,             0x91c),
+       DRIVE_PINGROUP(OWR,             0x920),
+       DRIVE_PINGROUP(UAD,             0x924),
+       DRIVE_PINGROUP(GPV,             0x928),
+       DRIVE_PINGROUP(DEV3,            0x92c),
+       DRIVE_PINGROUP(CEC,             0x938),
+};
+
+#define PINGROUP(pg_name, vdd, f0, f1, f2, f3, fs, iod, reg)   \
+       [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_ ## fs,                  \
+               .tri_reg = reg,                                 \
+               .tri_bit = 4,                                   \
+               .mux_reg = reg,                                 \
+               .mux_bit = 0,                                   \
+               .pupd_reg = reg,                                \
+               .pupd_bit = 2,                                  \
+               .io_default = TEGRA_PIN_ ## iod,                \
+       }
+
+/* !!!FIXME!!! FILL IN fSafe COLUMN IN TABLE ....... */
+const struct tegra_pingroup_desc tegra_soc_pingroups[TEGRA_MAX_PINGROUP] = {
+       /*       NAME             VDD       f0          f1          f2          f3          fSafe       io      reg */
+       PINGROUP(ULPI_DATA0,      BB,       SPI3,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x3000),
+       PINGROUP(ULPI_DATA1,      BB,       SPI3,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x3004),
+       PINGROUP(ULPI_DATA2,      BB,       SPI3,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x3008),
+       PINGROUP(ULPI_DATA3,      BB,       SPI3,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x300c),
+       PINGROUP(ULPI_DATA4,      BB,       SPI2,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x3010),
+       PINGROUP(ULPI_DATA5,      BB,       SPI2,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x3014),
+       PINGROUP(ULPI_DATA6,      BB,       SPI2,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x3018),
+       PINGROUP(ULPI_DATA7,      BB,       SPI2,       HSI,        UARTA,      ULPI,       RSVD,       INPUT,  0x301c),
+       PINGROUP(ULPI_CLK,        BB,       SPI1,       RSVD,       UARTD,      ULPI,       RSVD,       INPUT,  0x3020),
+       PINGROUP(ULPI_DIR,        BB,       SPI1,       RSVD,       UARTD,      ULPI,       RSVD,       INPUT,  0x3024),
+       PINGROUP(ULPI_NXT,        BB,       SPI1,       RSVD,       UARTD,      ULPI,       RSVD,       INPUT,  0x3028),
+       PINGROUP(ULPI_STP,        BB,       SPI1,       RSVD,       UARTD,      ULPI,       RSVD,       INPUT,  0x302c),
+       PINGROUP(DAP3_FS,         BB,       I2S2,       RSVD1,      DISPLAYA,   DISPLAYB,   RSVD,       INPUT,  0x3030),
+       PINGROUP(DAP3_DIN,        BB,       I2S2,       RSVD1,      DISPLAYA,   DISPLAYB,   RSVD,       INPUT,  0x3034),
+       PINGROUP(DAP3_DOUT,       BB,       I2S2,       RSVD1,      DISPLAYA,   DISPLAYB,   RSVD,       INPUT,  0x3038),
+       PINGROUP(DAP3_SCLK,       BB,       I2S2,       RSVD1,      DISPLAYA,   DISPLAYB,   RSVD,       INPUT,  0x303c),
+       PINGROUP(GPIO_PV0,        BB,       RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3040),
+       PINGROUP(GPIO_PV1,        BB,       RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3044),
+       PINGROUP(SDMMC1_CLK,      SDMMC1,   SDMMC1,     RSVD1,      RSVD2,      UARTA,      RSVD,       INPUT,  0x3048),
+       PINGROUP(SDMMC1_CMD,      SDMMC1,   SDMMC1,     RSVD1,      RSVD2,      UARTA,      RSVD,       INPUT,  0x304c),
+       PINGROUP(SDMMC1_DAT3,     SDMMC1,   SDMMC1,     RSVD1,      UARTE,      UARTA,      RSVD,       INPUT,  0x3050),
+       PINGROUP(SDMMC1_DAT2,     SDMMC1,   SDMMC1,     RSVD1,      UARTE,      UARTA,      RSVD,       INPUT,  0x3054),
+       PINGROUP(SDMMC1_DAT1,     SDMMC1,   SDMMC1,     RSVD1,      UARTE,      UARTA,      RSVD,       INPUT,  0x3058),
+       PINGROUP(SDMMC1_DAT0,     SDMMC1,   SDMMC1,     RSVD1,      UARTE,      UARTA,      RSVD,       INPUT,  0x305c),
+       PINGROUP(GPIO_PV2,        SDMMC1,   OWR,        RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3060),
+       PINGROUP(GPIO_PV3,        SDMMC1,   CLK12,      RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3064),
+       PINGROUP(CLK2_OUT,        SDMMC1,   EXTPERIPH2, RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3068),
+       PINGROUP(CLK2_REQ,        SDMMC1,   DAP,        RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x306c),
+       PINGROUP(LCD_PWR1,        LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x3070),
+       PINGROUP(LCD_PWR2,        LCD,      DISPLAYA,   DISPLAYB,   SPI5,       HDMI,       RSVD,       OUTPUT, 0x3074),
+       PINGROUP(LCD_SDIN,        LCD,      DISPLAYA,   DISPLAYB,   SPI5,       RSVD,       RSVD,       OUTPUT, 0x3078),
+       PINGROUP(LCD_SDOUT,       LCD,      DISPLAYA,   DISPLAYB,   SPI5,       HDMI,       RSVD,       OUTPUT, 0x307c),
+       PINGROUP(LCD_WR_N,        LCD,      DISPLAYA,   DISPLAYB,   SPI5,       HDMI,       RSVD,       OUTPUT, 0x3080),
+       PINGROUP(LCD_CS0_N,       LCD,      DISPLAYA,   DISPLAYB,   SPI5,       RSVD,       RSVD,       OUTPUT, 0x3084),
+       PINGROUP(LCD_DC0,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x3088),
+       PINGROUP(LCD_SCK,         LCD,      DISPLAYA,   DISPLAYB,   SPI5,       HDMI,       RSVD,       OUTPUT, 0x308c),
+       PINGROUP(LCD_PWR0,        LCD,      DISPLAYA,   DISPLAYB,   SPI5,       HDMI,       RSVD,       OUTPUT, 0x3090),
+       PINGROUP(LCD_PCLK,        LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x3094),
+       PINGROUP(LCD_DE,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x3098),
+       PINGROUP(LCD_HSYNC,       LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x309c),
+       PINGROUP(LCD_VSYNC,       LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30a0),
+       PINGROUP(LCD_D0,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30a4),
+       PINGROUP(LCD_D1,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30a8),
+       PINGROUP(LCD_D2,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30ac),
+       PINGROUP(LCD_D3,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30b0),
+       PINGROUP(LCD_D4,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30b4),
+       PINGROUP(LCD_D5,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30b8),
+       PINGROUP(LCD_D6,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30bc),
+       PINGROUP(LCD_D7,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30c0),
+       PINGROUP(LCD_D8,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30c4),
+       PINGROUP(LCD_D9,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30c8),
+       PINGROUP(LCD_D10,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30cc),
+       PINGROUP(LCD_D11,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30d0),
+       PINGROUP(LCD_D12,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30d4),
+       PINGROUP(LCD_D13,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30d8),
+       PINGROUP(LCD_D14,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30dc),
+       PINGROUP(LCD_D15,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30e0),
+       PINGROUP(LCD_D16,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30e4),
+       PINGROUP(LCD_D17,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30e8),
+       PINGROUP(LCD_D18,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30ec),
+       PINGROUP(LCD_D19,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30f0),
+       PINGROUP(LCD_D20,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30f4),
+       PINGROUP(LCD_D21,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30f8),
+       PINGROUP(LCD_D22,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x30fc),
+       PINGROUP(LCD_D23,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x3100),
+       PINGROUP(LCD_CS1_N,       LCD,      DISPLAYA,   DISPLAYB,   SPI5,       RSVD2,      RSVD,       OUTPUT, 0x3104),
+       PINGROUP(LCD_M1,          LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x3108),
+       PINGROUP(LCD_DC1,         LCD,      DISPLAYA,   DISPLAYB,   RSVD1,      RSVD2,      RSVD,       OUTPUT, 0x310c),
+       PINGROUP(HDMI_INT,        LCD,      RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3110),
+       PINGROUP(DDC_SCL,         LCD,      I2C4,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3114),
+       PINGROUP(DDC_SDA,         LCD,      I2C4,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3118),
+       PINGROUP(CRT_HSYNC,       LCD,      CRT,        RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x311c),
+       PINGROUP(CRT_VSYNC,       LCD,      CRT,        RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3120),
+       PINGROUP(VI_D0,           VI,       DDR,        RSVD1,      VI,         RSVD2,      RSVD,       INPUT,  0x3124),
+       PINGROUP(VI_D1,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3128),
+       PINGROUP(VI_D2,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x312c),
+       PINGROUP(VI_D3,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3130),
+       PINGROUP(VI_D4,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3134),
+       PINGROUP(VI_D5,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3138),
+       PINGROUP(VI_D6,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x313c),
+       PINGROUP(VI_D7,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3140),
+       PINGROUP(VI_D8,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3144),
+       PINGROUP(VI_D9,           VI,       DDR,        SDMMC2,     VI,         RSVD1,      RSVD,       INPUT,  0x3148),
+       PINGROUP(VI_D10,          VI,       DDR,        RSVD1,      VI,         RSVD2,      RSVD,       INPUT,  0x314c),
+       PINGROUP(VI_D11,          VI,       DDR,        RSVD1,      VI,         RSVD2,      RSVD,       INPUT,  0x3150),
+       PINGROUP(VI_PCLK,         VI,       RSVD1,      SDMMC2,     VI,         RSVD2,      RSVD,       INPUT,  0x3154),
+       PINGROUP(VI_MCLK,         VI,       VI,         VI_ALT1,    VI_ALT2,    VI_ALT3,    RSVD,       INPUT,  0x3158),
+       PINGROUP(VI_VSYNC,        VI,       DDR,        RSVD1,      VI,         RSVD2,      RSVD,       INPUT,  0x315c),
+       PINGROUP(VI_HSYNC,        VI,       DDR,        RSVD1,      VI,         RSVD2,      RSVD,       INPUT,  0x3160),
+       PINGROUP(UART2_RXD,       UART,     IRDA,       SPDIF,      UARTA,      SPI4,       RSVD,       INPUT,  0x3164),
+       PINGROUP(UART2_TXD,       UART,     IRDA,       SPDIF,      UARTA,      SPI4,       RSVD,       INPUT,  0x3168),
+       PINGROUP(UART2_RTS_N,     UART,     UARTA,      UARTB,      GMI,        SPI4,       RSVD,       INPUT,  0x316c),
+       PINGROUP(UART2_CTS_N,     UART,     UARTA,      UARTB,      GMI,        SPI4,       RSVD,       INPUT,  0x3170),
+       PINGROUP(UART3_TXD,       UART,     UARTC,      RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x3174),
+       PINGROUP(UART3_RXD,       UART,     UARTC,      RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x3178),
+       PINGROUP(UART3_CTS_N,     UART,     UARTC,      RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x317c),
+       PINGROUP(UART3_RTS_N,     UART,     UARTC,      PWM0,       GMI,        RSVD2,      RSVD,       INPUT,  0x3180),
+       PINGROUP(GPIO_PU0,        UART,     OWR,        UARTA,      GMI,        RSVD1,      RSVD,       INPUT,  0x3184),
+       PINGROUP(GPIO_PU1,        UART,     RSVD1,      UARTA,      GMI,        RSVD2,      RSVD,       INPUT,  0x3188),
+       PINGROUP(GPIO_PU2,        UART,     RSVD1,      UARTA,      GMI,        RSVD2,      RSVD,       INPUT,  0x318c),
+       PINGROUP(GPIO_PU3,        UART,     PWM0,       UARTA,      GMI,        RSVD1,      RSVD,       INPUT,  0x3190),
+       PINGROUP(GPIO_PU4,        UART,     PWM1,       UARTA,      GMI,        RSVD1,      RSVD,       INPUT,  0x3194),
+       PINGROUP(GPIO_PU5,        UART,     PWM2,       UARTA,      GMI,        RSVD1,      RSVD,       INPUT,  0x3198),
+       PINGROUP(GPIO_PU6,        UART,     PWM3,       UARTA,      GMI,        RSVD1,      RSVD,       INPUT,  0x319c),
+       PINGROUP(GEN1_I2C_SDA,    UART,     I2C1,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x31a0),
+       PINGROUP(GEN1_I2C_SCL,    UART,     I2C1,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x31a4),
+       PINGROUP(DAP4_FS,         UART,     I2S3,       RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x31a8),
+       PINGROUP(DAP4_DIN,        UART,     I2S3,       RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x31ac),
+       PINGROUP(DAP4_DOUT,       UART,     I2S3,       RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x31b0),
+       PINGROUP(DAP4_SCLK,       UART,     I2S3,       RSVD1,      GMI,        RSVD2,      RSVD,       INPUT,  0x31b4),
+       PINGROUP(CLK3_OUT,        UART,     EXTPERIPH3, RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x31b8),
+       PINGROUP(CLK3_REQ,        UART,     DEV3,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x31bc),
+       PINGROUP(GMI_WP_N,        GMI,      RSVD1,      NAND,       GMI,        GMI_ALT,    RSVD,       INPUT,  0x31c0),
+       PINGROUP(GMI_IORDY,       GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31c4),
+       PINGROUP(GMI_WAIT,        GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31c8),
+       PINGROUP(GMI_ADV_N,       GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31cc),
+       PINGROUP(GMI_CLK,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31d0),
+       PINGROUP(GMI_CS0_N,       GMI,      RSVD1,      NAND,       GMI,        DTV,        RSVD,       INPUT,  0x31d4),
+       PINGROUP(GMI_CS1_N,       GMI,      RSVD1,      NAND,       GMI,        DTV,        RSVD,       INPUT,  0x31d8),
+       PINGROUP(GMI_CS2_N,       GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31dc),
+       PINGROUP(GMI_CS3_N,       GMI,      RSVD1,      NAND,       GMI,        GMI_ALT,    RSVD,       INPUT,  0x31e0),
+       PINGROUP(GMI_CS4_N,       GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31e4),
+       PINGROUP(GMI_CS6_N,       GMI,      NAND,       NAND_ALT,   GMI,        SATA,       RSVD,       INPUT,  0x31e8),
+       PINGROUP(GMI_CS7_N,       GMI,      NAND,       NAND_ALT,   GMI,        GMI_ALT,    RSVD,       INPUT,  0x31ec),
+       PINGROUP(GMI_AD0,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31f0),
+       PINGROUP(GMI_AD1,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31f4),
+       PINGROUP(GMI_AD2,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31f8),
+       PINGROUP(GMI_AD3,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x31fc),
+       PINGROUP(GMI_AD4,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3200),
+       PINGROUP(GMI_AD5,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3204),
+       PINGROUP(GMI_AD6,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3208),
+       PINGROUP(GMI_AD7,         GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x320c),
+       PINGROUP(GMI_AD8,         GMI,      PWM0,       NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3210),
+       PINGROUP(GMI_AD9,         GMI,      PWM1,       NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3214),
+       PINGROUP(GMI_AD10,        GMI,      PWM2,       NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3218),
+       PINGROUP(GMI_AD11,        GMI,      PWM3,       NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x321c),
+       PINGROUP(GMI_AD12,        GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3220),
+       PINGROUP(GMI_AD13,        GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3224),
+       PINGROUP(GMI_AD14,        GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x3228),
+       PINGROUP(GMI_AD15,        GMI,      RSVD1,      NAND,       GMI,        RSVD2,      RSVD,       INPUT,  0x322c),
+       PINGROUP(GMI_A16,         GMI,      UARTD,      SPI4,       GMI,        GMI_ALT,    RSVD,       INPUT,  0x3230),
+       PINGROUP(GMI_A17,         GMI,      UARTD,      SPI4,       GMI,        DTV,        RSVD,       INPUT,  0x3234),
+       PINGROUP(GMI_A18,         GMI,      UARTD,      SPI4,       GMI,        DTV,        RSVD,       INPUT,  0x3238),
+       PINGROUP(GMI_A19,         GMI,      UARTD,      SPI4,       GMI,        RSVD3,      RSVD,       INPUT,  0x323c),
+       PINGROUP(GMI_WR_N,        GMI,      RSVD1,      NAND,       GMI,        RSVD3,      RSVD,       INPUT,  0x3240),
+       PINGROUP(GMI_OE_N,        GMI,      RSVD1,      NAND,       GMI,        RSVD3,      RSVD,       INPUT,  0x3244),
+       PINGROUP(GMI_DQS,         GMI,      RSVD1,      NAND,       GMI,        RSVD3,      RSVD,       INPUT,  0x3248),
+       PINGROUP(GMI_RST_N,       GMI,      NAND,       NAND_ALT,   GMI,        RSVD3,      RSVD,       INPUT,  0x324c),
+       PINGROUP(GEN2_I2C_SCL,    GMI,      I2C2,       HDMI,       GMI,        RSVD3,      RSVD,       INPUT,  0x3250),
+       PINGROUP(GEN2_I2C_SDA,    GMI,      I2C2,       HDMI,       GMI,        RSVD3,      RSVD,       INPUT,  0x3254),
+       PINGROUP(SDMMC4_CLK,      SDMMC4,   VI,         NAND,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3258),
+       PINGROUP(SDMMC4_CMD,      SDMMC4,   I2C3,       NAND,       GMI,        SDMMC4,     RSVD,       INPUT,  0x325c),
+       PINGROUP(SDMMC4_DAT0,     SDMMC4,   UARTE,      SPI3,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3260),
+       PINGROUP(SDMMC4_DAT1,     SDMMC4,   UARTE,      SPI3,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3264),
+       PINGROUP(SDMMC4_DAT2,     SDMMC4,   UARTE,      SPI3,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3268),
+       PINGROUP(SDMMC4_DAT3,     SDMMC4,   UARTE,      SPI3,       GMI,        SDMMC4,     RSVD,       INPUT,  0x326c),
+       PINGROUP(SDMMC4_DAT4,     SDMMC4,   I2C3,       I2S4,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3270),
+       PINGROUP(SDMMC4_DAT5,     SDMMC4,   VGP3,       I2S4,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3274),
+       PINGROUP(SDMMC4_DAT6,     SDMMC4,   VGP4,       I2S4,       GMI,        SDMMC4,     RSVD,       INPUT,  0x3278),
+       PINGROUP(SDMMC4_DAT7,     SDMMC4,   VGP5,       I2S4,       GMI,        SDMMC4,     RSVD,       INPUT,  0x327c),
+       PINGROUP(SDMMC4_RST_N,    SDMMC4,   VGP6,       RSVD1,      RSVD2,      POPSDMMC4,  RSVD,       INPUT,  0x3280),
+       PINGROUP(CAM_MCLK,        CAM,      VI,         VI_ALT1,    VI_ALT2,    POPSDMMC4,  RSVD,       INPUT,  0x3284),
+       PINGROUP(GPIO_PCC1,       CAM,      I2S4,       RSVD1,      RSVD2,      POPSDMMC4,  RSVD,       INPUT,  0x3288),
+       PINGROUP(GPIO_PBB0,       CAM,      I2S4,       RSVD1,      RSVD2,      POPSDMMC4,  RSVD,       INPUT,  0x328c),
+       PINGROUP(CAM_I2C_SCL,     CAM,      VGP1,       I2C3,       RSVD2,      POPSDMMC4,  RSVD,       INPUT,  0x3290),
+       PINGROUP(CAM_I2C_SDA,     CAM,      VGP2,       I2C3,       RSVD2,      POPSDMMC4,  RSVD,       INPUT,  0x3294),
+       PINGROUP(GPIO_PBB3,       CAM,      VGP3,       DISPLAYA,   DISPLAYB,   POPSDMMC4,  RSVD,       INPUT,  0x3298),
+       PINGROUP(GPIO_PBB4,       CAM,      VGP4,       DISPLAYA,   DISPLAYB,   POPSDMMC4,  RSVD,       INPUT,  0x329c),
+       PINGROUP(GPIO_PBB5,       CAM,      VGP5,       DISPLAYA,   DISPLAYB,   POPSDMMC4,  RSVD,       INPUT,  0x32a0),
+       PINGROUP(GPIO_PBB6,       CAM,      VGP6,       DISPLAYA,   DISPLAYB,   POPSDMMC4,  RSVD,       INPUT,  0x32a4),
+       PINGROUP(GPIO_PBB7,       CAM,      I2S4,       RSVD1,      RSVD2,      POPSDMMC4,  RSVD,       INPUT,  0x32a8),
+       PINGROUP(GPIO_PCC2,       CAM,      I2S4,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x32ac),
+       PINGROUP(JTAG_RTCK,       SYS,      RTCK,       RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x32b0),
+       PINGROUP(PWR_I2C_SCL,     SYS,      I2CPWR,     RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x32b4),
+       PINGROUP(PWR_I2C_SDA,     SYS,      I2CPWR,     RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x32b8),
+       PINGROUP(KB_ROW0,         SYS,      KBC,        NAND,       RSVD2,      RSVD3,      RSVD,       INPUT,  0x32bc),
+       PINGROUP(KB_ROW1,         SYS,      KBC,        NAND,       RSVD2,      RSVD3,      RSVD,       INPUT,  0x32c0),
+       PINGROUP(KB_ROW2,         SYS,      KBC,        NAND,       RSVD2,      RSVD3,      RSVD,       INPUT,  0x32c4),
+       PINGROUP(KB_ROW3,         SYS,      KBC,        NAND,       RSVD2,      MIO,        RSVD,       INPUT,  0x32c8),
+       PINGROUP(KB_ROW4,         SYS,      KBC,        NAND,       TRACE,      RSVD3,      RSVD,       INPUT,  0x32cc),
+       PINGROUP(KB_ROW5,         SYS,      KBC,        NAND,       TRACE,      OWR,        RSVD,       INPUT,  0x32d0),
+       PINGROUP(KB_ROW6,         SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32d4),
+       PINGROUP(KB_ROW7,         SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32d8),
+       PINGROUP(KB_ROW8,         SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32dc),
+       PINGROUP(KB_ROW9,         SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32e0),
+       PINGROUP(KB_ROW10,        SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32e4),
+       PINGROUP(KB_ROW11,        SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32e8),
+       PINGROUP(KB_ROW12,        SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32ec),
+       PINGROUP(KB_ROW13,        SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32f0),
+       PINGROUP(KB_ROW14,        SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32f4),
+       PINGROUP(KB_ROW15,        SYS,      KBC,        NAND,       SDMMC2,     MIO,        RSVD,       INPUT,  0x32f8),
+       PINGROUP(KB_COL0,         SYS,      KBC,        NAND,       TRACE,      EMC_DLL,    RSVD,       INPUT,  0x32fc),
+       PINGROUP(KB_COL1,         SYS,      KBC,        NAND,       TRACE,      EMC_DLL,    RSVD,       INPUT,  0x3300),
+       PINGROUP(KB_COL2,         SYS,      KBC,        NAND,       TRACE,      RSVD,       RSVD,       INPUT,  0x3304),
+       PINGROUP(KB_COL3,         SYS,      KBC,        NAND,       TRACE,      RSVD,       RSVD,       INPUT,  0x3308),
+       PINGROUP(KB_COL4,         SYS,      KBC,        NAND,       TRACE,      RSVD,       RSVD,       INPUT,  0x330c),
+       PINGROUP(KB_COL5,         SYS,      KBC,        NAND,       TRACE,      RSVD,       RSVD,       INPUT,  0x3310),
+       PINGROUP(KB_COL6,         SYS,      KBC,        NAND,       TRACE,      MIO,        RSVD,       INPUT,  0x3314),
+       PINGROUP(KB_COL7,         SYS,      KBC,        NAND,       TRACE,      MIO,        RSVD,       INPUT,  0x3318),
+       PINGROUP(CLK_32K_OUT,     SYS,      BLINK,      RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x331c),
+       PINGROUP(SYS_CLK_REQ,     SYS,      SYSCLK,     RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x3320),
+       PINGROUP(CORE_PWR_REQ,    SYS,      RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3324),
+       PINGROUP(CPU_PWR_REQ,     SYS,      RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3328),
+       PINGROUP(PWR_INT_N,       SYS,      RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x332c),
+       PINGROUP(CLK_32K_IN,      SYS,      RSVD,       RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3330),
+       PINGROUP(OWR,             SYS,      OWR,        RSVD,       RSVD,       RSVD,       RSVD,       INPUT,  0x3334),
+       PINGROUP(DAP1_FS,         AUDIO,    I2S0,       HDA,        GMI,        SDMMC2,     RSVD,       INPUT,  0x3338),
+       PINGROUP(DAP1_DIN,        AUDIO,    I2S0,       HDA,        GMI,        SDMMC2,     RSVD,       INPUT,  0x333c),
+       PINGROUP(DAP1_DOUT,       AUDIO,    I2S0,       HDA,        GMI,        SDMMC2,     RSVD,       INPUT,  0x3340),
+       PINGROUP(DAP1_SCLK,       AUDIO,    I2S0,       HDA,        GMI,        SDMMC2,     RSVD,       INPUT,  0x3344),
+       PINGROUP(CLK1_REQ,        AUDIO,    DAP,        DAP1,       RSVD2,      RSVD3,      RSVD,       INPUT,  0x3348),
+       PINGROUP(CLK1_OUT,        AUDIO,    EXTPERIPH1, RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x334c),
+       PINGROUP(SPDIF_IN,        AUDIO,    SPDIF,      DAP2,       I2C1,       DAPSDMMC2,  RSVD,       INPUT,  0x3350),
+       PINGROUP(SPDIF_OUT,       AUDIO,    SPDIF,      RSVD1,      I2C1,       DAPSDMMC2,  RSVD,       INPUT,  0x3354),
+       PINGROUP(DAP2_FS,         AUDIO,    I2S1,       HDA,        RSVD2,      GMI,        RSVD,       INPUT,  0x3358),
+       PINGROUP(DAP2_DIN,        AUDIO,    I2S1,       HDA,        RSVD2,      GMI,        RSVD,       INPUT,  0x335c),
+       PINGROUP(DAP2_DOUT,       AUDIO,    I2S1,       HDA,        RSVD2,      GMI,        RSVD,       INPUT,  0x3360),
+       PINGROUP(DAP2_SCLK,       AUDIO,    I2S1,       HDA,        RSVD2,      GMI,        RSVD,       INPUT,  0x3364),
+       PINGROUP(SPI2_MOSI,       AUDIO,    SPI6,       SPI2,       SPI3,       GMI,        RSVD,       INPUT,  0x3368),
+       PINGROUP(SPI2_MISO,       AUDIO,    SPI6,       SPI2,       SPI3,       GMI,        RSVD,       INPUT,  0x336c),
+       PINGROUP(SPI2_CS0_N,      AUDIO,    SPI6,       SPI2,       SPI3,       GMI,        RSVD,       INPUT,  0x3370),
+       PINGROUP(SPI2_SCK,        AUDIO,    SPI6,       SPI2,       SPI3,       GMI,        RSVD,       INPUT,  0x3374),
+       PINGROUP(SPI1_MOSI,       AUDIO,    SPI2,       SPI1,       SPI2_ALT,   GMI,        RSVD,       INPUT,  0x3378),
+       PINGROUP(SPI1_SCK,        AUDIO,    SPI2,       SPI1,       SPI2_ALT,   GMI,        RSVD,       INPUT,  0x337c),
+       PINGROUP(SPI1_CS0_N,      AUDIO,    SPI2,       SPI1,       SPI2_ALT,   GMI,        RSVD,       INPUT,  0x3380),
+       PINGROUP(SPI1_MISO,       AUDIO,    SPI3,       SPI1,       SPI2,       RSVD3,      RSVD,       INPUT,  0x3384),
+       PINGROUP(SPI2_CS1_N,      AUDIO,    SPI3,       SPI2,       SPI2_ALT,   I2C1,       RSVD,       INPUT,  0x3388),
+       PINGROUP(SPI2_CS2_N,      AUDIO,    SPI3,       SPI2,       SPI2_ALT,   I2C1,       RSVD,       INPUT,  0x338c),
+       PINGROUP(SDMMC3_CLK,      SDMMC3,   UARTA,      PWM2,       SDMMC3,     SPI3,       RSVD,       INPUT,  0x3390),
+       PINGROUP(SDMMC3_CMD,      SDMMC3,   UARTA,      PWM3,       SDMMC3,     SPI2,       RSVD,       INPUT,  0x3394),
+       PINGROUP(SDMMC3_DAT0,     SDMMC3,   RSVD0,      RSVD1,      SDMMC3,     SPI3,       RSVD,       INPUT,  0x3398),
+       PINGROUP(SDMMC3_DAT1,     SDMMC3,   RSVD0,      RSVD1,      SDMMC3,     SPI3,       RSVD,       INPUT,  0x339c),
+       PINGROUP(SDMMC3_DAT2,     SDMMC3,   RSVD0,      PWM1,       SDMMC3,     SPI3,       RSVD,       INPUT,  0x33a0),
+       PINGROUP(SDMMC3_DAT3,     SDMMC3,   RSVD0,      PWM0,       SDMMC3,     SPI3,       RSVD,       INPUT,  0x33a4),
+       PINGROUP(SDMMC3_DAT4,     SDMMC3,   PWM1,       SPI4,       SDMMC3,     SPI2,       RSVD,       INPUT,  0x33a8),
+       PINGROUP(SDMMC3_DAT5,     SDMMC3,   PWM0,       SPI4,       SDMMC3,     SPI2,       RSVD,       INPUT,  0x33ac),
+       PINGROUP(SDMMC3_DAT6,     SDMMC3,   SPDIF,      SPI4,       SDMMC3,     SPI2,       RSVD,       INPUT,  0x33b0),
+       PINGROUP(SDMMC3_DAT7,     SDMMC3,   SPDIF,      SPI4,       SDMMC3,     SPI2,       RSVD,       INPUT,  0x33b4),
+       PINGROUP(PEX_L0_PRSNT_N,  PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33b8),
+       PINGROUP(PEX_L0_RST_N,    PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33bc),
+       PINGROUP(PEX_L0_CLKREQ_N, PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33c0),
+       PINGROUP(PEX_WAKE_N,      PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33c4),
+       PINGROUP(PEX_L1_PRSNT_N,  PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33c8),
+       PINGROUP(PEX_L1_RST_N,    PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33cc),
+       PINGROUP(PEX_L1_CLKREQ_N, PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33d0),
+       PINGROUP(PEX_L2_PRSNT_N,  PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33d4),
+       PINGROUP(PEX_L2_RST_N,    PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33d8),
+       PINGROUP(PEX_L2_CLKREQ_N, PEXCTL,   PCIE,       HDA,        RSVD2,      RSVD3,      RSVD,       INPUT,  0x33dc),
+       PINGROUP(HDMI_CEC,        SYS,      CEC,        RSVD1,      RSVD2,      RSVD3,      RSVD,       INPUT,  0x33e0),
+};
+
+#ifdef CONFIG_PM
+
+static u32 pinmux_reg[TEGRA_MAX_PINGROUP +
+                     ARRAY_SIZE(tegra_soc_drive_pingroups)];
+
+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 < TEGRA_MAX_PINGROUP; i++)
+               *ctx++ = pg_readl(tegra_soc_pingroups[i].mux_reg);
+
+       for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i ++)
+               *ctx++ = pg_readl(tegra_soc_drive_pingroups[i].reg);
+}
+
+void tegra_pinmux_resume(void)
+{
+       unsigned int i;
+       u32 *ctx = pinmux_reg;
+
+       for (i = 0; i < TEGRA_MAX_PINGROUP; i++)
+               pg_writel(*ctx++, tegra_soc_pingroups[i].mux_reg);
+
+       for (i = 0; i < ARRAY_SIZE(tegra_soc_drive_pingroups); i ++)
+               pg_writel(*ctx++, tegra_soc_drive_pingroups[i].reg);
+}
+#endif
index a49c20d..2b6b694 100644 (file)
@@ -96,6 +96,51 @@ static char *tegra_mux_names[TEGRA_MAX_MUX] = {
        [TEGRA_MUX_VI] = "VI",
        [TEGRA_MUX_VI_SENSOR_CLK] = "VI_SENSOR_CLK",
        [TEGRA_MUX_XIO] = "XIO",
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       [TEGRA_MUX_BLINK] = "BLINK",
+       [TEGRA_MUX_CEC] = "CEC",
+       [TEGRA_MUX_CLK12] = "CLK12",
+       [TEGRA_MUX_DAP] = "DAP",
+       [TEGRA_MUX_DAPSDMMC2] = "DAPSDMMC2",
+       [TEGRA_MUX_DDR] = "DDR",
+       [TEGRA_MUX_DEV3] = "DEV3",
+       [TEGRA_MUX_DTV] = "DTV",
+       [TEGRA_MUX_VI_ALT1] = "VI_ALT1",
+       [TEGRA_MUX_VI_ALT2] = "VI_ALT2",
+       [TEGRA_MUX_VI_ALT3] = "VI_ALT3",
+       [TEGRA_MUX_EMC_DLL] = "EMC_DLL",
+       [TEGRA_MUX_EXTPERIPH1] = "EXTPERIPH1",
+       [TEGRA_MUX_EXTPERIPH2] = "EXTPERIPH2",
+       [TEGRA_MUX_EXTPERIPH3] = "EXTPERIPH3",
+       [TEGRA_MUX_GMI_ALT] = "GMI_ALT",
+       [TEGRA_MUX_HDA] = "HDA",
+       [TEGRA_MUX_HSI] = "HSI",
+       [TEGRA_MUX_I2C4] = "I2C4",
+       [TEGRA_MUX_I2C5] = "I2C5",
+       [TEGRA_MUX_I2CPWR] = "I2CPWR",
+       [TEGRA_MUX_I2S0] = "I2S0",
+       [TEGRA_MUX_I2S1] = "I2S1",
+       [TEGRA_MUX_I2S2] = "I2S2",
+       [TEGRA_MUX_I2S3] = "I2S3",
+       [TEGRA_MUX_I2S4] = "I2S4",
+       [TEGRA_MUX_NAND_ALT] = "NAND_ALT",
+       [TEGRA_MUX_POPSDIO4] = "POPSDIO4",
+       [TEGRA_MUX_POPSDMMC4] = "POPSDMMC4",
+       [TEGRA_MUX_PWM0] = "PWM0",
+       [TEGRA_MUX_PWM1] = "PWM1",
+       [TEGRA_MUX_PWM2] = "PWM2",
+       [TEGRA_MUX_PWM3] = "PWM3",
+       [TEGRA_MUX_SATA] = "SATA",
+       [TEGRA_MUX_SPI5] = "SPI5",
+       [TEGRA_MUX_SPI6] = "SPI6",
+       [TEGRA_MUX_SYSCLK] = "SYSCLK",
+       [TEGRA_MUX_VGP1] = "VGP1",
+       [TEGRA_MUX_VGP2] = "VGP2",
+       [TEGRA_MUX_VGP3] = "VGP3",
+       [TEGRA_MUX_VGP4] = "VGP4",
+       [TEGRA_MUX_VGP5] = "VGP5",
+       [TEGRA_MUX_VGP6] = "VGP6",
+#endif
        [TEGRA_MUX_SAFE] = "<safe>",
 };
 
@@ -169,6 +214,21 @@ static const char *pupd_name(unsigned long val)
        }
 }
 
+#if defined(TEGRA_PINMUX_HAS_IO_DIRECTION)
+static const char *io_name(unsigned long val)
+{
+       switch (val) {
+       case 0:
+               return "OUTPUT";
+
+       case 1:
+               return "INPUT";
+
+       default:
+               return "RSVD";
+       }
+}
+#endif
 
 static inline unsigned long pg_readl(unsigned long offset)
 {
@@ -220,6 +280,10 @@ static int tegra_pinmux_set_func(const struct tegra_pingroup_config *config)
        reg = pg_readl(pingroups[pg].mux_reg);
        reg &= ~(0x3 << pingroups[pg].mux_bit);
        reg |= mux << pingroups[pg].mux_bit;
+#if defined(TEGRA_PINMUX_HAS_IO_DIRECTION)
+       reg &= ~(0x1 << 5);
+       reg |= ((config->io & 0x1) << 5);
+#endif
        pg_writel(reg, pingroups[pg].mux_reg);
 
        spin_unlock_irqrestore(&mux_lock, flags);
@@ -717,7 +781,7 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
 
                seq_printf(s, "\t{TEGRA_PINGROUP_%s", pingroups[i].name);
                len = strlen(pingroups[i].name);
-               dbg_pad_field(s, 5 - len);
+               dbg_pad_field(s, 15 - len);
 
                if (pingroups[i].mux_reg < 0) {
                        seq_printf(s, "TEGRA_MUX_NONE");
@@ -725,10 +789,12 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
                } else {
                        mux = (pg_readl(pingroups[i].mux_reg) >>
                               pingroups[i].mux_bit) & 0x3;
-                       if (pingroups[i].funcs[mux] == TEGRA_MUX_RSVD) {
+                       BUG_ON(pingroups[i].funcs[mux] == 0);
+                       if (pingroups[i].funcs[mux] & TEGRA_MUX_RSVD) {
                                seq_printf(s, "TEGRA_MUX_RSVD%1lu", mux+1);
                                len = 5;
                        } else {
+                               BUG_ON(!tegra_mux_names[pingroups[i].funcs[mux]]);
                                seq_printf(s, "TEGRA_MUX_%s",
                                           tegra_mux_names[pingroups[i].funcs[mux]]);
                                len = strlen(tegra_mux_names[pingroups[i].funcs[mux]]);
@@ -736,6 +802,15 @@ static int dbg_pinmux_show(struct seq_file *s, void *unused)
                }
                dbg_pad_field(s, 13-len);
 
+#if defined(TEGRA_PINMUX_HAS_IO_DIRECTION)
+               {
+                       unsigned long io;
+                       io = (pg_readl(pingroups[i].mux_reg) >> 5) & 0x1;
+                       seq_printf(s, "TEGRA_PIN_%s", io_name(io));
+                       len = strlen(io_name(io));
+                       dbg_pad_field(s, 6 - len);
+               }
+#endif
                if (pingroups[i].pupd_reg < 0) {
                        seq_printf(s, "TEGRA_PUPD_NORMAL");
                        len = strlen("NORMAL");
index ae6067e..9f8c3c8 100644 (file)
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/smp.h>
+#include <linux/delay.h>
 
 #include <asm/hardware/gic.h>
 #include <asm/smp_scu.h>
 
 #include <mach/iomap.h>
+#include <mach/powergate.h>
+
+#include "pm.h"
 
 #define EVP_CPU_RESET_VECTOR \
        (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100)
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x340)
 #define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
        (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x344)
+#define FLOW_CTRL_HALT_CPUx_EVENTS(cpu)        \
+       (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + ((cpu)?(((cpu)-1)*8 + 0x14) : 0x0)))
+
+#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
+#define CPU_RESET(cpu) (0x1111ul<<(cpu))
+
+static unsigned int available_cpus(void);
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+static inline int is_g_cluster_available(unsigned int cpu)
+{ return -EPERM; }
+static inline bool is_cpu_powered(unsigned int cpu)
+{ return true; }
+static inline int power_up_cpu(unsigned int cpu)
+{ return 0; }
+
+/* For Tegra2 use the software-written value of the reset regsiter for status.*/
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET
+
+#else
+static int is_g_cluster_available(unsigned int cpu);
+static bool is_cpu_powered(unsigned int cpu);
+static int power_up_cpu(unsigned int cpu);
+
+#define CAR_BOND_OUT_V \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
+#define CAR_BOND_OUT_V_CPU_G   (1<<0)
+#define CLK_RST_CONTROLLER_CPU_CMPLX_STATUS \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x470)
+
+#define FUSE_SKU_DIRECT_CONFIG \
+       (IO_ADDRESS(TEGRA_FUSE_BASE) + 0x1F4)
+#define FUSE_SKU_DISABLE_ALL_CPUS      (1<<5)
+#define FUSE_SKU_NUM_DISABLED_CPUS(x)  (((x) >> 3) & 3)
+#endif
 
 extern void tegra_secondary_startup(void);
 
@@ -45,23 +83,57 @@ void __cpuinit platform_secondary_init(unsigned int cpu)
 int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
 {
        u32 reg;
+       int status;
+
+       if (is_lp_cluster()) {
+               /* The G CPU may not be available for a
+                  variety of reasons. */
+               status = is_g_cluster_available(cpu);
+               if (status)
+                       return status;
+
+               /* Switch to the G CPU before continuing. */
+               status = tegra_cluster_control(0,
+                                              TEGRA_POWER_CLUSTER_G |
+                                              TEGRA_POWER_CLUSTER_IMMEDIATE);
+               if (status)
+                       return status;
+       }
 
        smp_wmb();
 
        /* set the reset vector to point to the secondary_startup routine */
        writel(virt_to_phys(tegra_secondary_startup), EVP_CPU_RESET_VECTOR);
 
+       /* Force the CPU into reset. The CPU must remain in reset when the
+          flow controller state is cleared (which will cause the flow
+          controller to stop driving reset if the CPU has been power-gated
+          via the flow controller). This will have no effect on first boot
+          of the CPU since it should already be in reset. */
+       writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET);
+       dmb();
+
+       /* Unhalt the CPU. If the flow controller was used to power-gate the
+          CPU this will cause the flow controller to stop driving reset.
+          The CPU will remain in reset because the clock and reset block
+          is now driving reset. */
+       writel(0, FLOW_CTRL_HALT_CPUx_EVENTS(cpu));
+       dmb();
+
        /* enable cpu clock on cpu */
        reg = readl(CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
-       writel(reg & ~(1 << (8 + cpu)), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+       writel(reg & ~CPU_CLOCK(cpu), CLK_RST_CONTROLLER_CLK_CPU_CMPLX);
+       dmb();
 
-       reg = 0x1111 << cpu;
-       writel(reg, CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
+       status = power_up_cpu(cpu);
+       if (status)
+               goto done;
 
-       /* unhalt the cpu */
-       writel(0, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x14 + 0x8 * (cpu - 1));
+       dmb();
+       writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR);
 
-       return 0;
+done:
+       return status;
 }
 
 /*
@@ -70,7 +142,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
  */
 void __init smp_init_cpus(void)
 {
-       unsigned int i, ncores = scu_get_core_count(scu_base);
+       unsigned int i, ncores = available_cpus();
 
        if (ncores > nr_cpu_ids) {
                pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
@@ -89,3 +161,83 @@ void __init platform_smp_prepare_cpus(unsigned int max_cpus)
 
        scu_enable(scu_base);
 }
+
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+
+static bool is_cpu_powered(unsigned int cpu)
+{
+       if (is_lp_cluster())
+               return true;
+       else
+               return tegra_powergate_is_powered(TEGRA_CPU_POWERGATE_ID(cpu));
+}
+
+static int power_up_cpu(unsigned int cpu)
+{
+       int ret;
+       unsigned long timeout;
+
+       BUG_ON(cpu == smp_processor_id());
+       BUG_ON(is_lp_cluster());
+
+       if (!is_cpu_powered(cpu))
+       {
+               ret = tegra_powergate_power_on(TEGRA_CPU_POWERGATE_ID(cpu));
+               if (ret)
+                       goto fail;
+
+               /* Wait for the power to come up. */
+               timeout = jiffies + 10*HZ;
+
+               do {
+                       if (is_cpu_powered(cpu))
+                               goto remove_clamps;
+                       udelay(10);
+               } while (time_before(jiffies, timeout));
+               ret = -ETIMEDOUT;
+               goto fail;
+       }
+
+remove_clamps:
+       ret = tegra_powergate_remove_clamping(TEGRA_CPU_POWERGATE_ID(cpu));
+fail:
+       return ret;
+}
+
+static int is_g_cluster_available(unsigned int cpu)
+{
+       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+       u32 bond_out = readl(CAR_BOND_OUT_V);
+
+       /* Does the G CPU complex exist at all? */
+       if ((fuse_sku & FUSE_SKU_DISABLE_ALL_CPUS) ||
+           (bond_out & CAR_BOND_OUT_V_CPU_G))
+               return -EPERM;
+
+       if (cpu >= available_cpus())
+               return -EPERM;
+
+       /* FIXME: The G CPU can be unavailable for a number of reasons
+        *        (e.g., low battery, over temperature, etc.). Add checks for
+        *        these conditions. */
+
+       return 0;
+}
+#endif
+
+static unsigned int available_cpus(void)
+{
+       static unsigned int ncores = 0;
+
+       if (ncores == 0) {
+               ncores = scu_get_core_count(scu_base);
+#ifdef CONFIG_ARCH_TEGRA_3x_SOC
+               if (ncores > 1) {
+                       u32 fuse_sku = readl(FUSE_SKU_DIRECT_CONFIG);
+                       ncores -= FUSE_SKU_NUM_DISABLED_CPUS(fuse_sku);
+                       BUG_ON((int)ncores <= 0);
+               }
+#endif
+       }
+       return ncores;
+}
diff --git a/arch/arm/mach-tegra/pm-t3.c b/arch/arm/mach-tegra/pm-t3.c
new file mode 100644 (file)
index 0000000..8efae69
--- /dev/null
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 2009-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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/smp.h>
+#include <linux/interrupt.h>
+#include <linux/clk.h>
+
+#include <mach/gpio.h>
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+#include <asm/hardware/gic.h>
+
+#include "clock.h"
+#include "gpio-names.h"
+#include "pm.h"
+
+#define SUSPEND_DEBUG_PRINT    1       /* Nonzero for debug prints */
+
+#if SUSPEND_DEBUG_PRINT
+#define DEBUG_SUSPEND(x) printk x
+#else
+#define DEBUG_SUSPEND(x)
+#endif
+
+#define CAR_CCLK_BURST_POLICY \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x20)
+
+#define CAR_SUPER_CCLK_DIVIDER \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x24)
+
+#define CAR_CCLKG_BURST_POLICY \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x368)
+
+#define CAR_SUPER_CCLKG_DIVIDER \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x36C)
+
+#define CAR_CCLKLP_BURST_POLICY \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x370)
+#define PLLX_DIV2_BYPASS_LP    (1<<16)
+
+#define CAR_SUPER_CCLKLP_DIVIDER \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x374)
+
+#define CAR_BOND_OUT_V \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x390)
+#define CAR_BOND_OUT_V_CPU_G   (1<<0)
+#define CAR_BOND_OUT_V_CPU_LP  (1<<1)
+
+#define CAR_CLK_ENB_V_SET \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x440)
+#define CAR_CLK_ENB_V_CPU_G    (1<<0)
+#define CAR_CLK_ENB_V_CPU_LP   (1<<1)
+
+#define CAR_RST_CPUG_CMPLX_SET \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x450)
+
+#define CAR_RST_CPUG_CMPLX_CLR \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x454)
+
+#define CAR_RST_CPULP_CMPLX_SET \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x458)
+
+#define CAR_RST_CPULP_CMPLX_CLR \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x45C)
+
+#define CAR_CLK_CPUG_CMPLX_SET \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x460)
+
+#define CAR_CLK_CPUG_CMPLX_CLR \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x464)
+
+#define CAR_CLK_CPULP_CMPLX_SET \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x468)
+
+#define CAR_CLK_CPULP_CMPLX_CLR \
+       (IO_ADDRESS(TEGRA_CLK_RESET_BASE) + 0x46C)
+
+#define CPU_CLOCK(cpu) (0x1<<(8+cpu))
+#define CPU_RESET(cpu) (0x1111ul<<(cpu))
+
+#define FLOW_CTRL_CLUSTER_CONTROL \
+       (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x2c)
+#define FLOW_CTRL_CPUx_CSR(cpu)        \
+       (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + ((cpu)?(((cpu)-1)*8 + 0x18) : 0x8)))
+#define FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE       (1<<3)
+#define FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER       (1<<2)
+
+void tegra_suspend_dram(bool lp0_ok, unsigned int flags);
+
+unsigned int is_lp_cluster(void)
+{
+       unsigned int reg;
+       reg = readl(FLOW_CTRL_CLUSTER_CONTROL);
+       return (reg & 1); /* 0 == G, 1 == LP*/
+}
+
+static int cluster_switch_prolog_clock(unsigned int flags)
+{
+       u32 reg;
+       u32 CclkBurstPolicy;
+       u32 SuperCclkDivier;
+
+       /* Read the CPU clock settings for the currently active CPU. */
+       CclkBurstPolicy = readl(CAR_CCLK_BURST_POLICY);
+       SuperCclkDivier = readl(CAR_SUPER_CCLK_DIVIDER);
+
+       /* Read the bond out register containing the G and LP CPUs. */
+       reg = readl(CAR_BOND_OUT_V);
+
+       /* Switching to G? */
+       if (flags & TEGRA_POWER_CLUSTER_G) {
+               /* Do the G CPUs exist? */
+               if (reg & CAR_BOND_OUT_V_CPU_G)
+                       return -ENXIO;
+
+               if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
+                       /* In LP1 power mode come up on CLKM (oscillator) */
+                       CclkBurstPolicy |= ~0xF;
+                       SuperCclkDivier = 0;
+               }
+
+               /* We will be running on the G CPU after the switch.
+                  Set up the G clock policy. */
+               writel(CclkBurstPolicy, CAR_CCLKG_BURST_POLICY);
+               writel(SuperCclkDivier, CAR_SUPER_CCLKG_DIVIDER);
+
+               /* Hold G CPUs 1-3 in reset after the switch */
+               reg = CPU_RESET(1) | CPU_RESET(2) | CPU_RESET(3);
+               writel(reg, CAR_RST_CPUG_CMPLX_SET);
+
+               /* Take G CPU 0 out of reset after the switch */
+               reg = CPU_RESET(0);
+               writel(reg, CAR_RST_CPUG_CMPLX_CLR);
+
+               /* Disable the clocks on G CPUs 1-3 after the switch */
+               reg = CPU_CLOCK(1) | CPU_CLOCK(2) | CPU_CLOCK(3);
+               writel(reg, CAR_CLK_CPUG_CMPLX_SET);
+
+               /* Enable the clock on G CPU 0 after the switch */
+               reg = CPU_CLOCK(0);
+               writel(reg, CAR_CLK_CPUG_CMPLX_CLR);
+
+               /* Enable the G CPU complex clock after the switch */
+               reg = CAR_CLK_ENB_V_CPU_G;
+               writel(reg, CAR_CLK_ENB_V_SET);
+       }
+       /* Switching to LP? */
+       else if (flags & TEGRA_POWER_CLUSTER_LP) {
+               /* Does the LP CPU exist? */
+               if (reg & CAR_BOND_OUT_V_CPU_LP)
+                       return -ENXIO;
+
+               if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
+                       /* In LP1 power mode come up on CLKM (oscillator) */
+                       CclkBurstPolicy |= ~0xF;
+                       SuperCclkDivier = 0;
+               } else {
+                       /* It is possible that PLLX frequency is too high
+                          for the LP CPU. Reduce the frequency if necessary
+                          to prevent over-clocking when we switch. PLLX
+                          has an implied divide-by-2 when the LP CPU is
+                          active unless PLLX_DIV2_BYPASS_LP is selected. */
+
+                       struct clk *c = tegra_get_clock_by_name("cpu");
+                       unsigned long cur_rate = clk_get_rate(c);
+                       unsigned long max_rate = clk_get_rate(c); /* !!!FIXME!!! clk_alt_max_rate(c); */
+                       int err;
+
+                       if (cur_rate/2 > max_rate) {
+                               /* PLLX is running too fast for the LP CPU.
+                                  Reduce it to LP maximum rate which must
+                                  be multipled by 2 because of the LP CPU's
+                                  implied divied-by-2. */
+
+                               DEBUG_SUSPEND(("%s: G freq %lu\r\n", __func__,
+                                              cur_rate));
+                               err = clk_set_rate(c, max_rate * 2);
+                               BUG_ON(err);
+                               DEBUG_SUSPEND(("%s: G freq %lu\r\n", __func__,
+                                              clk_get_rate(c)));
+                       }
+               }
+
+               /* We will be running on the LP CPU after the switch.
+                  Set up the LP clock policy. */
+               CclkBurstPolicy &= ~PLLX_DIV2_BYPASS_LP;
+               writel(CclkBurstPolicy, CAR_CCLKLP_BURST_POLICY);
+               writel(SuperCclkDivier, CAR_SUPER_CCLKLP_DIVIDER);
+
+               /* Take the LP CPU ut of reset after the switch */
+               reg = CPU_RESET(0);
+               writel(reg, CAR_RST_CPULP_CMPLX_CLR);
+
+               /* Enable the clock on the LP CPU after the switch */
+               reg = CPU_CLOCK(0);
+               writel(reg, CAR_CLK_CPULP_CMPLX_CLR);
+
+               /* Enable the LP CPU complex clock after the switch */
+               reg = CAR_CLK_ENB_V_CPU_LP;
+               writel(reg, CAR_CLK_ENB_V_SET);
+       }
+
+       return 0;
+}
+
+void tegra_cluster_switch_prolog(unsigned int flags)
+{
+       unsigned int target_cluster = flags & TEGRA_POWER_CLUSTER_MASK;
+       unsigned int current_cluster = is_lp_cluster()
+                                       ? TEGRA_POWER_CLUSTER_LP
+                                       : TEGRA_POWER_CLUSTER_G;
+       u32 reg;
+
+       /* Read the flow controler CSR register and clear the CPU switch
+          and immediate flags. If an actual CPU switch is to be performed,
+          re-write the CSR register with the desired values. */
+       reg = readl(FLOW_CTRL_CPUx_CSR(0));
+       reg &= ~(FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE |
+                FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER);
+
+       /* Program flow controller for immediate wake if requested */
+       if (flags & TEGRA_POWER_CLUSTER_IMMEDIATE)
+               reg |= FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE;
+
+       /* Do nothing if no switch actions requested */
+       if (!target_cluster)
+               goto done;
+
+       if ((current_cluster != target_cluster) ||
+               (flags & TEGRA_POWER_CLUSTER_FORCE)) {
+               if (current_cluster != target_cluster) {
+                       // Set up the clocks for the target CPU.
+                       if (cluster_switch_prolog_clock(flags)) {
+                               /* The target CPU does not exist */
+                               goto done;
+                       }
+
+                       /* Set up the flow controller to switch CPUs. */
+                       reg |= FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER;
+               }
+       }
+
+done:
+       writel(reg, FLOW_CTRL_CPUx_CSR(0));
+}
+
+static void cluster_switch_epilog_gic(void)
+{
+       unsigned int max_irq, i;
+       void __iomem *gic_base = IO_ADDRESS(TEGRA_ARM_INT_DIST_BASE);
+
+       /* Nothing to do if currently running on the LP CPU. */
+       if (is_lp_cluster())
+               return;
+
+       /* Reprogram the interrupt affinity because the on the LP CPU,
+          the interrupt distributor affinity regsiters are stubbed out
+          by ARM (reads as zero, writes ignored). So when the LP CPU
+          context save code runs, the affinity registers will read
+          as all zero. This causes all interrupts to be effectively
+          disabled when back on the G CPU because they aren't routable
+          to any CPU. See bug 667720 for details. */
+
+       max_irq = readl(gic_base + GIC_DIST_CTR) & 0x1f;
+       max_irq = (max_irq + 1) * 32;
+
+       for (i = 32; i < max_irq; i += 4)
+               writel(0x01010101, gic_base + GIC_DIST_TARGET + i * 4 / 4);
+}
+
+void tegra_cluster_switch_epilog(unsigned int flags)
+{
+       u32 reg;
+
+       /* Make sure the switch and immediate flags are cleared in
+          the flow controller to prevent undesirable side-effects
+          for future users of the flow controller. */
+       reg = readl(FLOW_CTRL_CPUx_CSR(0));
+       reg &= ~(FLOW_CTRL_CPU_CSR_IMMEDIATE_WAKE |
+                FLOW_CTRL_CPU_CSR_SWITCH_CLUSTER);
+       writel(reg, FLOW_CTRL_CPUx_CSR(0));
+
+       /* Perform post-switch clean-up of the interrupt distributor */
+       cluster_switch_epilog_gic();
+
+       #if SUSPEND_DEBUG_PRINT
+       {
+               struct clk *c = tegra_get_clock_by_name("cpu");
+               DEBUG_SUSPEND(("%s: %s freq %lu\r\n", __func__,
+                       is_lp_cluster() ? "LP" : "G", clk_get_rate(c)));
+       }
+       #endif
+}
+
+int tegra_cluster_control(unsigned int us, unsigned int flags)
+{
+       unsigned int target_cluster = flags & TEGRA_POWER_CLUSTER_MASK;
+       unsigned int current_cluster = is_lp_cluster()
+                                       ? TEGRA_POWER_CLUSTER_LP
+                                       : TEGRA_POWER_CLUSTER_G;
+
+       if ((target_cluster == TEGRA_POWER_CLUSTER_MASK) || !target_cluster)
+               return -EINVAL;
+
+       if (num_online_cpus() > 1)
+               return -EBUSY;
+
+       if ((current_cluster == target_cluster)
+       && !(flags & TEGRA_POWER_CLUSTER_FORCE))
+               return -EEXIST;
+
+       if (flags & TEGRA_POWER_CLUSTER_IMMEDIATE)
+               us = 0;
+
+       DEBUG_SUSPEND(("%s(LP%d): %s->%s %s %s %d\r\n", __func__,
+               (flags & TEGRA_POWER_SDRAM_SELFREFRESH) ? 1 : 2,
+               is_lp_cluster() ? "LP" : "G",
+               (target_cluster == TEGRA_POWER_CLUSTER_G) ? "G" : "LP",
+               (flags & TEGRA_POWER_CLUSTER_IMMEDIATE) ? "immediate" : "",
+               (flags & TEGRA_POWER_CLUSTER_FORCE) ? "force" : "",
+               us));
+
+       local_irq_disable();
+       if (flags & TEGRA_POWER_SDRAM_SELFREFRESH) {
+               if (us)
+                       tegra_lp2_set_trigger(us);
+
+               tegra_suspend_dram(false, flags);
+
+               if (us)
+                       tegra_lp2_set_trigger(0);
+       } else
+               tegra_idle_lp2_last(flags);
+       local_irq_enable();
+
+       DEBUG_SUSPEND(("%s: %s\r\n", __func__, is_lp_cluster() ? "LP" : "G"));
+
+       return 0;
+}
index 3c8eca7..c70f1ad 100644 (file)
@@ -51,6 +51,7 @@
 #include <mach/irqs.h>
 
 #include "board.h"
+#include "clock.h"
 #include "pm.h"
 #include "pm-irq.h"
 #include "sleep.h"
@@ -133,12 +134,21 @@ static void __iomem *evp_reset =
 #define CLK_RESET_CCLK_BURST_POLICY_PLLM   3
 #define CLK_RESET_CCLK_BURST_POLICY_PLLX   8
 
-#define FLOW_CTRL_CPU_CSR(cpu) (0x8 + 0x10 * (cpu))
+#define FLOW_CTRL_CPU_CSR(cpu) ((cpu) == 0 ? 0x8 : (0x18 + 8 * ((cpu) - 1)))
 #define FLOW_CTRL_HALT_CPU(cpu) ((cpu) == 0 ? 0x0 : (0x4 + cpu * 0x10))
 
-#define FLOW_CTRL_CSR_CLEAR_EVENT      (1 << 14)
-#define FLOW_CTRL_CSR_WFE_BITMAP       (3 << 4)
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
 #define FLOW_CTRL_CSR_WFE_CPU0         (1 << 4)
+#define FLOW_CTRL_CSR_WFE_BITMAP       (3 << 4)
+#define FLOW_CTRL_CSR_WFI_BITMAP       0
+#else
+#define FLOW_CTRL_CSR_WFE_BITMAP       (0xF << 4)
+#define FLOW_CTRL_CSR_WFI_CPU0         (1 << 8)
+#define FLOW_CTRL_CSR_WFI_BITMAP       (0xF << 8)
+#endif
+
+#define FLOW_CTRL_CSR_CLEAR_INTR       (1 << 15)
+#define FLOW_CTRL_CSR_CLEAR_EVENT      (1 << 14)
 #define FLOW_CTRL_CSR_ENABLE           (1 << 0)
 
 #define EMC_MRW_0              0x0e8
@@ -285,16 +295,46 @@ static void restore_cpu_complex(void)
        writel(tegra_sctx.pllp_base, clk_rst + CLK_RESET_PLLP_BASE);
        writel(tegra_sctx.pllp_outa, clk_rst + CLK_RESET_PLLP_OUTA);
        writel(tegra_sctx.pllp_outb, clk_rst + CLK_RESET_PLLP_OUTB);
-       udelay(300);
-       writel(tegra_sctx.cclk_divider, clk_rst + CLK_RESET_CCLK_DIVIDER);
-       writel(tegra_sctx.cpu_burst, clk_rst + CLK_RESET_CCLK_BURST);
+
+       /* Is CPU complex already running on PLLX? */
+       reg = readl(clk_rst + CLK_RESET_CCLK_BURST);
+       reg &= 0xF;
+       if (reg != 0x8) {
+               /* restore original burst policy setting; PLLX state restored
+                * by CPU boot-up code - wait for PLL stabilization if PLLX
+                * was enabled */
+
+               BUG_ON(readl(clk_rst + CLK_RESET_PLLX_BASE) !=
+                      tegra_sctx.pllx_base);
+
+               if (tegra_sctx.pllx_base & (1<<30)) {
+#if USE_PLL_LOCK_BITS
+                       /* Enable lock detector */
+                       reg = readl(clk_rst + CLK_RESET_PLLX_MISC);
+                       reg |= 1<<18;
+                       writel(reg, clk_rst + CLK_RESET_PLLX_MISC);
+                       while (!(readl(clk_rst + CLK_RESET_PLLX_BASE) &&
+                                (1<<27)))
+                               cpu_relax();
+#else
+                       udelay(300);
+#endif
+               }
+               writel(tegra_sctx.cclk_divider, clk_rst +
+                      CLK_RESET_CCLK_DIVIDER);
+               writel(tegra_sctx.cpu_burst, clk_rst +
+                      CLK_RESET_CCLK_BURST);
+       }
+
        writel(tegra_sctx.clk_csite_src, clk_rst + CLK_RESET_SOURCE_CSITE);
 
        /* do not power-gate the CPU when flow controlled */
        for (i = 0; i < num_possible_cpus(); i++) {
                reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR(i));
                reg &= ~FLOW_CTRL_CSR_WFE_BITMAP;       /* clear wfe bitmap */
+               reg &= ~FLOW_CTRL_CSR_WFI_BITMAP;       /* clear wfi bitmap */
                reg &= ~FLOW_CTRL_CSR_ENABLE;           /* clear enable */
+               reg |= FLOW_CTRL_CSR_CLEAR_INTR;        /* clear intr */
                reg |= FLOW_CTRL_CSR_CLEAR_EVENT;       /* clear event */
                writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR(i));
                wmb();
@@ -331,8 +371,13 @@ static void suspend_cpu_complex(void)
 
        reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR(cpu));
        reg &= ~FLOW_CTRL_CSR_WFE_BITMAP;       /* clear wfe bitmap */
+       reg &= ~FLOW_CTRL_CSR_WFI_BITMAP;       /* clear wfi bitmap */
        reg |= FLOW_CTRL_CSR_CLEAR_EVENT;       /* clear event flag */
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
        reg |= FLOW_CTRL_CSR_WFE_CPU0 << cpu;   /* enable power gating on wfe */
+#else
+       reg |= FLOW_CTRL_CSR_WFI_CPU0 << cpu;   /* enable power gating on wfi */
+#endif
        reg |= FLOW_CTRL_CSR_ENABLE;            /* enable power gating */
        writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR(cpu));
        wmb();
@@ -342,6 +387,7 @@ static void suspend_cpu_complex(void)
                        continue;
                reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR(i));
                reg |= FLOW_CTRL_CSR_CLEAR_EVENT;
+               reg |= FLOW_CTRL_CSR_CLEAR_INTR;
                writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR(i));
                writel(0, flow_ctrl + FLOW_CTRL_HALT_CPU(i));
                wmb();
@@ -373,7 +419,7 @@ int tegra_reset_other_cpus(int cpu)
        return 0;
 }
 
-void tegra_idle_lp2_last(void)
+void tegra_idle_lp2_last(unsigned int flags)
 {
        u32 reg;
        int i;
@@ -389,6 +435,7 @@ void tegra_idle_lp2_last(void)
        reg = readl(pmc + PMC_CTRL);
        reg |= TEGRA_POWER_CPU_PWRREQ_OE;
        reg |= TEGRA_POWER_PWRREQ_OE;
+       reg |= flags;
        reg &= ~TEGRA_POWER_EFFECT_LP0;
        pmc_32kwritel(reg, PMC_CTRL);
 
@@ -401,6 +448,9 @@ void tegra_idle_lp2_last(void)
        set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer,
                clk_get_rate_all_locked(tegra_pclk));
 
+       if (flags & TEGRA_POWER_CLUSTER_MASK)
+               tegra_cluster_switch_prolog(mode);
+
        cpu_complex_pm_enter();
 
        suspend_cpu_complex();
@@ -414,6 +464,9 @@ void tegra_idle_lp2_last(void)
        restore_cpu_complex();
        cpu_complex_pm_exit();
 
+       if (flags & TEGRA_POWER_CLUSTER_MASK)
+               tegra_cluster_switch_epilog(mode);
+
        for_each_online_cpu(i)
                if (i != cpu)
                        tegra_wake_reset_cpu(i);
@@ -437,7 +490,7 @@ void tegra_idle_lp2(void)
        cpu_pm_enter();
 
        if (last_cpu)
-               tegra_idle_lp2_last();
+               tegra_idle_lp2_last(0);
        else
                tegra_sleep_wfi(PLAT_PHYS_OFFSET - PAGE_OFFSET);
 
index 76113b6..e892d9f 100644 (file)
@@ -42,7 +42,21 @@ struct tegra_suspend_platform_data {
 unsigned long tegra_cpu_power_good_time(void);
 unsigned long tegra_cpu_power_off_time(void);
 
+#define TEGRA_POWER_SDRAM_SELFREFRESH  0x400   /* SDRAM is in self-refresh */
+
+#define TEGRA_POWER_CLUSTER_G          0x1000  /* G CPU */
+#define TEGRA_POWER_CLUSTER_LP         0x2000  /* LP CPU */
+#define TEGRA_POWER_CLUSTER_MASK       0x3000
+#define TEGRA_POWER_CLUSTER_IMMEDIATE  0x4000  /* Immediate wake */
+#define TEGRA_POWER_CLUSTER_FORCE      0x8000  /* Force switch */
+
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
 void tegra2_lp0_suspend_init(void);
+#else
+static inline void tegra2_lp0_suspend_init(void)
+{
+}
+#endif
 void __init tegra_init_suspend(struct tegra_suspend_platform_data *plat);
 
 void tegra_idle_lp2(void);
@@ -54,4 +68,19 @@ u64 tegra_rtc_read_ms(void);
  */
 extern void (*tegra_deep_sleep)(int);
 
+void tegra_idle_lp2_last(unsigned int flags);
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
+static inline int tegra_cluster_control(unsigned int us, unsigned int flags)
+{ return -EPERM; }
+#define tegra_cluster_switch_prolog(flags) do {} while(0)
+#define tegra_cluster_switch_epilog(flags) do {} while(0)
+static inline unsigned int is_lp_cluster(void)
+{ return 0; }
+#else
+int tegra_cluster_control(unsigned int us, unsigned int flags);
+void tegra_cluster_switch_prolog(unsigned int flags);
+void tegra_cluster_switch_epilog(unsigned int flags);
+unsigned int is_lp_cluster(void);
+#endif
+
 #endif /* _MACH_TEGRA_SUSPEND_H_ */
index 8f6f968..847e8b9 100644 (file)
@@ -59,7 +59,7 @@ static int tegra_powergate_set(int id, bool new_state)
 
        spin_lock_irqsave(&tegra_powergate_lock, flags);
 
-       status = pmc_read(PWRGATE_STATUS) & (1 << id);
+       status = !!(pmc_read(PWRGATE_STATUS) & (1 << id));
 
        if (status == new_state) {
                spin_unlock_irqrestore(&tegra_powergate_lock, flags);
@@ -107,6 +107,7 @@ int tegra_powergate_remove_clamping(int id)
        if (id < 0 || id >= TEGRA_NUM_POWERGATE)
                return -EINVAL;
 
+#ifdef CONFIG_ARCH_TEGRA_2x_SOC
        /*
         * Tegra 2 has a bug where PCIE and VDE clamping masks are
         * swapped relatively to the partition ids
@@ -116,6 +117,7 @@ int tegra_powergate_remove_clamping(int id)
        else if (id == TEGRA_POWERGATE_PCIE)
                mask = (1 << TEGRA_POWERGATE_VDEC);
        else
+#endif
                mask = (1 << id);
 
        pmc_write(mask, REMOVE_CLAMPING);
@@ -183,13 +185,22 @@ err_power:
 #ifdef CONFIG_DEBUG_FS
 
 static const char * const powergate_name[] = {
-       [TEGRA_POWERGATE_CPU]   = "cpu",
-       [TEGRA_POWERGATE_3D]    = "3d",
+       [TEGRA_POWERGATE_CPU]   = "cpu0",
+       [TEGRA_POWERGATE_3D]    = "3d0",
        [TEGRA_POWERGATE_VENC]  = "venc",
        [TEGRA_POWERGATE_VDEC]  = "vdec",
        [TEGRA_POWERGATE_PCIE]  = "pcie",
        [TEGRA_POWERGATE_L2]    = "l2",
        [TEGRA_POWERGATE_MPE]   = "mpe",
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+       [TEGRA_POWERGATE_HEG]   = "heg",
+       [TEGRA_POWERGATE_SATA]  = "sata",
+       [TEGRA_POWERGATE_CPU1]  = "cpu1",
+       [TEGRA_POWERGATE_CPU2]  = "cpu2",
+       [TEGRA_POWERGATE_CPU3]  = "cpu3",
+       [TEGRA_POWERGATE_A9LP]  = "a9lp",
+       [TEGRA_POWERGATE_3D1]   = "3d1",
+#endif
 };
 
 static int powergate_show(struct seq_file *s, void *data)
diff --git a/arch/arm/mach-tegra/sysfs-cluster.c b/arch/arm/mach-tegra/sysfs-cluster.c
new file mode 100644 (file)
index 0000000..50f9535
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * This driver creates the /sys/kernel/cluster node and attributes for CPU
+ * switch testing. Node attributes:
+ *
+ * active: currently active CPU (G or LP)
+ *             write:  'g'      = switch to G CPU
+ *                     'lp'     = switch to LP CPU
+ *                     'toggle' = switch to the other CPU
+ *             read: returns the currently active CPU (g or lp)
+ *
+ * force: force switch even if already on target CPU
+ *             write:  '0' = do not perform switch if
+ *                           active CPU == target CPU (default)
+ *                     '1' = force switch regardless of
+ *                           currently active CPU
+ *             read: returns the current status of the force flag
+ *
+ * immediate: request immediate wake-up from switch request
+ *             write:  '0' = non-immediate wake-up on next interrupt (default)
+ *                     '1' = immediate wake-up
+ *             read: returns the current status of the immediate flag
+ *
+ * power_mode: power mode to use for switch (LP1 or LP2)
+ *             write:  '1' = use LP1 power mode
+ *                     '2' = use LP2 power mode (default)
+ *             read: returns the current status of the immediate flag
+ *
+ * wake_ms: wake time (in milliseconds) -- ignored if immediate==1
+ *             write:  '0' = wake up at the next non-timer interrupt
+ *                     'n' = (n > 0) wake-up after 'n' milliseconds or the
+ *                           next non-timer interrupt (whichever comes first)
+ *             read: returns the current wake_ms value
+ *
+ * Writing the force, immediate and wake_ms attributes simply updates the
+ * state of internal variables that will be used for the next switch request.
+ * Writing to the active attribute initates a switch request using the
+ * current values of the force, immediate, and wake_ms attributes.
+ *
+ * The OS tick timer is not a valid interrupt source for waking up following
+ * a switch request. This is because the kernel uses local timers that are
+ * part of the CPU complex. These get shut down when the CPU complex is
+ * placed into reset by the switch request. If you want a timed wake up
+ * from a switch, you must specify a positive wake_ms value. This will
+ * ensure that a non-local timer is programmed to fire an interrupt
+ * after the desired interval.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sysfs.h>
+#include <linux/kobject.h>
+#include <linux/smp.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include "power.h"
+
+#define SYSFS_CLUSTER_PRINTS      1    /* Nonzero: enable status prints */
+#define SYSFS_CLUSTER_DEBUG_PRINTS 0   /* Nonzero: enable debug prints */
+#define SYSFS_CLUSTER_POWER_MODE   1   /* Nonzero: use power modes other than LP2*/
+
+#if SYSFS_CLUSTER_DEBUG_PRINTS
+#define DEBUG_CLUSTER(x) printk x
+#else
+#define DEBUG_CLUSTER(x)
+#endif
+
+#if SYSFS_CLUSTER_PRINTS
+#define PRINT_CLUSTER(x) printk x
+#else
+#define PRINT_CLUSTER(x)
+#endif
+
+#define FLOW_CTRL_CLUSTER_CONTROL \
+       (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE) + 0x2c)
+#define FLOW_CTRL_CPUx_CSR(cpu)        \
+       (IO_ADDRESS(TEGRA_FLOW_CTRL_BASE + ((cpu)?(((cpu)-1)*8 + 0x18) : 0x8)))
+
+static struct kobject *cluster_kobj;
+static spinlock_t cluster_lock;
+static unsigned int flags = 0;
+static unsigned int power_mode = 2;
+static unsigned int wake_ms = 0;
+
+static ssize_t sysfscluster_show(struct kobject *kobj,
+               struct kobj_attribute *attr, char *buf);
+
+static ssize_t sysfscluster_store(struct kobject *kobj,
+               struct kobj_attribute *attr, const char *buf, size_t count);
+
+/* Active CPU: "G", "LP", "toggle" */
+static struct kobj_attribute cluster_active_attr =
+               __ATTR(active, 0640, sysfscluster_show, sysfscluster_store);
+
+/* Immediate wake-up when performing switch: 0, 1 */
+static struct kobj_attribute cluster_immediate_attr =
+               __ATTR(immediate, 0640, sysfscluster_show, sysfscluster_store);
+
+/* Force power transition even if already on the desired CPU: 0, 1 */
+static struct kobj_attribute cluster_force_attr =
+               __ATTR(force, 0640, sysfscluster_show, sysfscluster_store);
+
+/* Wake time (in milliseconds) */
+static struct kobj_attribute cluster_wake_ms_attr =
+               __ATTR(wake_ms, 0640, sysfscluster_show, sysfscluster_store);
+
+#if SYSFS_CLUSTER_POWER_MODE
+/* LPx power mode to use when switching CPUs: 1, 2 */
+static struct kobj_attribute cluster_powermode_attr =
+               __ATTR(power_mode, 0640, sysfscluster_show, sysfscluster_store);
+#endif
+
+typedef enum
+{
+       ClusterAttr_Invalid = 0,
+       ClusterAttr_Active,
+       ClusterAttr_Immediate,
+       ClusterAttr_Force,
+       ClusterAttr_WakeMs,
+#if SYSFS_CLUSTER_POWER_MODE
+       ClusterAttr_PowerMode
+#endif
+} ClusterAttr;
+
+static ClusterAttr GetClusterAttr(const char *name)
+{
+       if (!strcmp(name, "active"))
+               return ClusterAttr_Active;
+       if (!strcmp(name, "immediate"))
+               return ClusterAttr_Immediate;
+       if (!strcmp(name, "force"))
+               return ClusterAttr_Force;
+       if (!strcmp(name, "wake_ms"))
+               return ClusterAttr_WakeMs;
+#if SYSFS_CLUSTER_POWER_MODE
+       if (!strcmp(name, "power_mode"))
+               return ClusterAttr_PowerMode;
+#endif
+       DEBUG_CLUSTER(("GetClusterAttr(%s): invalid\n", name));
+       return ClusterAttr_Invalid;
+}
+
+static ssize_t sysfscluster_show(struct kobject *kobj,
+               struct kobj_attribute *attr, char *buf)
+{
+       ClusterAttr type;
+       ssize_t len;
+
+       DEBUG_CLUSTER(("+sysfscluster_show\n"));
+
+       type = GetClusterAttr(attr->attr.name);
+       switch (type) {
+       case ClusterAttr_Active:
+               len = sprintf(buf, "%s\n", is_lp_cluster() ? "LP" : "G");
+               break;
+
+       case ClusterAttr_Immediate:
+               len = sprintf(buf, "%d\n",
+                             ((flags & TEGRA_POWER_CLUSTER_IMMEDIATE) != 0));
+               break;
+
+       case ClusterAttr_Force:
+               len = sprintf(buf, "%d\n",
+                             ((flags & TEGRA_POWER_CLUSTER_FORCE) != 0));
+               break;
+
+       case ClusterAttr_WakeMs:
+               len = sprintf(buf, "%d\n", wake_ms);
+               break;
+
+#if SYSFS_CLUSTER_POWER_MODE
+       case ClusterAttr_PowerMode:
+               len = sprintf(buf, "%d\n", power_mode);
+               break;
+#endif
+
+       default:
+               len = sprintf(buf, "invalid\n");
+               break;
+       }
+
+       DEBUG_CLUSTER(("-sysfscluster_show\n"));
+       return len;
+}
+
+static ssize_t sysfscluster_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       ClusterAttr type;
+       ssize_t ret = count--;
+       unsigned request;
+       int e;
+       int tmp;
+       int cnt;
+
+       DEBUG_CLUSTER(("+sysfscluster_store: %p, %d\n", buf, count));
+
+       /* The count includes data bytes follow by a line feed character. */
+       if (!buf || (count < 1)) {
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       type = GetClusterAttr(attr->attr.name);
+
+       spin_lock(&cluster_lock);
+
+       switch (type) {
+       case ClusterAttr_Active:
+               if (!strncasecmp(buf, "g", count)) {
+                       flags &= ~TEGRA_POWER_CLUSTER_MASK;
+                       flags |= TEGRA_POWER_CLUSTER_G;
+               } else if (!strncasecmp(buf, "lp", count)) {
+                       flags &= ~TEGRA_POWER_CLUSTER_MASK;
+                       flags |= TEGRA_POWER_CLUSTER_LP;
+               } else if (!strncasecmp(buf, "toggle", count)) {
+                       flags &= ~TEGRA_POWER_CLUSTER_MASK;
+                       if (is_lp_cluster())
+                               flags |= TEGRA_POWER_CLUSTER_G;
+                       else
+                               flags |= TEGRA_POWER_CLUSTER_LP;
+               } else {
+                       PRINT_CLUSTER(("cluster/active: '%*.*s' invalid, "
+                               " must be g, lp, or toggle\n",
+                               count, count, buf));
+                       ret = -EINVAL;
+                       break;
+               }
+               PRINT_CLUSTER(("cluster/active -> %s\n",
+                       (flags & TEGRA_POWER_CLUSTER_G) ? "G" : "LP"));
+
+               request = flags;
+#if SYSFS_CLUSTER_POWER_MODE
+               if (power_mode == 1) {
+                       request |= TEGRA_POWER_SDRAM_SELFREFRESH;
+               }
+#endif
+               e = tegra_cluster_control(wake_ms * 1000, request);
+               if (e) {
+                       PRINT_CLUSTER(("cluster/active: request failed (%d)\n",
+                                      e));
+                       ret = e;
+               }
+               break;
+
+       case ClusterAttr_Immediate:
+               if ((count == 1) && (*buf == '0'))
+                       flags &= ~TEGRA_POWER_CLUSTER_IMMEDIATE;
+               else if ((count == 1) && *buf == '1')
+                       flags |= TEGRA_POWER_CLUSTER_IMMEDIATE;
+               else {
+                       PRINT_CLUSTER(("cluster/immediate: '%*.*s' invalid, "
+                               "must be 0 or 1\n", count, count, buf));
+                       ret = -EINVAL;
+                       break;
+               }
+               PRINT_CLUSTER(("cluster/immediate -> %c\n",
+                       (flags & TEGRA_POWER_CLUSTER_IMMEDIATE) ? '1' : '0'));
+               break;
+
+       case ClusterAttr_Force:
+               if ((count == 1) && (*buf == '0'))
+                       flags &= ~TEGRA_POWER_CLUSTER_FORCE;
+               else if ((count == 1) && (*buf == '1'))
+                       flags |= TEGRA_POWER_CLUSTER_FORCE;
+               else {
+                       PRINT_CLUSTER(("cluster/force: '%*.*s' invalid, "
+                               "must be 0 or 1\n", count, count, buf));
+                       ret = -EINVAL;
+                       break;
+               }
+               PRINT_CLUSTER(("cluster/force -> %c\n",
+                       (flags & TEGRA_POWER_CLUSTER_FORCE) ? '1' : '0'));
+               break;
+
+       case ClusterAttr_WakeMs:
+               tmp = 0;
+               cnt = sscanf(buf, "%d\n", &tmp);
+               if ((cnt != 1) || (tmp < 0)) {
+                       PRINT_CLUSTER(("cluster/wake_ms: '%*.*s' is invalid\n",
+                               count, count, buf));
+                       ret = -EINVAL;
+                       break;
+               }
+               wake_ms = tmp;
+               PRINT_CLUSTER(("cluster/wake_ms -> %d\n", wake_ms));
+               break;
+
+#if SYSFS_CLUSTER_POWER_MODE
+       case ClusterAttr_PowerMode:
+               if ((count == 1) && (*buf == '2'))
+                       power_mode = 2;
+               else if ((count == 1) && *buf == '1')
+                       power_mode = 1;
+               else {
+                       PRINT_CLUSTER(("cluster/power_mode: '%*.*s' invalid, "
+                               "must be 2 or 1\n", count, count, buf));
+                       ret = -EINVAL;
+                       break;
+               }
+               PRINT_CLUSTER(("cluster/power_mode -> %d\n", power_mode));
+               break;
+#endif
+
+       default:
+               ret = -ENOENT;
+               break;
+       }
+
+       spin_unlock(&cluster_lock);
+
+fail:
+       DEBUG_CLUSTER(("-sysfscluster_store: %d\n", count));
+       return ret;
+}
+
+#define CREATE_FILE(x) \
+       do { \
+               e = sysfs_create_file(cluster_kobj, &cluster_##x##_attr.attr); \
+               if (e) { \
+                       DEBUG_CLUSTER(("cluster/" __stringify(x) \
+                               ": sysfs_create_file failed!\n")); \
+                       goto fail; \
+               } \
+       } while (0)
+
+static int __init sysfscluster_init(void)
+{
+       int e;
+
+       DEBUG_CLUSTER(("+sysfscluster_init\n"));
+
+       spin_lock_init(&cluster_lock);
+       cluster_kobj = kobject_create_and_add("cluster", kernel_kobj);
+
+       CREATE_FILE(active);
+       CREATE_FILE(immediate);
+       CREATE_FILE(force);
+       CREATE_FILE(wake_ms);
+#if SYSFS_CLUSTER_POWER_MODE
+       CREATE_FILE(powermode);
+#endif
+
+       spin_lock(&cluster_lock);
+       if (is_lp_cluster())
+               flags |= TEGRA_POWER_CLUSTER_LP;
+       else
+               flags |= TEGRA_POWER_CLUSTER_G;
+       spin_unlock(&cluster_lock);
+
+fail:
+       DEBUG_CLUSTER(("-sysfscluster_init\n"));
+       return e;
+}
+
+#define REMOVE_FILE(x) \
+               sysfs_remove_file(cluster_kobj, &cluster_##x##_attr.attr)
+
+static void __exit sysfscluster_exit(void)
+{
+       DEBUG_CLUSTER(("+sysfscluster_exit\n"));
+#if SYSFS_CLUSTER_POWER_MODE
+       REMOVE_FILE(powermode);
+#endif
+       REMOVE_FILE(wake_ms);
+       REMOVE_FILE(force);
+       REMOVE_FILE(immediate);
+       REMOVE_FILE(active);
+       kobject_del(cluster_kobj);
+       DEBUG_CLUSTER(("-sysfscluster_exit\n"));
+}
+
+module_init(sysfscluster_init);
+module_exit(sysfscluster_exit);
+MODULE_LICENSE("GPL");
diff --git a/arch/arm/mach-tegra/sysfs-dcc.c b/arch/arm/mach-tegra/sysfs-dcc.c
new file mode 100644 (file)
index 0000000..fb1cf92
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * Neither the name of the NVIDIA Corporation nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include "nvos.h"
+
+#define DCC_TIMEOUT_US     100000      /* Delay time for DCC timeout (in US) */
+#define CP14_DSCR_WDTRFULL  0x20000000 /* Write Data Transfer Register Full */
+#define SYSFS_DCC_DEBUG_PRINTS 0       /* Set non-zero to enable debug prints */
+
+#if SYSFS_DCC_DEBUG_PRINTS
+#define DEBUG_DCC(x) printk x
+#else
+#define DEBUG_DCC(x)
+#endif
+
+static int DebuggerConnected = 0;  /* -1=not connected, 0=unknown, 1=connected */
+static struct kobject *nvdcc_kobj;
+static spinlock_t dcc_lock;
+static struct list_head dcc_list;
+
+static ssize_t sysfsdcc_show(struct kobject *kobj,
+               struct kobj_attribute *attr, char *buf);
+
+static ssize_t sysfsdcc_store(struct kobject *kobj,
+               struct kobj_attribute *attr, const char *buf, size_t count);
+
+
+static struct kobj_attribute nvdcc_attr =
+               __ATTR(dcc0, 0222, sysfsdcc_show, sysfsdcc_store);
+
+static int write_to_dcc(u32 c)
+{
+       volatile NvU32 dscr;
+
+       /* Have we already determined that there is no debugger connected? */
+       if (DebuggerConnected < 0)
+       {
+               return -ENXIO;
+       }
+
+       /* Read the DSCR. */
+       asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
+
+       /* If DSCR Bit 29 (wDTRFull) is set there is data in the write
+        * register. If it stays there for than the DCC_TIMEOUT_US
+        * period, ignore this write and disable further DCC accesses. */
+       if (dscr & CP14_DSCR_WDTRFULL)
+       {
+               NvU64 start  = NvOsGetTimeUS();
+               NvU64 end    = start + DCC_TIMEOUT_US;
+               NvU64 offset = (end > start) ? 0 : 0 - start;
+               NvU64 now;
+
+               for (;;)
+               {
+                       /* Re-read the DSCR. */
+                       asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
+
+                       /* Previous data still there? */
+                       if (dscr & CP14_DSCR_WDTRFULL)
+                       {
+                               if (end > start)
+                               {
+                                       now = NvOsGetTimeUS();
+
+                                       if ((now >= end) || (now < start))
+                                       {
+                                               goto fail;
+                                       }
+                               }
+                               else
+                               {
+                                       now = offset + NvOsGetTimeUS();
+
+                                       if (now >= (end + offset))
+                                       {
+                                               goto fail;
+                                       }
+                               }
+                       }
+                       else
+                       {
+                               if (DebuggerConnected == 0) {
+                                       /* Debugger connected */
+                                       spin_lock(&dcc_lock);
+                                       DebuggerConnected = 1;
+                                       spin_unlock(&dcc_lock);
+                               }
+                               break;
+                       }
+               }
+       }
+
+       // Write the data into the DCC output register
+       asm volatile ("mcr p14, 0, %0, c0, c5, 0" : : "r" (c) : "cc");
+       return 0;
+
+fail:
+       /* No debugged connected -- disable DCC */
+       spin_lock(&dcc_lock);
+       DebuggerConnected = -1;
+       spin_unlock(&dcc_lock);
+       return -ENXIO;
+}
+
+
+struct tegra_dcc_req {
+       struct list_head node;
+
+       const char *pBuf;
+       unsigned int size;
+};
+
+struct dcc_action {
+       struct tegra_dcc_req req;
+       struct work_struct work;
+       struct list_head node;
+};
+
+
+static void dcc_writer(struct work_struct *work)
+{
+       struct dcc_action *action = container_of(work, struct dcc_action, work);
+       const char *p;
+
+       DEBUG_DCC(("+dcc_writer\n"));
+
+       spin_lock(&dcc_lock);
+       list_del(&action->req.node);
+       spin_unlock(&dcc_lock);
+
+       p = action->req.pBuf;
+       if (p)
+               while ((p < &(action->req.pBuf[action->req.size])) && (*p))
+                       if (write_to_dcc(*p++))
+                               break;
+
+       kfree(action->req.pBuf);
+       kfree(action);
+
+       DEBUG_DCC(("-dcc_writer\n"));
+}
+
+static ssize_t sysfsdcc_show(struct kobject *kobj,
+               struct kobj_attribute *attr, char *buf)
+{
+       DEBUG_DCC(("!sysfsdcc_show\n"));
+       return -EACCES;
+}
+
+static ssize_t sysfsdcc_store(struct kobject *kobj,
+       struct kobj_attribute *attr, const char *buf, size_t count)
+{
+       struct dcc_action *action;
+       char *pBuf;
+       ssize_t ret = count;
+
+       DEBUG_DCC(("+sysfsdcc_store: %p, %d\n", buf, count));
+
+       if (!buf || !count) {
+               ret = -EINVAL;
+               goto fail;
+       }
+
+       pBuf = kmalloc(count+1, GFP_KERNEL);
+       if (!pBuf) {
+               pr_debug("%s: insufficient memory\n", __func__);
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       action = kzalloc(sizeof(*action), GFP_KERNEL);
+       if (!action) {
+               kfree(pBuf);
+               pr_debug("%s: insufficient memory\n", __func__);
+               ret = -ENOMEM;
+               goto fail;
+       }
+
+       strncpy(pBuf, buf, count);
+       pBuf[count] = '\0';
+       action->req.pBuf = pBuf;
+       action->req.size = count;
+
+       INIT_WORK(&action->work, dcc_writer);
+
+       spin_lock(&dcc_lock);
+       list_add_tail(&action->req.node, &dcc_list);
+       spin_unlock(&dcc_lock);
+
+       /* DCC writes can only be performed from CPU0 */
+       schedule_work_on(0, &action->work);
+
+fail:
+       DEBUG_DCC(("-sysfsdcc_store: %d\n", count));
+       return ret;
+}
+
+static int __init sysfsdcc_init(void)
+{
+       spin_lock_init(&dcc_lock);
+       INIT_LIST_HEAD(&dcc_list);
+
+       DEBUG_DCC(("+sysfsdcc_init\n"));
+       nvdcc_kobj = kobject_create_and_add("dcc", kernel_kobj);
+
+       if (sysfs_create_file(nvdcc_kobj, &nvdcc_attr.attr))
+       {
+               DEBUG_DCC(("DCC: sysfs_create_file failed!\n"));
+               return -ENXIO;
+       }
+
+       DEBUG_DCC(("-sysfsdcc_init\n"));
+       return 0;
+}
+
+static void __exit sysfsdcc_exit(void)
+{
+       DEBUG_DCC(("+sysfsdcc_exit\n"));
+       sysfs_remove_file(nvdcc_kobj, &nvdcc_attr.attr);
+       kobject_del(nvdcc_kobj);
+       DEBUG_DCC(("-sysfsdcc_exit\n"));
+}
+
+module_init(sysfsdcc_init);
+module_exit(sysfsdcc_exit);
+MODULE_LICENSE("GPL");
index f0222bd..0ae724c 100644 (file)
@@ -280,18 +280,6 @@ static struct clk_ops tegra_clk_m_ops = {
        .disable        = tegra2_clk_m_disable,
 };
 
-void tegra2_periph_reset_assert(struct clk *c)
-{
-       BUG_ON(!c->ops->reset);
-       c->ops->reset(c, true);
-}
-
-void tegra2_periph_reset_deassert(struct clk *c)
-{
-       BUG_ON(!c->ops->reset);
-       c->ops->reset(c, false);
-}
-
 /* super clock functions */
 /* "super clocks" on tegra have two-stage muxes and a clock skipping
  * super divider.  We will ignore the clock skipping divider, since we
@@ -2643,7 +2631,7 @@ static struct syscore_ops tegra_clk_syscore_ops = {
        .resume = tegra_clk_resume,
 };
 
-void __init tegra2_init_clocks(void)
+void __init tegra_soc_init_clocks(void)
 {
        int i;
        struct clk *c;
index 34a6219..28a450e 100644 (file)
@@ -293,7 +293,7 @@ module_param_cb(disable_core, &tegra_dvfs_disable_core_ops,
 module_param_cb(disable_cpu, &tegra_dvfs_disable_cpu_ops,
        &tegra_dvfs_cpu_disabled, 0644);
 
-void __init tegra2_init_dvfs(void)
+void __init tegra_soc_init_dvfs(void)
 {
        int i;
        struct clk *c;
diff --git a/arch/arm/mach-tegra/tegra3_clocks.c b/arch/arm/mach-tegra/tegra3_clocks.c
new file mode 100644 (file)
index 0000000..161df87
--- /dev/null
@@ -0,0 +1,2396 @@
+/*
+ * arch/arm/mach-tegra/tegra3_clocks.c
+ *
+ * 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/module.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+
+#include <asm/clkdev.h>
+
+#include <mach/iomap.h>
+
+#include "clock.h"
+#include "fuse.h"
+#include "pm.h"
+
+#define RST_DEVICES_L                  0x004
+#define RST_DEVICES_H                  0x008
+#define RST_DEVICES_U                  0x00C
+#define RST_DEVICES_V                  0x358
+#define RST_DEVICES_W                  0x35C
+#define RST_DEVICES_SET_L              0x300
+#define RST_DEVICES_CLR_L              0x304
+#define RST_DEVICES_SET_V              0x430
+#define RST_DEVICES_CLR_V              0x434
+#define RST_DEVICES_NUM                        5
+
+#define CLK_OUT_ENB_L                  0x010
+#define CLK_OUT_ENB_H                  0x014
+#define CLK_OUT_ENB_U                  0x018
+#define CLK_OUT_ENB_V                  0x360
+#define CLK_OUT_ENB_W                  0x364
+#define CLK_OUT_ENB_SET_L              0x320
+#define CLK_OUT_ENB_CLR_L              0x324
+#define CLK_OUT_ENB_SET_V              0x440
+#define CLK_OUT_ENB_CLR_V              0x444
+#define CLK_OUT_ENB_NUM                        5
+
+#define PERIPH_CLK_TO_BIT(c)           (1 << (c->u.periph.clk_num % 32))
+#define PERIPH_CLK_TO_RST_REG(c)       \
+       periph_clk_to_reg((c), RST_DEVICES_L, RST_DEVICES_V, 4)
+#define PERIPH_CLK_TO_RST_SET_REG(c)   \
+       periph_clk_to_reg((c), RST_DEVICES_SET_L, RST_DEVICES_SET_V, 8)
+#define PERIPH_CLK_TO_RST_CLR_REG(c)   \
+       periph_clk_to_reg((c), RST_DEVICES_CLR_L, RST_DEVICES_CLR_V, 8)
+
+#define PERIPH_CLK_TO_ENB_REG(c)       \
+       periph_clk_to_reg((c), CLK_OUT_ENB_L, CLK_OUT_ENB_V, 4)
+#define PERIPH_CLK_TO_ENB_SET_REG(c)   \
+       periph_clk_to_reg((c), CLK_OUT_ENB_SET_L, CLK_OUT_ENB_SET_V, 8)
+#define PERIPH_CLK_TO_ENB_CLR_REG(c)   \
+       periph_clk_to_reg((c), CLK_OUT_ENB_CLR_L, CLK_OUT_ENB_CLR_V, 8)
+
+#define CLK_MASK_ARM                   0x44
+#define MISC_CLK_ENB                   0x48
+
+#define OSC_CTRL                       0x50
+#define OSC_CTRL_OSC_FREQ_MASK         (0xF<<28)
+#define OSC_CTRL_OSC_FREQ_13MHZ                (0x0<<28)
+#define OSC_CTRL_OSC_FREQ_19_2MHZ      (0x4<<28)
+#define OSC_CTRL_OSC_FREQ_12MHZ                (0x8<<28)
+#define OSC_CTRL_OSC_FREQ_26MHZ                (0xC<<28)
+#define OSC_CTRL_OSC_FREQ_16_8MHZ      (0x1<<28)
+#define OSC_CTRL_OSC_FREQ_38_4MHZ      (0x5<<28)
+#define OSC_CTRL_OSC_FREQ_48MHZ                (0x9<<28)
+#define OSC_CTRL_MASK                  (0x3f2 | OSC_CTRL_OSC_FREQ_MASK)
+
+#define OSC_CTRL_PLL_REF_DIV_MASK      (3<<26)
+#define OSC_CTRL_PLL_REF_DIV_1         (0<<26)
+#define OSC_CTRL_PLL_REF_DIV_2         (1<<26)
+#define OSC_CTRL_PLL_REF_DIV_4         (2<<26)
+
+#define OSC_FREQ_DET                   0x58
+#define OSC_FREQ_DET_TRIG              (1<<31)
+
+#define OSC_FREQ_DET_STATUS            0x5C
+#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_NUM1 \
+       ((PERIPH_CLK_SOURCE_OSC - PERIPH_CLK_SOURCE_I2S1) / 4)
+
+#define PERIPH_CLK_SOURCE_G3D2         0x3b0
+#define PERIPH_CLK_SOURCE_SE           0x42c
+#define PERIPH_CLK_SOURCE_NUM2 \
+       ((PERIPH_CLK_SOURCE_G3D2 - PERIPH_CLK_SOURCE_SE) / 4)
+
+#define PERIPH_CLK_SOURCE_NUM          (PERIPH_CLK_SOURCE_NUM1 + \
+                                        PERIPH_CLK_SOURCE_NUM2)
+
+#define PERIPH_CLK_SOURCE_MASK(c)      \
+       (((c)->flags & MUX8) ? (7<<29) :\
+               (((c)->flags & MUX_PWM) ? (3 << 28) : (3<<30)))
+#define PERIPH_CLK_SOURCE_SHIFT(c)     \
+       (((c)->flags & MUX8) ? 29 : (((c)->flags & MUX_PWM) ? 28 : 30))
+#define PERIPH_CLK_SOURCE_DIVU71_MASK  0xFF
+#define PERIPH_CLK_SOURCE_DIVU16_MASK  0xFFFF
+#define PERIPH_CLK_SOURCE_DIV_SHIFT    0
+
+#define AUDIO_SYNC_SOURCE_MASK         0x0F
+#define AUDIO_SYNC_DISABLE_BIT         0x10
+#define AUDIO_SYNC_TAP_NIBBLE_SHIFT(c) ((c->reg_shift - 24) * 4)
+
+#define PLL_BASE                       0x0
+#define PLL_BASE_BYPASS                        (1<<31)
+#define PLL_BASE_ENABLE                        (1<<30)
+#define PLL_BASE_REF_ENABLE            (1<<29)
+#define PLL_BASE_OVERRIDE              (1<<28)
+#define PLL_BASE_LOCK                  (1<<27)
+#define PLL_BASE_DIVP_MASK             (0x7<<20)
+#define PLL_BASE_DIVP_SHIFT            20
+#define PLL_BASE_DIVN_MASK             (0x3FF<<8)
+#define PLL_BASE_DIVN_SHIFT            8
+#define PLL_BASE_DIVM_MASK             (0x1F)
+#define PLL_BASE_DIVM_SHIFT            0
+
+#define PLL_OUT_RATIO_MASK             (0xFF<<8)
+#define PLL_OUT_RATIO_SHIFT            8
+#define PLL_OUT_OVERRIDE               (1<<2)
+#define PLL_OUT_CLKEN                  (1<<1)
+#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 | PLLD)) ? (1<<22) : (1<<18))
+
+#define PLL_MISC_DCCON_SHIFT           20
+#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_LFCON_MASK            (0xF<<PLL_MISC_LFCON_SHIFT)
+#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 SUPER_CLK_MUX                  0x00
+#define SUPER_STATE_SHIFT              28
+#define SUPER_STATE_MASK               (0xF << SUPER_STATE_SHIFT)
+#define SUPER_STATE_STANDBY            (0x0 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_IDLE               (0x1 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_RUN                        (0x2 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_IRQ                        (0x3 << SUPER_STATE_SHIFT)
+#define SUPER_STATE_FIQ                        (0x4 << SUPER_STATE_SHIFT)
+#define SUPER_SOURCE_MASK              0xF
+#define        SUPER_FIQ_SOURCE_SHIFT          12
+#define        SUPER_IRQ_SOURCE_SHIFT          8
+#define        SUPER_RUN_SOURCE_SHIFT          4
+#define        SUPER_IDLE_SOURCE_SHIFT         0
+
+#define SUPER_CLK_DIVIDER              0x04
+#define IS_PLLX_DIV2_BYPASS            (clk_readl(0x370) & (1<<16))
+/* FIXME: replace with global is_lp_cluster() ? */
+#define IS_LP_CLUSTER                  (flow_readl(0x2c) & 1)
+
+#define BUS_CLK_DISABLE                        (1<<3)
+#define BUS_CLK_DIV_MASK               0x3
+
+#define PMC_CTRL                       0x0
+ #define PMC_CTRL_BLINK_ENB            (1 << 7)
+
+#define PMC_DPD_PADS_ORIDE             0x1c
+ #define PMC_DPD_PADS_ORIDE_BLINK_ENB  (1 << 20)
+
+#define PMC_BLINK_TIMER_DATA_ON_SHIFT  0
+#define PMC_BLINK_TIMER_DATA_ON_MASK   0x7fff
+#define PMC_BLINK_TIMER_ENB            (1 << 15)
+#define PMC_BLINK_TIMER_DATA_OFF_SHIFT 16
+#define PMC_BLINK_TIMER_DATA_OFF_MASK  0xffff
+
+static void __iomem *reg_clk_base = IO_ADDRESS(TEGRA_CLK_RESET_BASE);
+static void __iomem *reg_pmc_base = IO_ADDRESS(TEGRA_PMC_BASE);
+static void __iomem *reg_flow_base = IO_ADDRESS(TEGRA_FLOW_CTRL_BASE);
+
+/*
+ * Some peripheral clocks share an enable bit, so refcount the enable bits
+ * in registers CLK_ENABLE_L, ... CLK_ENABLE_W
+ */
+static int tegra_periph_clk_enable_refcount[CLK_OUT_ENB_NUM * 32];
+
+#define clk_writel(value, reg) \
+       __raw_writel(value, (u32)reg_clk_base + (reg))
+#define clk_readl(reg) \
+       __raw_readl((u32)reg_clk_base + (reg))
+#define pmc_writel(value, reg) \
+       __raw_writel(value, (u32)reg_pmc_base + (reg))
+#define pmc_readl(reg) \
+       __raw_readl((u32)reg_pmc_base + (reg))
+#define flow_readl(reg) \
+       __raw_readl((u32)reg_flow_base + (reg))
+
+static inline u32 periph_clk_to_reg(
+       struct clk *c, u32 reg_L, u32 reg_V, int offs)
+{
+       u32 reg = c->u.periph.clk_num / 32;
+       BUG_ON(reg >= RST_DEVICES_NUM);
+       if (reg < 3) {
+               reg = reg_L + (reg * offs);
+       } else {
+               reg = reg_V + ((reg - 3) * offs);
+       }
+       return reg;
+}
+
+unsigned long clk_measure_input_freq(void)
+{
+       u32 clock_autodetect;
+       clk_writel(OSC_FREQ_DET_TRIG | 1, OSC_FREQ_DET);
+       do {} while (clk_readl(OSC_FREQ_DET_STATUS) & OSC_FREQ_DET_BUSY);
+       clock_autodetect = clk_readl(OSC_FREQ_DET_STATUS);
+       if (clock_autodetect >= 732 - 3 && clock_autodetect <= 732 + 3) {
+               return 12000000;
+       } else if (clock_autodetect >= 794 - 3 && clock_autodetect <= 794 + 3) {
+               return 13000000;
+       } else if (clock_autodetect >= 1172 - 3 && clock_autodetect <= 1172 + 3) {
+               return 19200000;
+       } else if (clock_autodetect >= 1587 - 3 && clock_autodetect <= 1587 + 3) {
+               return 26000000;
+       } else if (clock_autodetect >= 1025 - 3 && clock_autodetect <= 1025 + 3) {
+               return 16800000;
+       } else if (clock_autodetect >= 2344 - 3 && clock_autodetect <= 2344 + 3) {
+               return 38400000;
+       } else if (clock_autodetect >= 2928 - 3 && clock_autodetect <= 2928 + 3) {
+               return 48000000;
+       } else {
+               pr_err("%s: Unexpected clock autodetect value %d", __func__, clock_autodetect);
+               BUG();
+               return 0;
+       }
+}
+
+static int clk_div71_get_divider(unsigned long parent_rate, unsigned long rate)
+{
+       s64 divider_u71 = parent_rate * 2;
+       divider_u71 += rate - 1;
+       do_div(divider_u71, rate);
+
+       if (divider_u71 - 2 < 0)
+               return 0;
+
+       if (divider_u71 - 2 > 255)
+               return -EINVAL;
+
+       return divider_u71 - 2;
+}
+
+static int clk_div16_get_divider(unsigned long parent_rate, unsigned long rate)
+{
+       s64 divider_u16;
+
+       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 tegra3_clk_m_autodetect_rate(struct clk *c)
+{
+       u32 osc_ctrl = clk_readl(OSC_CTRL);
+       u32 auto_clock_control = osc_ctrl & ~OSC_CTRL_OSC_FREQ_MASK;
+       u32 pll_ref_div = osc_ctrl & OSC_CTRL_PLL_REF_DIV_MASK;
+
+       c->rate = clk_measure_input_freq();
+       switch (c->rate) {
+       case 12000000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_12MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+               break;
+       case 13000000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_13MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+               break;
+       case 19200000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_19_2MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+               break;
+       case 26000000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_26MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+               break;
+       case 16800000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_16_8MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_1);
+               break;
+       case 38400000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_38_4MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_2);
+               break;
+       case 48000000:
+               auto_clock_control |= OSC_CTRL_OSC_FREQ_48MHZ;
+               BUG_ON(pll_ref_div != OSC_CTRL_PLL_REF_DIV_4);
+               break;
+       default:
+               pr_err("%s: Unexpected clock rate %ld", __func__, c->rate);
+               BUG();
+       }
+       clk_writel(auto_clock_control, OSC_CTRL);
+       return c->rate;
+}
+
+static void tegra3_clk_m_init(struct clk *c)
+{
+       pr_debug("%s on clock %s\n", __func__, c->name);
+       tegra3_clk_m_autodetect_rate(c);
+}
+
+static int tegra3_clk_m_enable(struct clk *c)
+{
+       pr_debug("%s on clock %s\n", __func__, c->name);
+       return 0;
+}
+
+static void tegra3_clk_m_disable(struct clk *c)
+{
+       pr_debug("%s on clock %s\n", __func__, c->name);
+       BUG();
+}
+
+static struct clk_ops tegra_clk_m_ops = {
+       .init           = tegra3_clk_m_init,
+       .enable         = tegra3_clk_m_enable,
+       .disable        = tegra3_clk_m_disable,
+};
+
+/* PLL reference divider functions */
+static void tegra3_pll_ref_init(struct clk *c)
+{
+       u32 pll_ref_div = clk_readl(OSC_CTRL) & OSC_CTRL_PLL_REF_DIV_MASK;
+       pr_debug("%s on clock %s\n", __func__, c->name);
+
+       switch (pll_ref_div) {
+       case OSC_CTRL_PLL_REF_DIV_1:
+               c->div = 1;
+               break;
+       case OSC_CTRL_PLL_REF_DIV_2:
+               c->div = 2;
+               break;
+       case OSC_CTRL_PLL_REF_DIV_4:
+               c->div = 4;
+               break;
+       default:
+               pr_err("%s: Invalid pll ref divider %d", __func__, pll_ref_div);
+               BUG();
+       }
+       c->mul = 1;
+       c->state = ON;
+}
+
+static struct clk_ops tegra_pll_ref_ops = {
+       .init           = tegra3_pll_ref_init,
+       .enable         = tegra3_clk_m_enable,
+       .disable        = tegra3_clk_m_disable,
+};
+
+/* super clock functions */
+/* "super clocks" on tegra have two-stage muxes and a clock skipping
+ * super divider.  We will ignore the clock skipping divider, since we
+ * can't lower the voltage when using the clock skip, but we can if we
+ * lower the PLL frequency.
+ */
+static void tegra3_super_clk_init(struct clk *c)
+{
+       u32 val;
+       int source;
+       int shift;
+       const struct clk_mux_sel *sel;
+       val = clk_readl(c->reg + SUPER_CLK_MUX);
+       c->state = ON;
+       BUG_ON(((val & SUPER_STATE_MASK) != SUPER_STATE_RUN) &&
+               ((val & SUPER_STATE_MASK) != SUPER_STATE_IDLE));
+       shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
+               SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
+       source = (val >> shift) & SUPER_SOURCE_MASK;
+       for (sel = c->inputs; sel->input != NULL; sel++) {
+               if (sel->value == source)
+                       break;
+       }
+       BUG_ON(sel->input == NULL);
+       c->parent = sel->input;
+
+       INIT_LIST_HEAD(&c->u.shared_bus.list);
+}
+
+static int tegra3_super_clk_enable(struct clk *c)
+{
+       clk_writel(0, c->reg + SUPER_CLK_DIVIDER);
+       return 0;
+}
+
+static void tegra3_super_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 tegra3_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));
+       shift = ((val & SUPER_STATE_MASK) == SUPER_STATE_IDLE) ?
+               SUPER_IDLE_SOURCE_SHIFT : SUPER_RUN_SOURCE_SHIFT;
+       for (sel = c->inputs; sel->input != NULL; sel++) {
+               if (sel->input == p) {
+                       val &= ~(SUPER_SOURCE_MASK << shift);
+                       val |= sel->value << shift;
+
+                       if (c->refcnt)
+                               clk_enable(p);
+
+                       clk_writel(val, c->reg);
+
+                       if (c->refcnt && c->parent)
+                               clk_disable(c->parent);
+
+                       clk_reparent(c, p);
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+/*
+ * Super clocks have "clock skippers" instead of dividers.  Dividing using
+ * a clock skipper does not allow the voltage to be scaled down, so instead
+ * adjust the rate of the parent clock.  This requires that the parent of a
+ * super clock have no other children, otherwise the rate will change
+ * underneath the other children.
+ */
+static int tegra3_super_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       return clk_set_rate(c->parent, rate);
+}
+
+static struct clk_ops tegra_super_ops = {
+       .init                   = tegra3_super_clk_init,
+       .enable                 = tegra3_super_clk_enable,
+       .disable                = tegra3_super_clk_disable,
+       .set_parent             = tegra3_super_clk_set_parent,
+       .set_rate               = tegra3_super_clk_set_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 tegra3_cpu_clk_init(struct clk *c)
+{
+       /* FIXME: max limits for different SKUs */
+}
+
+static int tegra3_cpu_clk_enable(struct clk *c)
+{
+       return 0;
+}
+
+static void tegra3_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 tegra3_cpu_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       int ret;
+       /*
+        * Take an extra reference to the main pll so it doesn't turn
+        * off when we move the cpu off of it
+        */
+       clk_enable(c->u.cpu.main);
+
+       ret = clk_set_parent(c->parent, c->u.cpu.backup);
+       if (ret) {
+               pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.backup->name);
+               goto out;
+       }
+
+       if (rate == clk_get_rate(c->u.cpu.backup))
+               goto out;
+
+       ret = clk_set_rate(c->u.cpu.main, rate);
+       if (ret) {
+               pr_err("Failed to change cpu pll to %lu\n", rate);
+               goto out;
+       }
+
+       ret = clk_set_parent(c->parent, c->u.cpu.main);
+       if (ret) {
+               pr_err("Failed to switch cpu to clock %s\n", c->u.cpu.main->name);
+               goto out;
+       }
+
+out:
+       clk_disable(c->u.cpu.main);
+       return ret;
+}
+
+static unsigned long tegra3_cpu_get_max_rate(struct clk *c)
+{
+       if (IS_LP_CLUSTER)
+               return c->u.cpu.lp_max_rate;
+       else
+               return c->max_rate;
+}
+
+static struct clk_ops tegra_cpu_ops = {
+       .init     = tegra3_cpu_clk_init,
+       .enable   = tegra3_cpu_clk_enable,
+       .disable  = tegra3_cpu_clk_disable,
+       .set_rate = tegra3_cpu_clk_set_rate,
+       .get_max_rate = tegra3_cpu_get_max_rate,
+};
+
+/* virtual cop clock functions. Used to acquire the fake 'cop' clock to
+ * reset the COP block (i.e. AVP) */
+static void tegra3_cop_clk_reset(struct clk *c, bool assert)
+{
+       unsigned long reg = assert ? RST_DEVICES_SET_L : RST_DEVICES_CLR_L;
+
+       pr_debug("%s %s\n", __func__, assert ? "assert" : "deassert");
+       clk_writel(1 << 1, reg);
+}
+
+static struct clk_ops tegra_cop_ops = {
+       .reset    = tegra3_cop_clk_reset,
+};
+
+/* bus clock functions */
+static void tegra3_bus_clk_init(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       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;
+}
+
+static int tegra3_bus_clk_enable(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       val &= ~(BUS_CLK_DISABLE << c->reg_shift);
+       clk_writel(val, c->reg);
+       return 0;
+}
+
+static void tegra3_bus_clk_disable(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       val |= BUS_CLK_DISABLE << c->reg_shift;
+       clk_writel(val, c->reg);
+}
+
+static int tegra3_bus_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       u32 val = clk_readl(c->reg);
+       unsigned long parent_rate = clk_get_rate(c->parent);
+       int i;
+       for (i = 1; i <= 4; i++) {
+               if (rate == parent_rate / i) {
+                       val &= ~(BUS_CLK_DIV_MASK << c->reg_shift);
+                       val |= (i - 1) << c->reg_shift;
+                       clk_writel(val, c->reg);
+                       c->div = i;
+                       c->mul = 1;
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+static struct clk_ops tegra_bus_ops = {
+       .init                   = tegra3_bus_clk_init,
+       .enable                 = tegra3_bus_clk_enable,
+       .disable                = tegra3_bus_clk_disable,
+       .set_rate               = tegra3_bus_clk_set_rate,
+};
+
+/* Blink output functions */
+
+static void tegra3_blink_clk_init(struct clk *c)
+{
+       u32 val;
+
+       val = pmc_readl(PMC_CTRL);
+       c->state = (val & PMC_CTRL_BLINK_ENB) ? ON : OFF;
+       c->mul = 1;
+       val = pmc_readl(c->reg);
+
+       if (val & PMC_BLINK_TIMER_ENB) {
+               unsigned int on_off;
+
+               on_off = (val >> PMC_BLINK_TIMER_DATA_ON_SHIFT) &
+                       PMC_BLINK_TIMER_DATA_ON_MASK;
+               val >>= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+               val &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+               on_off += val;
+               /* each tick in the blink timer is 4 32KHz clocks */
+               c->div = on_off * 4;
+       } else {
+               c->div = 1;
+       }
+}
+
+static int tegra3_blink_clk_enable(struct clk *c)
+{
+       u32 val;
+
+       val = pmc_readl(PMC_DPD_PADS_ORIDE);
+       pmc_writel(val | PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+
+       val = pmc_readl(PMC_CTRL);
+       pmc_writel(val | PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+       return 0;
+}
+
+static void tegra3_blink_clk_disable(struct clk *c)
+{
+       u32 val;
+
+       val = pmc_readl(PMC_CTRL);
+       pmc_writel(val & ~PMC_CTRL_BLINK_ENB, PMC_CTRL);
+
+       val = pmc_readl(PMC_DPD_PADS_ORIDE);
+       pmc_writel(val & ~PMC_DPD_PADS_ORIDE_BLINK_ENB, PMC_DPD_PADS_ORIDE);
+}
+
+static int tegra3_blink_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       unsigned long parent_rate = clk_get_rate(c->parent);
+       if (rate >= parent_rate) {
+               c->div = 1;
+               pmc_writel(0, c->reg);
+       } else {
+               unsigned int on_off;
+               u32 val;
+
+               on_off = DIV_ROUND_UP(parent_rate / 8, rate);
+               c->div = on_off * 8;
+
+               val = (on_off & PMC_BLINK_TIMER_DATA_ON_MASK) <<
+                       PMC_BLINK_TIMER_DATA_ON_SHIFT;
+               on_off &= PMC_BLINK_TIMER_DATA_OFF_MASK;
+               on_off <<= PMC_BLINK_TIMER_DATA_OFF_SHIFT;
+               val |= on_off;
+               val |= PMC_BLINK_TIMER_ENB;
+               pmc_writel(val, c->reg);
+       }
+
+       return 0;
+}
+
+static struct clk_ops tegra_blink_clk_ops = {
+       .init                   = &tegra3_blink_clk_init,
+       .enable                 = &tegra3_blink_clk_enable,
+       .disable                = &tegra3_blink_clk_disable,
+       .set_rate               = &tegra3_blink_clk_set_rate,
+};
+
+/* PLL Functions */
+static int tegra3_pll_clk_wait_for_lock(struct clk *c)
+{
+#if USE_PLL_LOCK_BITS
+       int i;
+       for (i = 0; i < c->u.pll.lock_delay; i++) {
+               if (clk_readl(c->reg + PLL_BASE) & PLL_BASE_LOCK)
+                       return 0;
+               udelay(2);              /* timeout = 2 * lock time */
+       }
+       pr_err("Timed out waiting for lock bit on pll %s", c->name);
+       return -1;
+#endif
+       udelay(c->u.pll.lock_delay);
+
+       return 0;
+}
+
+static void tegra3_pll_clk_init(struct clk *c)
+{
+       u32 val = clk_readl(c->reg + PLL_BASE);
+
+       c->state = (val & PLL_BASE_ENABLE) ? ON : OFF;
+
+       if (c->flags & PLL_FIXED && !(val & PLL_BASE_OVERRIDE)) {
+               const struct clk_pll_freq_table *sel;
+               unsigned long input_rate = clk_get_rate(c->parent);
+               for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+                       if (sel->input_rate == input_rate &&
+                               sel->output_rate == c->u.pll.fixed_rate) {
+                               c->mul = sel->n;
+                               c->div = sel->m * sel->p;
+                               return;
+                       }
+               }
+               pr_warning("Clock %s has unknown fixed frequency\n", c->name);
+               c->mul = 1;
+               c->div = 1;
+       } else if (val & PLL_BASE_BYPASS) {
+               c->mul = 1;
+               c->div = 1;
+       } else {
+               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;
+       }
+}
+
+static int tegra3_pll_clk_enable(struct clk *c)
+{
+       u32 val;
+       pr_debug("%s on clock %s\n", __func__, c->name);
+
+#if USE_PLL_LOCK_BITS
+       val = clk_readl(c->reg + PLL_MISC(c));
+       val |= PLL_MISC_LOCK_ENABLE(c);
+       clk_writel(val, c->reg + PLL_MISC(c));
+#endif
+       val = clk_readl(c->reg + PLL_BASE);
+       val &= ~PLL_BASE_BYPASS;
+       val |= PLL_BASE_ENABLE;
+       clk_writel(val, c->reg + PLL_BASE);
+
+       tegra3_pll_clk_wait_for_lock(c);
+
+       return 0;
+}
+
+static void tegra3_pll_clk_disable(struct clk *c)
+{
+       u32 val;
+       pr_debug("%s on clock %s\n", __func__, c->name);
+
+       val = clk_readl(c->reg);
+       val &= ~(PLL_BASE_BYPASS | PLL_BASE_ENABLE);
+       clk_writel(val, c->reg);
+}
+
+static int tegra3_pll_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       u32 val;
+       unsigned long input_rate;
+       const struct clk_pll_freq_table *sel;
+
+       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+       if (c->flags & PLL_FIXED) {
+               val = clk_readl(c->reg + PLL_BASE);
+               if (!(val & PLL_BASE_OVERRIDE) && (rate == c->u.pll.fixed_rate))
+                       return 0;
+       }
+
+       /* FIXME: out-of-table set rate for PLLs */
+       input_rate = clk_get_rate(c->parent);
+       for (sel = c->u.pll.freq_table; sel->input_rate != 0; sel++) {
+               if (sel->input_rate == input_rate && sel->output_rate == rate) {
+                       c->mul = sel->n;
+                       c->div = sel->m * sel->p;
+
+                       val = clk_readl(c->reg + PLL_BASE);
+                       if (c->flags & PLL_FIXED)
+                       {
+                               BUG();
+                               val |= PLL_BASE_OVERRIDE;
+                       }
+                       val &= ~(PLL_BASE_DIVP_MASK | PLL_BASE_DIVN_MASK |
+                                PLL_BASE_DIVM_MASK);
+                       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 = 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)
+                               tegra3_pll_clk_enable(c);
+
+                       return 0;
+               }
+       }
+       return -EINVAL;
+}
+
+static struct clk_ops tegra_pll_ops = {
+       .init                   = tegra3_pll_clk_init,
+       .enable                 = tegra3_pll_clk_enable,
+       .disable                = tegra3_pll_clk_disable,
+       .set_rate               = tegra3_pll_clk_set_rate,
+};
+
+/* Clock divider ops */
+static void tegra3_pll_div_clk_init(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       u32 divu71;
+       val >>= c->reg_shift;
+       c->state = (val & PLL_OUT_CLKEN) ? ON : OFF;
+       if (!(val & PLL_OUT_RESET_DISABLE))
+               c->state = OFF;
+
+       if (c->flags & DIV_U71) {
+               divu71 = (val & PLL_OUT_RATIO_MASK) >> PLL_OUT_RATIO_SHIFT;
+               c->div = (divu71 + 2);
+               c->mul = 2;
+       } else if (c->flags & DIV_2) {
+               if (c->flags & PLLD) {
+                       c->div = 2;
+                       c->mul = 1;
+               }
+               else if (c->flags & PLLX) {
+                       c->div = (IS_LP_CLUSTER &&
+                                       (!IS_PLLX_DIV2_BYPASS)) ? 2 : 1;
+                       c->mul = 1;
+               }
+               else
+                       BUG();
+       } else {
+               c->div = 1;
+               c->mul = 1;
+       }
+}
+
+static int tegra3_pll_div_clk_enable(struct clk *c)
+{
+       u32 val;
+       u32 new_val;
+
+       pr_debug("%s: %s\n", __func__, c->name);
+       if (c->flags & DIV_U71) {
+               val = clk_readl(c->reg);
+               new_val = val >> c->reg_shift;
+               new_val &= 0xFFFF;
+
+               new_val |= PLL_OUT_CLKEN | PLL_OUT_RESET_DISABLE;
+
+               val &= ~(0xFFFF << c->reg_shift);
+               val |= new_val << c->reg_shift;
+               clk_writel(val, c->reg);
+               return 0;
+       } else if (c->flags & DIV_2) {
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static void tegra3_pll_div_clk_disable(struct clk *c)
+{
+       u32 val;
+       u32 new_val;
+
+       pr_debug("%s: %s\n", __func__, c->name);
+       if (c->flags & DIV_U71) {
+               val = clk_readl(c->reg);
+               new_val = val >> c->reg_shift;
+               new_val &= 0xFFFF;
+
+               new_val &= ~(PLL_OUT_CLKEN | PLL_OUT_RESET_DISABLE);
+
+               val &= ~(0xFFFF << c->reg_shift);
+               val |= new_val << c->reg_shift;
+               clk_writel(val, c->reg);
+       }
+}
+
+static int tegra3_pll_div_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       u32 val;
+       u32 new_val;
+       int divider_u71;
+       unsigned long parent_rate = clk_get_rate(c->parent);
+
+       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+       if (c->flags & DIV_U71) {
+               divider_u71 = clk_div71_get_divider(parent_rate, rate);
+               if (divider_u71 >= 0) {
+                       val = clk_readl(c->reg);
+                       new_val = val >> c->reg_shift;
+                       new_val &= 0xFFFF;
+                       if (c->flags & DIV_U71_FIXED)
+                               new_val |= PLL_OUT_OVERRIDE;
+                       new_val &= ~PLL_OUT_RATIO_MASK;
+                       new_val |= divider_u71 << PLL_OUT_RATIO_SHIFT;
+
+                       val &= ~(0xFFFF << c->reg_shift);
+                       val |= new_val << c->reg_shift;
+                       clk_writel(val, c->reg);
+                       c->div = divider_u71 + 2;
+                       c->mul = 2;
+                       return 0;
+               }
+       } else if (c->flags & DIV_2) {
+               if (c->flags & PLLD) {
+                       return clk_set_rate(c->parent, rate * 2);
+               }
+               else if (c->flags & PLLX) {
+                       if (IS_LP_CLUSTER && (!IS_PLLX_DIV2_BYPASS))
+                               rate *= 2;
+                       return clk_set_rate(c->parent, rate);
+               }
+       }
+       return -EINVAL;
+}
+
+static long tegra3_pll_div_clk_round_rate(struct clk *c, unsigned long rate)
+{
+       int divider;
+       unsigned long parent_rate = clk_get_rate(c->parent);
+       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+       if (c->flags & DIV_U71) {
+               divider = clk_div71_get_divider(parent_rate, rate);
+               if (divider < 0)
+                       return divider;
+               return parent_rate * 2 / (divider + 2);
+       }
+       return -EINVAL;
+}
+
+static void tegra3_pllx_div_clk_recalculate_rate(struct clk *c)
+{
+       c->div = (IS_LP_CLUSTER && (!IS_PLLX_DIV2_BYPASS)) ? 2 : 1;
+}
+
+static struct clk_ops tegra_pll_div_ops = {
+       .init                   = tegra3_pll_div_clk_init,
+       .enable                 = tegra3_pll_div_clk_enable,
+       .disable                = tegra3_pll_div_clk_disable,
+       .set_rate               = tegra3_pll_div_clk_set_rate,
+       .round_rate             = tegra3_pll_div_clk_round_rate,
+};
+
+static struct clk_ops tegra_plld_div_ops = {
+       .init                   = tegra3_pll_div_clk_init,
+       .enable                 = tegra3_pll_div_clk_enable,
+       .disable                = tegra3_pll_div_clk_disable,
+       .set_rate               = tegra3_pll_div_clk_set_rate,
+};
+
+static struct clk_ops tegra_pllx_div_ops = {
+       .init                   = tegra3_pll_div_clk_init,
+       .enable                 = tegra3_pll_div_clk_enable,
+       .disable                = tegra3_pll_div_clk_disable,
+       .set_rate               = tegra3_pll_div_clk_set_rate,
+       .recalculate_rate = tegra3_pllx_div_clk_recalculate_rate,
+};
+
+/* Periph clk ops */
+
+static void tegra3_periph_clk_init(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       const struct clk_mux_sel *mux = 0;
+       const struct clk_mux_sel *sel;
+       if (c->flags & (MUX | MUX_PWM | MUX8)) {
+               for (sel = c->inputs; sel->input != NULL; sel++) {
+                       if (val >> PERIPH_CLK_SOURCE_SHIFT(c) == sel->value)
+                               mux = sel;
+               }
+               BUG_ON(!mux);
+
+               c->parent = mux->input;
+       } else {
+               c->parent = c->inputs[0].input;
+       }
+
+       if (c->flags & DIV_U71) {
+               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;
+       }
+
+       c->state = ON;
+       if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
+               c->state = OFF;
+       if (!(c->flags & PERIPH_NO_RESET))
+               if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) & PERIPH_CLK_TO_BIT(c))
+                       c->state = OFF;
+}
+
+static int tegra3_periph_clk_enable(struct clk *c)
+{
+       u32 val;
+       pr_debug("%s on clock %s\n", __func__, c->name);
+
+       tegra_periph_clk_enable_refcount[c->u.periph.clk_num]++;
+       if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] > 1)
+               return 0;
+
+       clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_SET_REG(c));
+       if (!(c->flags & PERIPH_NO_RESET) && !(c->flags & PERIPH_MANUAL_RESET)) {
+               if (clk_readl(PERIPH_CLK_TO_RST_REG(c)) & PERIPH_CLK_TO_BIT(c)) {
+                       udelay(5);      /* reset propagation delay */
+                       clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_RST_CLR_REG(c));
+               }
+       }
+       if (c->flags & PERIPH_EMC_ENB) {
+               /* The EMC peripheral clock has 2 extra enable bits */
+               /* FIXME: Do they need to be disabled? */
+               val = clk_readl(c->reg);
+               val |= 0x3 << 24;
+               clk_writel(val, c->reg);
+       }
+       return 0;
+}
+
+static void tegra3_periph_clk_disable(struct clk *c)
+{
+       pr_debug("%s on clock %s\n", __func__, c->name);
+
+       if (c->refcnt)
+               tegra_periph_clk_enable_refcount[c->u.periph.clk_num]--;
+
+       if (tegra_periph_clk_enable_refcount[c->u.periph.clk_num] == 0)
+               clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
+}
+
+static void tegra3_periph_clk_reset(struct clk *c, bool assert)
+{
+       pr_debug("%s %s on clock %s\n", __func__,
+                assert ? "assert" : "deassert", c->name);
+
+       if (!(c->flags & PERIPH_NO_RESET)) {
+               if (assert)
+                       clk_writel(PERIPH_CLK_TO_BIT(c),
+                                  PERIPH_CLK_TO_RST_SET_REG(c));
+               else
+                       clk_writel(PERIPH_CLK_TO_BIT(c),
+                                  PERIPH_CLK_TO_RST_CLR_REG(c));
+       }
+}
+
+static int tegra3_periph_clk_set_parent(struct clk *c, struct clk *p)
+{
+       u32 val;
+       const struct clk_mux_sel *sel;
+       pr_debug("%s: %s %s\n", __func__, c->name, p->name);
+       for (sel = c->inputs; sel->input != NULL; sel++) {
+               if (sel->input == p) {
+                       val = clk_readl(c->reg);
+                       val &= ~PERIPH_CLK_SOURCE_MASK(c);
+                       val |= (sel->value) << PERIPH_CLK_SOURCE_SHIFT(c);
+
+                       if (c->refcnt)
+                               clk_enable(p);
+
+                       clk_writel(val, c->reg);
+
+                       if (c->refcnt && c->parent)
+                               clk_disable(c->parent);
+
+                       clk_reparent(c, p);
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int tegra3_periph_clk_set_rate(struct clk *c, unsigned long rate)
+{
+       u32 val;
+       int divider;
+       unsigned long parent_rate = clk_get_rate(c->parent);
+
+       if (c->flags & DIV_U71) {
+               divider = clk_div71_get_divider(parent_rate, rate);
+               if (divider >= 0) {
+                       val = clk_readl(c->reg);
+                       val &= ~PERIPH_CLK_SOURCE_DIVU71_MASK;
+                       val |= divider;
+                       clk_writel(val, c->reg);
+                       c->div = divider + 2;
+                       c->mul = 2;
+                       return 0;
+               }
+       } else if (c->flags & DIV_U16) {
+               divider = clk_div16_get_divider(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 (parent_rate <= rate) {
+               c->div = 1;
+               c->mul = 1;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static long tegra3_periph_clk_round_rate(struct clk *c,
+       unsigned long rate)
+{
+       int divider;
+       unsigned long parent_rate = clk_get_rate(c->parent);
+       pr_debug("%s: %s %lu\n", __func__, c->name, rate);
+
+       if (c->flags & DIV_U71) {
+               divider = clk_div71_get_divider(parent_rate, rate);
+               if (divider < 0)
+                       return divider;
+
+               return parent_rate * 2 / (divider + 2);
+       } else if (c->flags & DIV_U16) {
+               divider = clk_div16_get_divider(parent_rate, rate);
+               if (divider < 0)
+                       return divider;
+               return parent_rate / (divider + 1);
+       }
+       return -EINVAL;
+}
+
+static struct clk_ops tegra_periph_clk_ops = {
+       .init                   = &tegra3_periph_clk_init,
+       .enable                 = &tegra3_periph_clk_enable,
+       .disable                = &tegra3_periph_clk_disable,
+       .set_parent             = &tegra3_periph_clk_set_parent,
+       .set_rate               = &tegra3_periph_clk_set_rate,
+       .round_rate             = &tegra3_periph_clk_round_rate,
+       .reset                  = &tegra3_periph_clk_reset,
+};
+
+/* Clock doubler ops */
+static void tegra3_clk_double_init(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       c->mul = val & (0x1 << c->reg_shift) ? 1 : 2;
+       c->div = 1;
+       c->state = ON;
+       if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
+               c->state = OFF;
+};
+
+static int tegra3_clk_double_set_rate(struct clk *c, unsigned long rate)
+{
+       u32 val;
+       unsigned long parent_rate = clk_get_rate(c->parent);
+       if (rate == parent_rate) {
+               val = clk_readl(c->reg) | (0x1 << c->reg_shift);
+               clk_writel(val, c->reg);
+               c->mul = 1;
+               c->div = 1;
+               return 0;
+       } else if (rate == 2 * parent_rate) {
+               val = clk_readl(c->reg) & (~(0x1 << c->reg_shift));
+               clk_writel(val, c->reg);
+               c->mul = 2;
+               c->div = 1;
+               return 0;
+       }
+       return -EINVAL;
+}
+
+static struct clk_ops tegra_clk_double_ops = {
+       .init                   = &tegra3_clk_double_init,
+       .enable                 = &tegra3_periph_clk_enable,
+       .disable                = &tegra3_periph_clk_disable,
+       .set_rate               = &tegra3_clk_double_set_rate,
+};
+
+/* Audio sync clock ops */
+static void tegra3_audio_sync_clk_init(struct clk *c)
+{
+       int source;
+       const struct clk_mux_sel *sel;
+       u32 val = clk_readl(c->reg);
+       c->state = (val & AUDIO_SYNC_DISABLE_BIT) ? OFF : ON;
+       source = val & AUDIO_SYNC_SOURCE_MASK;
+       for (sel = c->inputs; sel->input != NULL; sel++)
+               if (sel->value == source)
+                       break;
+       BUG_ON(sel->input == NULL);
+       c->parent = sel->input;
+}
+
+static int tegra3_audio_sync_clk_enable(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       clk_writel((val & (~AUDIO_SYNC_DISABLE_BIT)), c->reg);
+       return 0;
+}
+
+static void tegra3_audio_sync_clk_disable(struct clk *c)
+{
+       u32 val = clk_readl(c->reg);
+       clk_writel((val | AUDIO_SYNC_DISABLE_BIT), c->reg);
+}
+
+static int tegra3_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 &= ~AUDIO_SYNC_SOURCE_MASK;
+                       val |= sel->value;
+
+                       if (c->refcnt)
+                               clk_enable(p);
+
+                       clk_writel(val, c->reg);
+
+                       if (c->refcnt && c->parent)
+                               clk_disable(c->parent);
+
+                       clk_reparent(c, p);
+                       return 0;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static struct clk_ops tegra_audio_sync_clk_ops = {
+       .init       = tegra3_audio_sync_clk_init,
+       .enable     = tegra3_audio_sync_clk_enable,
+       .disable    = tegra3_audio_sync_clk_disable,
+       .set_parent = tegra3_audio_sync_clk_set_parent,
+};
+
+/* cdev1 and cdev2 (dap_mclk1 and dap_mclk2) ops */
+
+static void tegra3_cdev_clk_init(struct clk *c)
+{
+       /* We could un-tristate the cdev1 or cdev2 pingroup here; this is
+        * currently done in the pinmux code. */
+       c->state = ON;
+       if (!(clk_readl(PERIPH_CLK_TO_ENB_REG(c)) & PERIPH_CLK_TO_BIT(c)))
+               c->state = OFF;
+}
+
+static int tegra3_cdev_clk_enable(struct clk *c)
+{
+       clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_SET_REG(c));
+       return 0;
+}
+
+static void tegra3_cdev_clk_disable(struct clk *c)
+{
+       clk_writel(PERIPH_CLK_TO_BIT(c), PERIPH_CLK_TO_ENB_CLR_REG(c));
+}
+
+static struct clk_ops tegra_cdev_clk_ops = {
+       .init                   = &tegra3_cdev_clk_init,
+       .enable                 = &tegra3_cdev_clk_enable,
+       .disable                = &tegra3_cdev_clk_disable,
+};
+
+/* shared bus ops */
+/*
+ * Some clocks may have multiple downstream users that need to request a
+ * higher clock rate.  Shared bus clocks provide a unique shared_bus_user
+ * clock to each user.  The frequency of the bus is set to the highest
+ * enabled share