summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mach-tegra/Kconfig27
-rw-r--r--arch/arm/mach-tegra/Makefile10
-rw-r--r--arch/arm/mach-tegra/Makefile.boot4
-rw-r--r--arch/arm/mach-tegra/clock.c42
-rw-r--r--arch/arm/mach-tegra/clock.h26
-rw-r--r--arch/arm/mach-tegra/common.c35
-rw-r--r--arch/arm/mach-tegra/cpu-tegra.c7
-rw-r--r--arch/arm/mach-tegra/cpuidle.c6
-rw-r--r--arch/arm/mach-tegra/devices.c172
-rw-r--r--arch/arm/mach-tegra/devices.h18
-rw-r--r--arch/arm/mach-tegra/dma.c9
-rw-r--r--arch/arm/mach-tegra/dvfs.h2
-rw-r--r--arch/arm/mach-tegra/fuse.c102
-rw-r--r--arch/arm/mach-tegra/gpio-names.h20
-rw-r--r--arch/arm/mach-tegra/headsmp-t3.S97
-rw-r--r--arch/arm/mach-tegra/headsmp.S10
-rw-r--r--arch/arm/mach-tegra/hotplug.c14
-rw-r--r--arch/arm/mach-tegra/include/mach/dma.h11
-rw-r--r--arch/arm/mach-tegra/include/mach/iomap.h163
-rw-r--r--arch/arm/mach-tegra/include/mach/iovmm.h26
-rw-r--r--arch/arm/mach-tegra/include/mach/irqs.h191
-rw-r--r--arch/arm/mach-tegra/include/mach/mc.h7
-rw-r--r--arch/arm/mach-tegra/include/mach/memory.h11
-rw-r--r--arch/arm/mach-tegra/include/mach/pinmux-t3.h320
-rw-r--r--arch/arm/mach-tegra/include/mach/pinmux.h72
-rw-r--r--arch/arm/mach-tegra/include/mach/powergate.h18
-rw-r--r--arch/arm/mach-tegra/include/mach/uncompress.h66
-rw-r--r--arch/arm/mach-tegra/iovmm-gart.c18
-rw-r--r--arch/arm/mach-tegra/iovmm-smmu.c1167
-rw-r--r--arch/arm/mach-tegra/iovmm.c94
-rw-r--r--arch/arm/mach-tegra/irq.c5
-rw-r--r--arch/arm/mach-tegra/mc.c5
-rw-r--r--arch/arm/mach-tegra/pinmux-t2-tables.c1
-rw-r--r--arch/arm/mach-tegra/pinmux-t3-tables.c396
-rw-r--r--arch/arm/mach-tegra/pinmux.c79
-rw-r--r--arch/arm/mach-tegra/platsmp.c166
-rw-r--r--arch/arm/mach-tegra/pm-t3.c353
-rw-r--r--arch/arm/mach-tegra/pm.c69
-rw-r--r--arch/arm/mach-tegra/pm.h29
-rw-r--r--arch/arm/mach-tegra/powergate.c17
-rw-r--r--arch/arm/mach-tegra/sysfs-cluster.c408
-rw-r--r--arch/arm/mach-tegra/sysfs-dcc.c262
-rw-r--r--arch/arm/mach-tegra/tegra2_clocks.c14
-rw-r--r--arch/arm/mach-tegra/tegra2_dvfs.c2
-rw-r--r--arch/arm/mach-tegra/tegra3_clocks.c2396
-rw-r--r--arch/arm/mach-tegra/tegra3_dvfs.c30
-rw-r--r--arch/arm/mach-tegra/tegra3_save.S480
-rw-r--r--arch/arm/mach-tegra/timer.c11
-rw-r--r--arch/arm/mach-tegra/wakeups-t3.c88
-rw-r--r--drivers/gpio/gpio-tegra.c34
50 files changed, 7441 insertions, 169 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig
index d74e2fa53c14..3c295bf0b6ee 100644
--- a/arch/arm/mach-tegra/Kconfig
+++ b/arch/arm/mach-tegra/Kconfig
@@ -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
diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile
index 7756063ced38..d94bccb6796e 100644
--- a/arch/arm/mach-tegra/Makefile
+++ b/arch/arm/mach-tegra/Makefile
@@ -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
diff --git a/arch/arm/mach-tegra/Makefile.boot b/arch/arm/mach-tegra/Makefile.boot
index 428ad122be03..d8cb9173cdf7 100644
--- a/arch/arm/mach-tegra/Makefile.boot
+++ b/arch/arm/mach-tegra/Makefile.boot
@@ -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
diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c
index 3c09fbec040e..0096f6f102d3 100644
--- a/arch/arm/mach-tegra/clock.c
+++ b/arch/arm/mach-tegra/clock.c
@@ -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);
diff --git a/arch/arm/mach-tegra/clock.h b/arch/arm/mach-tegra/clock.h
index fed02b294ae0..4f32594286ef 100644
--- a/arch/arm/mach-tegra/clock.h
+++ b/arch/arm/mach-tegra/clock.h
@@ -20,10 +20,12 @@
#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)
@@ -40,8 +42,18 @@
#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
diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c
index ee23ecdd2a9b..7a2456568e64 100644
--- a/arch/arm/mach-tegra/common.c
+++ b/arch/arm/mach-tegra/common.c
@@ -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
diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c
index 5ffb8549af51..3da60ac97072 100644
--- a/arch/arm/mach-tegra/cpu-tegra.c
+++ b/arch/arm/mach-tegra/cpu-tegra.c
@@ -39,12 +39,11 @@
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");
diff --git a/arch/arm/mach-tegra/cpuidle.c b/arch/arm/mach-tegra/cpuidle.c
index 18861b367c11..7eed5c11127f 100644
--- a/arch/arm/mach-tegra/cpuidle.c
+++ b/arch/arm/mach-tegra/cpuidle.c
@@ -43,8 +43,14 @@
#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);
diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c
index 6597465a1fee..a89d21034f84 100644
--- a/arch/arm/mach-tegra/devices.c
+++ b/arch/arm/mach-tegra/devices.c
@@ -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] = {
diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h
index be76fe151286..7118f99965f7 100644
--- a/arch/arm/mach-tegra/devices.h
+++ b/arch/arm/mach-tegra/devices.h
@@ -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;
diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c
index eb1c7cab01db..e9c28465b114 100644
--- a/arch/arm/mach-tegra/dma.c
+++ b/arch/arm/mach-tegra/dma.c
@@ -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",
diff --git a/arch/arm/mach-tegra/dvfs.h b/arch/arm/mach-tegra/dvfs.h
index a785a2edc530..6fbcad64fb5c 100644
--- a/arch/arm/mach-tegra/dvfs.h
+++ b/arch/arm/mach-tegra/dvfs.h
@@ -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);
diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c
index 1aa393c18323..dad92e4b365f 100644
--- a/arch/arm/mach-tegra/fuse.c
+++ b/arch/arm/mach-tegra/fuse.c
@@ -25,10 +25,26 @@
#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)
diff --git a/arch/arm/mach-tegra/gpio-names.h b/arch/arm/mach-tegra/gpio-names.h
index f28220a641b2..8441b21f5726 100644
--- a/arch/arm/mach-tegra/gpio-names.h
+++ b/arch/arm/mach-tegra/gpio-names.h
@@ -243,5 +243,23 @@
#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
index 000000000000..5ee115f5965f
--- /dev/null
+++ b/arch/arm/mach-tegra/headsmp-t3.S
@@ -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
diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S
index 1ddfb28f70d9..34e521b412df 100644
--- a/arch/arm/mach-tegra/headsmp.S
+++ b/arch/arm/mach-tegra/headsmp.S
@@ -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]
diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c
index 97d9746cee25..d54b8f93dace 100644
--- a/arch/arm/mach-tegra/hotplug.c
+++ b/arch/arm/mach-tegra/hotplug.c
@@ -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 \
@@ -24,17 +26,25 @@
#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;
}
diff --git a/arch/arm/mach-tegra/include/mach/dma.h b/arch/arm/mach-tegra/include/mach/dma.h
index cb0248cca615..a1a73d96f1e4 100644
--- a/arch/arm/mach-tegra/include/mach/dma.h
+++ b/arch/arm/mach-tegra/include/mach/dma.h
@@ -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
diff --git a/arch/arm/mach-tegra/include/mach/iomap.h b/arch/arm/mach-tegra/include/mach/iomap.h
index d10a0337ce46..5680dabda1b1 100644
--- a/arch/arm/mach-tegra/include/mach/iomap.h
+++ b/arch/arm/mach-tegra/include/mach/iomap.h
@@ -59,11 +59,22 @@
#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
@@ -83,6 +94,13 @@
#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
@@ -98,6 +116,28 @@
#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
@@ -140,6 +180,8 @@
#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
@@ -152,6 +194,58 @@
#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
@@ -200,9 +294,18 @@
#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
@@ -212,9 +315,21 @@
#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
@@ -227,6 +342,16 @@
#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
@@ -251,6 +376,8 @@
#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
@@ -272,6 +399,40 @@
#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)
diff --git a/arch/arm/mach-tegra/include/mach/iovmm.h b/arch/arm/mach-tegra/include/mach/iovmm.h
index 98c9888163fd..77ec3875f9b5 100644
--- a/arch/arm/mach-tegra/include/mach/iovmm.h
+++ b/arch/arm/mach-tegra/include/mach/iovmm.h
@@ -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;
}
diff --git a/arch/arm/mach-tegra/include/mach/irqs.h b/arch/arm/mach-tegra/include/mach/irqs.h
index 2352c2c77b61..58e07d7cc528 100644
--- a/arch/arm/mach-tegra/include/mach/irqs.h
+++ b/arch/arm/mach-tegra/include/mach/irqs.h
@@ -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)
@@ -175,14 +176,202 @@
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
diff --git a/arch/arm/mach-tegra/include/mach/mc.h b/arch/arm/mach-tegra/include/mach/mc.h
index 167081067f9e..5b909a506f2f 100644
--- a/arch/arm/mach-tegra/include/mach/mc.h
+++ b/arch/arm/mach-tegra/include/mach/mc.h
@@ -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
@@ -97,4 +98,10 @@
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
diff --git a/arch/arm/mach-tegra/include/mach/memory.h b/arch/arm/mach-tegra/include/mach/memory.h
index 1cdcb280e8b1..4f3a24d5de5e 100644
--- a/arch/arm/mach-tegra/include/mach/memory.h
+++ b/arch/arm/mach-tegra/include/mach/memory.h
@@ -22,8 +22,19 @@
#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
index 000000000000..921e3e8b500d
--- /dev/null
+++ b/arch/arm/mach-tegra/include/mach/pinmux-t3.h
@@ -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,
+};
+
diff --git a/arch/arm/mach-tegra/include/mach/pinmux.h b/arch/arm/mach-tegra/include/mach/pinmux.h
index 2207d0e15925..47056ace13ca 100644
--- a/arch/arm/mach-tegra/include/mach/pinmux.h
+++ b/arch/arm/mach-tegra/include/mach/pinmux.h
@@ -19,17 +19,20 @@
#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[];
diff --git a/arch/arm/mach-tegra/include/mach/powergate.h b/arch/arm/mach-tegra/include/mach/powergate.h
index 401d1b725291..dd636a73de77 100644
--- a/arch/arm/mach-tegra/include/mach/powergate.h
+++ b/arch/arm/mach-tegra/include/mach/powergate.h
@@ -21,13 +21,31 @@
#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);
diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h
index 4e8323770c79..b400f9246e48 100644
--- a/arch/arm/mach-tegra/include/mach/uncompress.h
+++ b/arch/arm/mach-tegra/include/mach/uncompress.h
@@ -26,6 +26,44 @@
#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;
diff --git a/arch/arm/mach-tegra/iovmm-gart.c b/arch/arm/mach-tegra/iovmm-gart.c
index fbab0362c081..adf46991e3dc 100644
--- a/arch/arm/mach-tegra/iovmm-gart.c
+++ b/arch/arm/mach-tegra/iovmm-gart.c
@@ -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
index 000000000000..f899d7204def
--- /dev/null
+++ b/arch/arm/mach-tegra/iovmm-smmu.c
@@ -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
+}
diff --git a/arch/arm/mach-tegra/iovmm.c b/arch/arm/mach-tegra/iovmm.c
index 7a634dd42afc..d492811e26b8 100644
--- a/arch/arm/mach-tegra/iovmm.c
+++ b/arch/arm/mach-tegra/iovmm.c
@@ -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);
diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c
index 56c5c0771b5c..957338aef765 100644
--- a/arch/arm/mach-tegra/irq.c
+++ b/arch/arm/mach-tegra/irq.c
@@ -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
diff --git a/arch/arm/mach-tegra/mc.c b/arch/arm/mach-tegra/mc.c
index 513ac3f5cf4d..53074ce3dc06 100644
--- a/arch/arm/mach-tegra/mc.c
+++ b/arch/arm/mach-tegra/mc.c
@@ -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
diff --git a/arch/arm/mach-tegra/pinmux-t2-tables.c b/arch/arm/mach-tegra/pinmux-t2-tables.c
index fd5e34d8dcb4..a6fcd6c62e98 100644
--- a/arch/arm/mach-tegra/pinmux-t2-tables.c
+++ b/arch/arm/mach-tegra/pinmux-t2-tables.c
@@ -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
index 000000000000..798986ba1837
--- /dev/null
+++ b/arch/arm/mach-tegra/pinmux-t3-tables.c
@@ -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
diff --git a/arch/arm/mach-tegra/pinmux.c b/arch/arm/mach-tegra/pinmux.c
index a49c20d6cd9d..2b6b69428588 100644
--- a/arch/arm/mach-tegra/pinmux.c
+++ b/arch/arm/mach-tegra/pinmux.c
@@ -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");
diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c
index ae6067e7f039..9f8c3c882f51 100644
--- a/arch/arm/mach-tegra/platsmp.c
+++ b/arch/arm/mach-tegra/platsmp.c
@@ -18,11 +18,15 @@
#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)
@@ -32,6 +36,40 @@
(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
index 000000000000..8efae6928170
--- /dev/null
+++ b/arch/arm/mach-tegra/pm-t3.c
@@ -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;
+}
diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c
index 3c8eca7f7cae..c70f1ad7badb 100644
--- a/arch/arm/mach-tegra/pm.c
+++ b/arch/arm/mach-tegra/pm.c
@@ -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);
diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h
index 76113b62c945..e892d9f3bb0f 100644
--- a/arch/arm/mach-tegra/pm.h
+++ b/arch/arm/mach-tegra/pm.h
@@ -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_ */
diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c
index 8f6f96822e37..847e8b9cdaf8 100644
--- a/arch/arm/mach-tegra/powergate.c
+++ b/arch/arm/mach-tegra/powergate.c
@@ -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
index 000000000000..50f9535cb4d9
--- /dev/null
+++ b/arch/arm/mach-tegra/sysfs-cluster.c
@@ -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
index 000000000000..fb1cf92b1641
--- /dev/null
+++ b/arch/arm/mach-tegra/sysfs-dcc.c
@@ -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");
diff --git a/arch/arm/mach-tegra/tegra2_clocks.c b/arch/arm/mach-tegra/tegra2_clocks.c
index f0222bd4d59e..0ae724cae9ae 100644
--- a/arch/arm/mach-tegra/tegra2_clocks.c
+++ b/arch/arm/mach-tegra/tegra2_clocks.c
@@ -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;
diff --git a/arch/arm/mach-tegra/tegra2_dvfs.c b/arch/arm/mach-tegra/tegra2_dvfs.c
index 34a62192346a..28a450ee48f4 100644
--- a/arch/arm/mach-tegra/tegra2_dvfs.c
+++ b/arch/arm/mach-tegra/tegra2_dvfs.c
@@ -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
index 000000000000..161df8720c9d
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_clocks.c
@@ -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 shared_bus_user clock, with a minimum value set by the
+ * shared bus.
+ */
+static void tegra_clk_shared_bus_update(struct clk *bus)
+{
+ struct clk *c;
+ unsigned long rate = bus->u.shared_bus.min_rate;
+
+ list_for_each_entry(c, &bus->u.shared_bus.list,
+ u.shared_bus_user.node) {
+ if (c->u.shared_bus_user.enabled)
+ rate = max(c->u.shared_bus_user.rate, rate);
+ }
+
+ clk_set_rate(bus, rate);
+};
+
+static void tegra_clk_shared_bus_init(struct clk *c)
+{
+ c->max_rate = c->parent->max_rate;
+ c->u.shared_bus_user.rate = c->parent->max_rate;
+ c->state = OFF;
+ c->set = true;
+
+ list_add_tail(&c->u.shared_bus_user.node,
+ &c->parent->u.shared_bus.list);
+}
+
+static int tegra_clk_shared_bus_set_rate(struct clk *c, unsigned long rate)
+{
+ c->u.shared_bus_user.rate = rate;
+ tegra_clk_shared_bus_update(c->parent);
+ return 0;
+}
+
+static int tegra_clk_shared_bus_enable(struct clk *c)
+{
+ c->u.shared_bus_user.enabled = true;
+ tegra_clk_shared_bus_update(c->parent);
+ return 0;
+}
+
+static void tegra_clk_shared_bus_disable(struct clk *c)
+{
+ c->u.shared_bus_user.enabled = false;
+ tegra_clk_shared_bus_update(c->parent);
+}
+
+static struct clk_ops tegra_clk_shared_bus_ops = {
+ .init = tegra_clk_shared_bus_init,
+ .enable = tegra_clk_shared_bus_enable,
+ .disable = tegra_clk_shared_bus_disable,
+ .set_rate = tegra_clk_shared_bus_set_rate,
+};
+
+
+/* Clock definitions */
+static struct clk tegra_clk_32k = {
+ .name = "clk_32k",
+ .rate = 32768,
+ .ops = NULL,
+ .max_rate = 32768,
+};
+
+static struct clk tegra_clk_m = {
+ .name = "clk_m",
+ .flags = ENABLE_ON_INIT,
+ .ops = &tegra_clk_m_ops,
+ .reg = 0x1fc,
+ .reg_shift = 28,
+ .max_rate = 48000000,
+};
+
+static struct clk tegra_pll_ref = {
+ .name = "pll_ref",
+ .flags = ENABLE_ON_INIT,
+ .ops = &tegra_pll_ref_ops,
+ .parent = &tegra_clk_m,
+ .max_rate = 26000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_c_freq_table[] = {
+ { 12000000, 600000000, 600, 12, 1, 8},
+ { 13000000, 600000000, 600, 13, 1, 8},
+ { 16800000, 600000000, 500, 14, 1, 8},
+ { 19200000, 600000000, 375, 12, 1, 6},
+ { 26000000, 600000000, 600, 26, 1, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_c = {
+ .name = "pll_c",
+ .flags = PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0x80,
+ .parent = &tegra_pll_ref,
+ .max_rate = 600000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_c_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_c_out1 = {
+ .name = "pll_c_out1",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_U71,
+ .parent = &tegra_pll_c,
+ .reg = 0x84,
+ .reg_shift = 0,
+ .max_rate = 600000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_m_freq_table[] = {
+ { 12000000, 666000000, 666, 12, 1, 8},
+ { 13000000, 666000000, 666, 13, 1, 8},
+ { 16800000, 666000000, 555, 14, 1, 8},
+ { 19200000, 666000000, 555, 16, 1, 8},
+ { 26000000, 666000000, 666, 26, 1, 8},
+ { 12000000, 600000000, 600, 12, 1, 8},
+ { 13000000, 600000000, 600, 13, 1, 8},
+ { 16800000, 600000000, 500, 14, 1, 8},
+ { 19200000, 600000000, 375, 12, 1, 6},
+ { 26000000, 600000000, 600, 26, 1, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_m = {
+ .name = "pll_m",
+ .flags = PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0x90,
+ .parent = &tegra_pll_ref,
+ .max_rate = 800000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .freq_table = tegra_pll_m_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_m_out1 = {
+ .name = "pll_m_out1",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_U71,
+ .parent = &tegra_pll_m,
+ .reg = 0x94,
+ .reg_shift = 0,
+ .max_rate = 600000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_p_freq_table[] = {
+ { 12000000, 216000000, 432, 12, 2, 8},
+ { 13000000, 216000000, 432, 13, 2, 8},
+ { 16800000, 216000000, 360, 14, 2, 8},
+ { 19200000, 216000000, 360, 16, 2, 8},
+ { 26000000, 216000000, 432, 26, 2, 8},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_p = {
+ .name = "pll_p",
+ .flags = ENABLE_ON_INIT | PLL_FIXED | PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0xa0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 432000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_p_freq_table,
+ .lock_delay = 300,
+ .fixed_rate = 216000000,
+ },
+};
+
+static struct clk tegra_pll_p_out1 = {
+ .name = "pll_p_out1",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa4,
+ .reg_shift = 0,
+ .max_rate = 432000000,
+};
+
+static struct clk tegra_pll_p_out2 = {
+ .name = "pll_p_out2",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa4,
+ .reg_shift = 16,
+ .max_rate = 432000000,
+};
+
+static struct clk tegra_pll_p_out3 = {
+ .name = "pll_p_out3",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa8,
+ .reg_shift = 0,
+ .max_rate = 432000000,
+};
+
+static struct clk tegra_pll_p_out4 = {
+ .name = "pll_p_out4",
+ .ops = &tegra_pll_div_ops,
+ .flags = ENABLE_ON_INIT | DIV_U71 | DIV_U71_FIXED,
+ .parent = &tegra_pll_p,
+ .reg = 0xa8,
+ .reg_shift = 16,
+ .max_rate = 432000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_a_freq_table[] = {
+ { 28800000, 56448000, 49, 25, 1, 1},
+ { 28800000, 73728000, 64, 25, 1, 1},
+ { 28800000, 11289600, 49, 25, 1, 1}, /* actual: 56.448MHz */
+ { 28800000, 12288000, 64, 25, 1, 1}, /* actual: 73.728MHz */
+ { 28800000, 24000000, 5, 6, 1, 1},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_a = {
+ .name = "pll_a",
+ .flags = PLL_HAS_CPCON,
+ .ops = &tegra_pll_ops,
+ .reg = 0xb0,
+ .parent = &tegra_pll_p_out1,
+ .max_rate = 56448000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1400000000,
+ .freq_table = tegra_pll_a_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_a_out0 = {
+ .name = "pll_a_out0",
+ .ops = &tegra_pll_div_ops,
+ .flags = DIV_U71,
+ .parent = &tegra_pll_a,
+ .reg = 0xb4,
+ .reg_shift = 0,
+ .max_rate = 56448000,
+};
+
+static struct clk_pll_freq_table tegra_pll_d_freq_table[] = {
+ { 12000000, 216000000, 216, 12, 1, 4},
+ { 13000000, 216000000, 216, 13, 1, 4},
+ { 16800000, 216000000, 180, 14, 1, 4},
+ { 19200000, 216000000, 180, 16, 1, 4},
+ { 26000000, 216000000, 216, 26, 1, 4},
+
+ { 12000000, 594000000, 594, 12, 1, 8},
+ { 13000000, 594000000, 594, 13, 1, 8},
+ { 16800000, 594000000, 495, 14, 1, 8},
+ { 19200000, 594000000, 495, 16, 1, 8},
+ { 26000000, 594000000, 594, 26, 1, 8},
+
+ { 12000000, 1000000000, 1000, 12, 1, 12},
+ { 13000000, 1000000000, 1000, 13, 1, 12},
+ { 19200000, 1000000000, 625, 12, 1, 8},
+ { 26000000, 1000000000, 1000, 26, 1, 12},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_d = {
+ .name = "pll_d",
+ .flags = PLL_HAS_CPCON | PLLD,
+ .ops = &tegra_pll_ops,
+ .reg = 0xd0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 1000000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 40000000,
+ .vco_max = 1000000000,
+ .freq_table = tegra_pll_d_freq_table,
+ .lock_delay = 1000,
+ },
+};
+
+static struct clk tegra_pll_d_out0 = {
+ .name = "pll_d_out0",
+ .ops = &tegra_plld_div_ops,
+ .flags = DIV_2 | PLLD,
+ .parent = &tegra_pll_d,
+ .max_rate = 500000000,
+};
+
+static struct clk_pll_freq_table tegra_pll_u_freq_table[] = {
+ { 12000000, 480000000, 960, 12, 2, 12},
+ { 13000000, 480000000, 960, 13, 2, 12},
+ { 16800000, 480000000, 400, 7, 2, 5},
+ { 19200000, 480000000, 200, 4, 2, 3},
+ { 26000000, 480000000, 960, 26, 2, 12},
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_u = {
+ .name = "pll_u",
+ .flags = PLL_HAS_CPCON | PLLU,
+ .ops = &tegra_pll_ops,
+ .reg = 0xc0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 480000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 40000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 480000000,
+ .vco_max = 960000000,
+ .freq_table = tegra_pll_u_freq_table,
+ .lock_delay = 1000,
+ },
+};
+
+/* FIXME: cpcon values */
+static struct clk_pll_freq_table tegra_pll_x_freq_table[] = {
+ /* 1 GHz */
+ { 12000000, 1000000000, 1000, 12, 1, 12},
+ { 13000000, 1000000000, 1000, 13, 1, 12},
+ { 16800000, 1000000000, 952, 16, 1, 12}, /* actual: 999.6 MHz */
+ { 19200000, 1000000000, 625, 12, 1, 8},
+ { 26000000, 1000000000, 1000, 26, 1, 12},
+
+ /* 912 MHz */
+ { 12000000, 912000000, 912, 12, 1, 12},
+ { 13000000, 912000000, 912, 13, 1, 12},
+ { 16800000, 912000000, 760, 14, 1, 8},
+ { 19200000, 912000000, 760, 16, 1, 8},
+ { 26000000, 912000000, 912, 26, 1, 12},
+
+ /* 816 MHz */
+ { 12000000, 816000000, 816, 12, 1, 12},
+ { 13000000, 816000000, 816, 13, 1, 12},
+ { 16800000, 816000000, 680, 14, 1, 8},
+ { 19200000, 816000000, 680, 16, 1, 8},
+ { 26000000, 816000000, 816, 26, 1, 12},
+
+ /* 760 MHz */
+ { 12000000, 760000000, 760, 12, 1, 12},
+ { 13000000, 760000000, 760, 13, 1, 12},
+ { 16800000, 760000000, 723, 16, 1, 8}, /* actual: 759.15 MHz */
+ { 19200000, 760000000, 475, 12, 1, 8},
+ { 26000000, 760000000, 760, 26, 1, 12},
+
+ /* 624 MHz */
+ { 12000000, 624000000, 624, 12, 1, 12},
+ { 13000000, 624000000, 624, 13, 1, 12},
+ { 16800000, 624000000, 520, 14, 1, 8},
+ { 19200000, 624000000, 520, 16, 1, 8},
+ { 26000000, 624000000, 624, 26, 1, 12},
+
+ /* 608 MHz */
+ { 12000000, 608000000, 608, 12, 1, 12},
+ { 13000000, 608000000, 608, 13, 1, 12},
+ { 16800000, 608000000, 579, 16, 1, 8}, /* actual: 607.95 MHz */
+ { 19200000, 608000000, 380, 12, 1, 8},
+ { 26000000, 608000000, 608, 26, 1, 12},
+
+ /* 456 MHz */
+ { 12000000, 456000000, 456, 12, 1, 12},
+ { 13000000, 456000000, 456, 13, 1, 12},
+ { 16800000, 456000000, 380, 14, 1, 8},
+ { 19200000, 456000000, 380, 16, 1, 8},
+ { 26000000, 456000000, 456, 26, 1, 12},
+
+ /* 312 MHz */
+ { 12000000, 312000000, 312, 12, 1, 12},
+ { 13000000, 312000000, 312, 13, 1, 12},
+ { 16800000, 312000000, 260, 14, 1, 8},
+ { 19200000, 312000000, 260, 16, 1, 8},
+ { 26000000, 312000000, 312, 26, 1, 12},
+
+ { 0, 0, 0, 0, 0, 0 },
+};
+
+static struct clk tegra_pll_x = {
+ .name = "pll_x",
+ .flags = PLL_HAS_CPCON | PLL_ALT_MISC_REG | PLLX,
+ .ops = &tegra_pll_ops,
+ .reg = 0xe0,
+ .parent = &tegra_pll_ref,
+ .max_rate = 1000000000,
+ .u.pll = {
+ .input_min = 2000000,
+ .input_max = 31000000,
+ .cf_min = 1000000,
+ .cf_max = 6000000,
+ .vco_min = 20000000,
+ .vco_max = 1200000000,
+ .freq_table = tegra_pll_x_freq_table,
+ .lock_delay = 300,
+ },
+};
+
+static struct clk tegra_pll_x_out0 = {
+ .name = "pll_x_out0",
+ .ops = &tegra_pllx_div_ops,
+ .flags = DIV_2 | PLLX,
+ .parent = &tegra_pll_x,
+ .max_rate = 1000000000,
+};
+
+/* dap_mclk1, belongs to the cdev1 pingroup. */
+static struct clk tegra_dev1_clk = {
+ .name = "clk_dev1",
+ .ops = &tegra_cdev_clk_ops,
+ .rate = 26000000,
+ .max_rate = 26000000,
+ .u.periph = {
+ .clk_num = 94,
+ },
+};
+
+/* dap_mclk2, belongs to the cdev2 pingroup. */
+static struct clk tegra_dev2_clk = {
+ .name = "clk_dev2",
+ .ops = &tegra_cdev_clk_ops,
+ .rate = 26000000,
+ .max_rate = 26000000,
+ .u.periph = {
+ .clk_num = 93,
+ },
+};
+
+/* initialized before peripheral clocks */
+static struct clk_mux_sel mux_audio_sync_clk[AUDIO_SYNC_SOURCE_MASK+1+1];
+static const struct audio_sources {
+ const char *name;
+ int value;
+} mux_audio_sync_clk_sources[] = {
+ { .name = "spdif_in", .value = 0 },
+ { .name = "i2s0", .value = 1 },
+ { .name = "i2s1", .value = 2 },
+ { .name = "i2s2", .value = 3 },
+ { .name = "i2s3", .value = 4 },
+ { .name = "i2s4", .value = 5 },
+ { .name = "pll_a_out0", .value = 6 },
+#if 0 /* FIXME: not implemented */
+ { .name = "ext_vimclk", .value = 7 },
+#endif
+#if 1 /* FIXME: for debugging on tegra2 - to be removed */
+ { .name = "pll_a_out0", .value = 7 },
+ { .name = "pll_a_out0", .value = 8 },
+ { .name = "pll_a_out0", .value = 9 },
+ { .name = "pll_a_out0", .value =10 },
+ { .name = "pll_a_out0", .value =11 },
+ { .name = "pll_a_out0", .value =12 },
+ { .name = "pll_a_out0", .value =13 },
+ { .name = "pll_a_out0", .value =14 },
+ { .name = "pll_a_out0", .value =15 },
+#endif
+ { 0, 0 }
+};
+
+#define AUDIO_SYNC_CLK(_id, _index) \
+ { \
+ .name = #_id, \
+ .inputs = mux_audio_sync_clk, \
+ .reg = 0x4A0 + (_index) * 4, \
+ .max_rate = 24000000, \
+ .ops = &tegra_audio_sync_clk_ops \
+ }
+static struct clk tegra_clk_audio_list[] = {
+ AUDIO_SYNC_CLK(audio0, 0),
+ AUDIO_SYNC_CLK(audio1, 1),
+ AUDIO_SYNC_CLK(audio2, 2),
+ AUDIO_SYNC_CLK(audio3, 3),
+ AUDIO_SYNC_CLK(audio4, 4),
+ AUDIO_SYNC_CLK(audio, 5), /* SPDIF */
+};
+
+#define AUDIO_SYNC_2X_CLK(_id, _index) \
+ { \
+ .name = #_id "_2x", \
+ .flags = PERIPH_NO_RESET, \
+ .max_rate = 48000000, \
+ .ops = &tegra_clk_double_ops, \
+ .reg = 0x49C, \
+ .reg_shift = 24 + (_index), \
+ .parent = &tegra_clk_audio_list[(_index)], \
+ .u.periph = { \
+ .clk_num = 113 + (_index), \
+ }, \
+ }
+static struct clk tegra_clk_audio_2x_list[] = {
+ AUDIO_SYNC_2X_CLK(audio0, 0),
+ AUDIO_SYNC_2X_CLK(audio1, 1),
+ AUDIO_SYNC_2X_CLK(audio2, 2),
+ AUDIO_SYNC_2X_CLK(audio3, 3),
+ AUDIO_SYNC_2X_CLK(audio4, 4),
+ AUDIO_SYNC_2X_CLK(audio, 5), /* SPDIF */
+};
+
+#define MUX_I2S_SPDIF(_id, _index) \
+static struct clk_mux_sel mux_pllaout0_##_id##_2x_pllp_clkm[] = { \
+ {.input = &tegra_pll_a_out0, .value = 0}, \
+ {.input = &tegra_clk_audio_2x_list[(_index)], .value = 1}, \
+ {.input = &tegra_pll_p, .value = 2}, \
+ {.input = &tegra_clk_m, .value = 3}, \
+ { 0, 0}, \
+}
+MUX_I2S_SPDIF(audio0, 0);
+MUX_I2S_SPDIF(audio1, 1);
+MUX_I2S_SPDIF(audio2, 2);
+MUX_I2S_SPDIF(audio3, 3);
+MUX_I2S_SPDIF(audio4, 4);
+MUX_I2S_SPDIF(audio, 5); /* SPDIF */
+
+/* This is called after peripheral clocks are initialized, as the
+ * audio_sync clock depends on some of the peripheral clocks.
+ */
+
+static void init_audio_sync_clock_mux(void)
+{
+ int i;
+ struct clk_mux_sel *sel = mux_audio_sync_clk;
+ const struct audio_sources *src = mux_audio_sync_clk_sources;
+
+ for (i = 0; src->name; i++, sel++, src++) {
+ sel->input = tegra_get_clock_by_name(src->name);
+ if (!sel->input)
+ pr_err("%s: could not find clk %s\n", __func__,
+ src->name);
+ sel->value = src->value;
+ }
+}
+
+static struct clk_mux_sel mux_cclk[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_clk_32k, .value = 2},
+ { .input = &tegra_pll_m, .value = 3},
+ { .input = &tegra_pll_p, .value = 4},
+ { .input = &tegra_pll_p_out4, .value = 5},
+ { .input = &tegra_pll_p_out3, .value = 6},
+ /* { .input = &tegra_clk_d, .value = 7}, - no use on tegra3 */
+ { .input = &tegra_pll_x_out0, .value = 8},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_sclk[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { .input = &tegra_pll_c_out1, .value = 1},
+ { .input = &tegra_pll_p_out4, .value = 2},
+ { .input = &tegra_pll_p_out3, .value = 3},
+ { .input = &tegra_pll_p_out2, .value = 4},
+ /* { .input = &tegra_clk_d, .value = 5}, - no use on tegra3 */
+ { .input = &tegra_clk_32k, .value = 6},
+ { .input = &tegra_pll_m_out1, .value = 7},
+ { 0, 0},
+};
+
+static struct clk tegra_clk_cclk = {
+ .name = "cclk",
+ .inputs = mux_cclk,
+ .reg = 0x20,
+ .ops = &tegra_super_ops,
+ .max_rate = 1000000000,
+};
+
+static struct clk tegra_clk_sclk = {
+ .name = "sclk",
+ .inputs = mux_sclk,
+ .reg = 0x28,
+ .ops = &tegra_super_ops,
+ .max_rate = 240000000,
+ .u.shared_bus = {
+ .min_rate = 120000000,
+ },
+};
+
+static struct clk tegra_clk_virtual_cpu = {
+ .name = "cpu",
+ .parent = &tegra_clk_cclk,
+ .ops = &tegra_cpu_ops,
+ .max_rate = 1000000000,
+ .u.cpu = {
+ .main = &tegra_pll_x_out0,
+ .backup = &tegra_pll_p,
+ .lp_max_rate = 456000000,
+ },
+};
+
+static struct clk tegra_clk_cop = {
+ .name = "cop",
+ .parent = &tegra_clk_sclk,
+ .ops = &tegra_cop_ops,
+ .max_rate = 240000000,
+};
+
+static struct clk tegra_clk_hclk = {
+ .name = "hclk",
+ .flags = DIV_BUS,
+ .parent = &tegra_clk_sclk,
+ .reg = 0x30,
+ .reg_shift = 4,
+ .ops = &tegra_bus_ops,
+ .max_rate = 240000000,
+};
+
+static struct clk tegra_clk_pclk = {
+ .name = "pclk",
+ .flags = DIV_BUS,
+ .parent = &tegra_clk_hclk,
+ .reg = 0x30,
+ .reg_shift = 0,
+ .ops = &tegra_bus_ops,
+ .max_rate = 120000000,
+};
+
+static struct clk tegra_clk_blink = {
+ .name = "blink",
+ .parent = &tegra_clk_32k,
+ .reg = 0x40,
+ .ops = &tegra_blink_clk_ops,
+ .max_rate = 32768,
+};
+
+static struct clk_mux_sel mux_pllm_pllc_pllp_plla[] = {
+ { .input = &tegra_pll_m, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_pll_a_out0, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllm_pllc_pllp_clkm[] = {
+ { .input = &tegra_pll_m, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_pllm_clkm[] = {
+ { .input = &tegra_pll_p, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_pll_m, .value = 2},
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_plld_pllc_clkm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_d_out0, .value = 1},
+ {.input = &tegra_pll_c, .value = 2},
+ {.input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+/* FIXME: add plld2 */
+static struct clk_mux_sel mux_pllp_pllm_plld_plla_pllc_clkm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_m, .value = 1},
+ {.input = &tegra_pll_d_out0, .value = 2},
+ {.input = &tegra_pll_a_out0, .value = 3},
+ {.input = &tegra_pll_c, .value = 4},
+ {.input = &tegra_clk_m, .value = 6},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_plla_pllc_pllp_clkm[] = {
+ { .input = &tegra_pll_a_out0, .value = 0},
+ { .input = &tegra_pll_c, .value = 1},
+ { .input = &tegra_pll_p, .value = 2},
+ { .input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_clk32_clkm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_c, .value = 1},
+ {.input = &tegra_clk_32k, .value = 2},
+ {.input = &tegra_clk_m, .value = 3},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_pllc_pllm[] = {
+ {.input = &tegra_pll_p, .value = 0},
+ {.input = &tegra_pll_c, .value = 1},
+ {.input = &tegra_pll_m, .value = 2},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_clk_m[] = {
+ { .input = &tegra_clk_m, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_pllp_out3[] = {
+ { .input = &tegra_pll_p_out3, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_plld_out0[] = {
+ { .input = &tegra_pll_d_out0, .value = 0},
+ { 0, 0},
+};
+
+static struct clk_mux_sel mux_clk_32k[] = {
+ { .input = &tegra_clk_32k, .value = 0},
+ { 0, 0},
+};
+
+#define PERIPH_CLK(_name, _dev, _con, _clk_num, _reg, _max, _inputs, _flags) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ .ops = &tegra_periph_clk_ops, \
+ .reg = _reg, \
+ .inputs = _inputs, \
+ .flags = _flags, \
+ .max_rate = _max, \
+ .u.periph = { \
+ .clk_num = _clk_num, \
+ }, \
+ }
+
+#define SHARED_CLK(_name, _dev, _con, _parent) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ .ops = &tegra_clk_shared_bus_ops, \
+ .parent = _parent, \
+ }
+
+struct clk tegra_list_clks[] = {
+ PERIPH_CLK("rtc", "rtc-tegra", NULL, 4, 0, 32768, mux_clk_32k, PERIPH_NO_RESET),
+ PERIPH_CLK("timer", "timer", NULL, 5, 0, 26000000, mux_clk_m, 0),
+ PERIPH_CLK("i2s0", "i2s.4", NULL, 30, 0x1d8, 26000000, mux_pllaout0_audio0_2x_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("i2s1", "i2s.0", NULL, 11, 0x100, 26000000, mux_pllaout0_audio1_2x_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("i2s2", "i2s.1", NULL, 18, 0x104, 26000000, mux_pllaout0_audio2_2x_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("i2s3", "i2s.2", NULL, 101, 0x3bc, 26000000, mux_pllaout0_audio3_2x_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("i2s4", "i2s.3", NULL, 102, 0x3c0, 26000000, mux_pllaout0_audio4_2x_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("spdif_out", "spdif_out", NULL, 10, 0x108, 100000000, mux_pllaout0_audio_2x_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("spdif_in", "spdif_in", NULL, 10, 0x10c, 100000000, mux_pllp_pllc_pllm, MUX | DIV_U71),
+ PERIPH_CLK("pwm", "pwm", NULL, 17, 0x110, 432000000, mux_pllp_pllc_clk32_clkm, MUX_PWM | DIV_U71),
+ PERIPH_CLK("d_audio", "d_audio", NULL, 106, 0x3d0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("dam0", "dam.0", NULL, 108, 0x3d8, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("dam1", "dam.1", NULL, 109, 0x3dc, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("dam2", "dam.2", NULL, 110, 0x3e0, 48000000, mux_plla_pllc_pllp_clkm, MUX | DIV_U71),
+ PERIPH_CLK("hda", "hda", NULL, 125, 0x428, 48000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("xio", "xio", NULL, 45, 0x120, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("twc", "twc", NULL, 16, 0x12c, 150000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sbc1", "spi_tegra.0", NULL, 41, 0x134, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sbc2", "spi_tegra.1", NULL, 44, 0x118, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sbc3", "spi_tegra.2", NULL, 46, 0x11c, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sbc4", "spi_tegra.3", NULL, 68, 0x1b4, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sbc5", "spi_tegra.4", NULL, 104, 0x3c8, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sbc6", "spi_tegra.5", NULL, 105, 0x3cc, 160000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sata_oob", "sata", NULL, 123, 0x420, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sata", "sata", NULL, 124, 0x424, 100000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("ndflash", "tegra_nand", NULL, 13, 0x160, 164000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("vfir", "vfir", NULL, 7, 0x168, 72000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("sdmmc1", "sdhci-tegra.0", NULL, 14, 0x150, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc2", "sdhci-tegra.1", NULL, 9, 0x154, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc3", "sdhci-tegra.2", NULL, 69, 0x1bc, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("sdmmc4", "sdhci-tegra.3", NULL, 15, 0x164, 52000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("vcp", "tegra-avp", "vcp", 29, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("bsea", "tegra-avp", "bsea", 62, 0, 250000000, mux_clk_m, 0),
+ PERIPH_CLK("vde", "tegra-avp", "vde", 61, 0x1c8, 250000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("csite", "csite", NULL, 73, 0x1d4, 144000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* max rate ??? */
+ /* FIXME: what is la? */
+ PERIPH_CLK("la", "la", NULL, 76, 0x1f8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("owr", "tegra_w1", NULL, 71, 0x1cc, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71),
+ PERIPH_CLK("nor", "nor", NULL, 42, 0x1d0, 92000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("mipi", "mipi", NULL, 50, 0x174, 60000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U71), /* scales with voltage */
+ PERIPH_CLK("i2c1", "tegra-i2c.0", NULL, 12, 0x124, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
+ PERIPH_CLK("i2c2", "tegra-i2c.1", NULL, 54, 0x198, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
+ PERIPH_CLK("i2c3", "tegra-i2c.2", NULL, 67, 0x1b8, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
+ PERIPH_CLK("i2c4", "tegra-i2c.3", NULL, 47, 0x128, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
+ PERIPH_CLK("i2c5", "tegra-i2c.4", NULL, 103, 0x3c4, 26000000, mux_pllp_pllc_pllm_clkm, MUX | DIV_U16),
+ PERIPH_CLK("i2c1_i2c", "tegra-i2c.0", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("i2c2_i2c", "tegra-i2c.1", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("i2c3_i2c", "tegra-i2c.2", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("i2c4_i2c", "tegra-i2c.3", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("i2c5_i2c", "tegra-i2c.4", "i2c", 0, 0, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("uarta", "uart.0", NULL, 6, 0x178, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
+ PERIPH_CLK("uartb", "uart.1", NULL, 7, 0x17c, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
+ PERIPH_CLK("uartc", "uart.2", NULL, 55, 0x1a0, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
+ PERIPH_CLK("uartd", "uart.3", NULL, 65, 0x1c0, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
+ PERIPH_CLK("uarte", "uart.4", NULL, 66, 0x1c4, 600000000, mux_pllp_pllc_pllm_clkm, MUX),
+ PERIPH_CLK("3d", "3d", NULL, 24, 0x158, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
+ PERIPH_CLK("3d2", "3d2", NULL, 98, 0x3b0, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_MANUAL_RESET), /* scales with voltage and process_id */
+ PERIPH_CLK("2d", "2d", NULL, 21, 0x15c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("vi", "tegra_camera", "vi", 20, 0x148, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("vi_sensor", "tegra_camera", "vi_sensor", 20, 0x1a8, 150000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71 | PERIPH_NO_RESET), /* scales with voltage and process_id */
+ PERIPH_CLK("epp", "epp", NULL, 19, 0x16c, 300000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("mpe", "mpe", NULL, 60, 0x170, 250000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("host1x", "host1x", NULL, 28, 0x180, 166000000, mux_pllm_pllc_pllp_plla, MUX | DIV_U71), /* scales with voltage and process_id */
+ PERIPH_CLK("cve", "cve", NULL, 49, 0x140, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("tvo", "tvo", NULL, 49, 0x188, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("hdmi", "hdmi", NULL, 51, 0x18c, 600000000, mux_pllp_pllm_plld_plla_pllc_clkm, MUX8 | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("tvdac", "tvdac", NULL, 53, 0x194, 250000000, mux_pllp_plld_pllc_clkm, MUX | DIV_U71), /* requires min voltage */
+ PERIPH_CLK("disp1", "tegradc.0", NULL, 27, 0x138, 600000000, mux_pllp_pllm_plld_plla_pllc_clkm, MUX8), /* scales with voltage and process_id */
+ PERIPH_CLK("disp2", "tegradc.1", NULL, 26, 0x13c, 600000000, mux_pllp_pllm_plld_plla_pllc_clkm, MUX8), /* scales with voltage and process_id */
+ PERIPH_CLK("usbd", "fsl-tegra-udc", NULL, 22, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb2", "tegra-ehci.1", NULL, 58, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("usb3", "tegra-ehci.2", NULL, 59, 0, 480000000, mux_clk_m, 0), /* requires min voltage */
+ PERIPH_CLK("emc", "emc", NULL, 57, 0x19c, 800000000, mux_pllm_pllc_pllp_clkm, MUX | DIV_U71 | PERIPH_EMC_ENB),
+ PERIPH_CLK("dsi", "dsi", NULL, 48, 0, 500000000, mux_plld_out0, 0), /* scales with voltage */
+ PERIPH_CLK("csi", "tegra_camera", "csi", 52, 0, 72000000, mux_pllp_out3, 0),
+ PERIPH_CLK("isp", "tegra_camera", "isp", 23, 0, 150000000, mux_clk_m, 0), /* same frequency as VI */
+ PERIPH_CLK("csus", "tegra_camera", "csus", 92, 0, 150000000, mux_clk_m, PERIPH_NO_RESET),
+
+ SHARED_CLK("avp.sclk", "tegra-avp", "sclk", &tegra_clk_sclk),
+};
+
+#define CLK_DUPLICATE(_name, _dev, _con) \
+ { \
+ .name = _name, \
+ .lookup = { \
+ .dev_id = _dev, \
+ .con_id = _con, \
+ }, \
+ }
+
+/* Some clocks may be used by different drivers depending on the board
+ * configuration. List those here to register them twice in the clock lookup
+ * table under two names.
+ */
+struct clk_duplicate tegra_clk_duplicates[] = {
+ CLK_DUPLICATE("uarta", "tegra_uart.0", NULL),
+ CLK_DUPLICATE("uartb", "tegra_uart.1", NULL),
+ CLK_DUPLICATE("uartc", "tegra_uart.2", NULL),
+ CLK_DUPLICATE("uartd", "tegra_uart.3", NULL),
+ CLK_DUPLICATE("uarte", "tegra_uart.4", NULL),
+ CLK_DUPLICATE("usbd", "utmip-pad", NULL),
+ CLK_DUPLICATE("usbd", "tegra-ehci.0", NULL),
+ CLK_DUPLICATE("usbd", "tegra-otg", NULL),
+ CLK_DUPLICATE("hdmi", "tegradc.0", "hdmi"),
+ CLK_DUPLICATE("hdmi", "tegradc.1", "hdmi"),
+ CLK_DUPLICATE("pwm", "tegra_pwm.0", NULL),
+ CLK_DUPLICATE("pwm", "tegra_pwm.1", NULL),
+ CLK_DUPLICATE("pwm", "tegra_pwm.2", NULL),
+ CLK_DUPLICATE("pwm", "tegra_pwm.3", NULL),
+ CLK_DUPLICATE("host1x", "tegra_grhost", "host1x"),
+ CLK_DUPLICATE("2d", "tegra_grhost", "gr2d"),
+ CLK_DUPLICATE("3d", "tegra_grhost", "gr3d"),
+ CLK_DUPLICATE("epp", "tegra_grhost", "epp"),
+ CLK_DUPLICATE("mpe", "tegra_grhost", "mpe"),
+ CLK_DUPLICATE("cop", "tegra-avp", "cop"),
+};
+
+struct clk *tegra_ptr_clks[] = {
+ &tegra_clk_32k,
+ &tegra_clk_m,
+ &tegra_pll_ref,
+ &tegra_pll_m,
+ &tegra_pll_m_out1,
+ &tegra_pll_c,
+ &tegra_pll_c_out1,
+ &tegra_pll_p,
+ &tegra_pll_p_out1,
+ &tegra_pll_p_out2,
+ &tegra_pll_p_out3,
+ &tegra_pll_p_out4,
+ &tegra_pll_a,
+ &tegra_pll_a_out0,
+ &tegra_pll_d,
+ &tegra_pll_d_out0,
+ &tegra_pll_u,
+ &tegra_pll_x,
+ &tegra_pll_x_out0,
+ &tegra_clk_cclk,
+ &tegra_clk_sclk,
+ &tegra_clk_hclk,
+ &tegra_clk_pclk,
+ &tegra_dev1_clk,
+ &tegra_dev2_clk,
+ &tegra_clk_virtual_cpu,
+ &tegra_clk_blink,
+ &tegra_clk_cop,
+};
+
+static void tegra3_init_one_clock(struct clk *c)
+{
+ clk_init(c);
+ if (!c->lookup.dev_id && !c->lookup.con_id)
+ c->lookup.con_id = c->name;
+ c->lookup.clk = c;
+ clkdev_add(&c->lookup);
+}
+
+//FIXME: void __init tegra3_init_clocks(void)
+void __init tegra_soc_init_clocks(void)
+{
+ int i;
+ struct clk *c;
+
+ for (i = 0; i < ARRAY_SIZE(tegra_ptr_clks); i++)
+ tegra3_init_one_clock(tegra_ptr_clks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_list_clks); i++)
+ tegra3_init_one_clock(&tegra_list_clks[i]);
+
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_duplicates); i++) {
+ c = tegra_get_clock_by_name(tegra_clk_duplicates[i].name);
+ if (!c) {
+ pr_err("%s: Unknown duplicate clock %s\n", __func__,
+ tegra_clk_duplicates[i].name);
+ continue;
+ }
+
+ tegra_clk_duplicates[i].lookup.clk = c;
+ clkdev_add(&tegra_clk_duplicates[i].lookup);
+ }
+
+ init_audio_sync_clock_mux();
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_list); i++)
+ tegra3_init_one_clock(&tegra_clk_audio_list[i]);
+ for (i = 0; i < ARRAY_SIZE(tegra_clk_audio_2x_list); i++)
+ tegra3_init_one_clock(&tegra_clk_audio_2x_list[i]);
+}
+
+#ifdef CONFIG_PM
+static u32 clk_rst_suspend[RST_DEVICES_NUM + CLK_OUT_ENB_NUM +
+ PERIPH_CLK_SOURCE_NUM + 15];
+
+void tegra_clk_suspend(void)
+{
+ unsigned long off;
+ u32 *ctx = clk_rst_suspend;
+
+ *ctx++ = clk_readl(OSC_CTRL) & OSC_CTRL_MASK;
+ *ctx++ = clk_readl(tegra_pll_c.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+ *ctx++ = clk_readl(tegra_pll_a.reg + PLL_BASE);
+ *ctx++ = clk_readl(tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+
+ *ctx++ = clk_readl(tegra_pll_m_out1.reg);
+ *ctx++ = clk_readl(tegra_pll_a_out0.reg);
+ *ctx++ = clk_readl(tegra_pll_c_out1.reg);
+
+ *ctx++ = clk_readl(tegra_clk_cclk.reg);
+ *ctx++ = clk_readl(tegra_clk_cclk.reg + SUPER_CLK_DIVIDER);
+
+ *ctx++ = clk_readl(tegra_clk_sclk.reg);
+ *ctx++ = clk_readl(tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+ *ctx++ = clk_readl(tegra_clk_pclk.reg);
+
+ for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
+ off += 4) {
+ if (off == PERIPH_CLK_SOURCE_EMC)
+ continue;
+ *ctx++ = clk_readl(off);
+ }
+ for (off = PERIPH_CLK_SOURCE_G3D2; off <= PERIPH_CLK_SOURCE_SE;
+ off+=4) {
+ *ctx++ = clk_readl(off);
+ }
+
+ *ctx++ = clk_readl(RST_DEVICES_L);
+ *ctx++ = clk_readl(RST_DEVICES_H);
+ *ctx++ = clk_readl(RST_DEVICES_U);
+ *ctx++ = clk_readl(RST_DEVICES_V);
+ *ctx++ = clk_readl(RST_DEVICES_W);
+
+ *ctx++ = clk_readl(CLK_OUT_ENB_L);
+ *ctx++ = clk_readl(CLK_OUT_ENB_H);
+ *ctx++ = clk_readl(CLK_OUT_ENB_U);
+ *ctx++ = clk_readl(CLK_OUT_ENB_V);
+ *ctx++ = clk_readl(CLK_OUT_ENB_W);
+
+ *ctx++ = clk_readl(MISC_CLK_ENB);
+ *ctx++ = clk_readl(CLK_MASK_ARM);
+}
+
+void tegra_clk_resume(void)
+{
+ unsigned long off;
+ const u32 *ctx = clk_rst_suspend;
+ u32 val;
+
+ val = clk_readl(OSC_CTRL) & ~OSC_CTRL_MASK;
+ val |= *ctx++;
+ clk_writel(val, OSC_CTRL);
+
+ /* FIXME: add plld, and wait for lock */
+ clk_writel(*ctx++, tegra_pll_c.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_c.reg + PLL_MISC(&tegra_pll_c));
+ clk_writel(*ctx++, tegra_pll_a.reg + PLL_BASE);
+ clk_writel(*ctx++, tegra_pll_a.reg + PLL_MISC(&tegra_pll_a));
+ udelay(300);
+
+ clk_writel(*ctx++, tegra_pll_m_out1.reg);
+ clk_writel(*ctx++, tegra_pll_a_out0.reg);
+ clk_writel(*ctx++, tegra_pll_c_out1.reg);
+
+ clk_writel(*ctx++, tegra_clk_cclk.reg);
+ clk_writel(*ctx++, tegra_clk_cclk.reg + SUPER_CLK_DIVIDER);
+
+ clk_writel(*ctx++, tegra_clk_sclk.reg);
+ clk_writel(*ctx++, tegra_clk_sclk.reg + SUPER_CLK_DIVIDER);
+ clk_writel(*ctx++, tegra_clk_pclk.reg);
+
+ /* enable all clocks before configuring clock sources */
+ clk_writel(0xfdfffff1ul, CLK_OUT_ENB_L);
+ clk_writel(0xfefff7f7ul, CLK_OUT_ENB_H);
+ clk_writel(0x75f79bfful, CLK_OUT_ENB_U);
+ clk_writel(0xfffffffful, CLK_OUT_ENB_V);
+ clk_writel(0x00003ffful, CLK_OUT_ENB_W);
+ wmb();
+
+ for (off = PERIPH_CLK_SOURCE_I2S1; off <= PERIPH_CLK_SOURCE_OSC;
+ off += 4) {
+ if (off == PERIPH_CLK_SOURCE_EMC)
+ continue;
+ clk_writel(*ctx++, off);
+ }
+ for (off = PERIPH_CLK_SOURCE_G3D2; off <= PERIPH_CLK_SOURCE_SE;
+ off += 4) {
+ clk_writel(*ctx++, off);
+ }
+ wmb();
+
+ clk_writel(*ctx++, RST_DEVICES_L);
+ clk_writel(*ctx++, RST_DEVICES_H);
+ clk_writel(*ctx++, RST_DEVICES_U);
+ clk_writel(*ctx++, RST_DEVICES_V);
+ clk_writel(*ctx++, RST_DEVICES_W);
+ wmb();
+
+ clk_writel(*ctx++, CLK_OUT_ENB_L);
+ clk_writel(*ctx++, CLK_OUT_ENB_H);
+ clk_writel(*ctx++, CLK_OUT_ENB_U);
+ clk_writel(*ctx++, CLK_OUT_ENB_V);
+ clk_writel(*ctx++, CLK_OUT_ENB_W);
+ wmb();
+
+ clk_writel(*ctx++, MISC_CLK_ENB);
+ clk_writel(*ctx++, CLK_MASK_ARM);
+}
+#endif
diff --git a/arch/arm/mach-tegra/tegra3_dvfs.c b/arch/arm/mach-tegra/tegra3_dvfs.c
new file mode 100644
index 000000000000..eecbad15e4e7
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_dvfs.c
@@ -0,0 +1,30 @@
+/*
+ * arch/arm/mach-tegra/tegra3_dvfs.c
+ *
+ * Copyright (C) 2010 Google, Inc.
+ *
+ * Author:
+ * Colin Cross <ccross@google.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include "clock.h"
+#include "dvfs.h"
+#include "fuse.h"
+
+void __init tegra_soc_init_dvfs(void)
+{
+}
diff --git a/arch/arm/mach-tegra/tegra3_save.S b/arch/arm/mach-tegra/tegra3_save.S
new file mode 100644
index 000000000000..7f546cf8a380
--- /dev/null
+++ b/arch/arm/mach-tegra/tegra3_save.S
@@ -0,0 +1,480 @@
+/*
+ * arch/arm/mach-tegra/tegra3_save.S
+ *
+ * CPU state save & restore routines for CPU hotplug
+ *
+ * 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/linkage.h>
+#include <linux/init.h>
+
+#include <asm/assembler.h>
+#include <asm/domain.h>
+#include <asm/ptrace.h>
+#include <asm/cache.h>
+#include <asm/vfpmacros.h>
+#include <asm/memory.h>
+#include <asm/hardware/cache-l2x0.h>
+
+#include <mach/iomap.h>
+#include <mach/io.h>
+
+#include "clock.h"
+#include "power.h"
+
+/* .section ".cpuinit.text", "ax"*/
+
+#define TTB_FLAGS 0x6A @ IRGN_WBWA, OC_RGN_WBWA, S, NOS
+
+#define EMC_CFG 0xc
+#define EMC_ADR_CFG 0x10
+#define EMC_REFRESH 0x70
+#define EMC_NOP 0xdc
+#define EMC_SELF_REF 0xe0
+#define EMC_REQ_CTRL 0x2b0
+#define EMC_EMC_STATUS 0x2b4
+
+#define PMC_CTRL 0x0
+#define PMC_CTRL_BFI_SHIFT 8
+#define PMC_CTRL_BFI_WIDTH 9
+#define PMC_SCRATCH1 0x54
+#define PMC_SCRATCH38 0x134
+#define PMC_SCRATCH41 0x140
+
+#define CLK_RESET_CCLK_BURST 0x20
+#define CLK_RESET_CCLK_DIVIDER 0x24
+#define CLK_RESET_SCLK_BURST 0x28
+#define CLK_RESET_SCLK_DIVIDER 0x2c
+
+#define CLK_RESET_PLLC_BASE 0x80
+#define CLK_RESET_PLLM_BASE 0x90
+#define CLK_RESET_PLLP_BASE 0xa0
+#define CLK_RESET_PLLX_BASE 0xe0
+
+#define CLK_RESET_PLLC_MISC 0x8c
+#define CLK_RESET_PLLM_MISC 0x9c
+#define CLK_RESET_PLLP_MISC 0xac
+#define CLK_RESET_PLLX_MISC 0xe4
+
+#define FLOW_CTRL_HALT_CPU_EVENTS 0x0
+
+#include "power-macros.S"
+
+#define DEBUG_SLAVE_HALT_FAIL 1 /* enable slave halt failure debug code */
+#define DEBUG_FORCE_RTC_WAKEUP_SEC 0 /* nonzero: force wake up via RTC */
+
+#if USE_PLL_LOCK_BITS
+.macro pll_locked, rd, car, base
+1:
+ ldr \rd, [\car, #\base]
+ tst \rd, #(1<<27)
+ beq 1b
+.endm
+#endif
+
+.macro pll_enable, rd, car, base, misc
+ ldr \rd, [\car, #\base]
+ tst \rd, #(1<<30)
+ orreq \rd, \rd, #(1<<30)
+ streq \rd, [\car, #\base]
+#if USE_PLL_LOCK_BITS
+ ldr \rd, [\car, #\misc]
+ orr \rd, \rd, #(1<<18)
+ str \rd, [\car, #\misc]
+#endif
+.endm
+
+.macro emc_device_mask, rd, base
+ ldr \rd, [\base, #EMC_ADR_CFG]
+ tst \rd, #(0x3<<24)
+ moveq \rd, #(0x1<<8) @ just 1 device
+ movne \rd, #(0x3<<8) @ 2 devices
+.endm
+
+/*
+ *
+ * __tear_down_master( r8 = context_pa, sp = power state )
+ *
+ * Set the clock burst policy to the selected wakeup source
+ * Enable CPU power-request mode in the PMC
+ * Put the CPU in wait-for-event mode on the flow controller
+ * Trigger the PMC state machine to put the CPU in reset
+ */
+ENTRY(__tear_down_master)
+#ifdef CONFIG_CACHE_L2X0
+ /* clean out the dirtied L2 lines, since all power transitions
+ * cause the cache state to get invalidated (although LP1 & LP2
+ * preserve the data in the L2, the control words (L2X0_CTRL,
+ * L2X0_AUX_CTRL, etc.) need to be cleaned to L3 so that they
+ * will be visible on reboot. skip this for LP0, since the L2 cache
+ * will be shutdown before we reach this point */
+ tst sp, #TEGRA_POWER_EFFECT_LP0
+ bne __l2_clean_done
+ mov32 r0, (TEGRA_ARM_PL310_BASE-IO_CPU_PHYS+IO_CPU_VIRT)
+ add r3, r8, #(CONTEXT_SIZE_BYTES)
+ bic r8, r8, #0x1f
+ add r3, r3, #0x1f
+11: str r8, [r0, #L2X0_CLEAN_LINE_PA]
+ add r8, r8, #32
+ cmp r8, r3
+ blo 11b
+12: ldr r1, [r0, #L2X0_CLEAN_LINE_PA]
+ tst r1, #1
+ bne 12b
+ mov r1, #0
+ str r1, [r0, #L2X0_CACHE_SYNC]
+13: ldr r1, [r0, #L2X0_CACHE_SYNC]
+ tst r1, #1
+ bne 13b
+__l2_clean_done:
+#endif
+
+ /* preload all the address literals that are needed for the
+ * CPU power-gating process, to avoid loads from SDRAM (which are
+ * not supported once SDRAM is put into self-refresh.
+ * LP0 / LP1 use physical address, since the MMU needs to be
+ * disabled before putting SDRAM into self-refresh to avoid
+ * memory access due to page table walks */
+ mov32 r4, TEGRA_PMC_BASE
+ mov32 r5, TEGRA_CLK_RESET_BASE
+ mov32 r6, TEGRA_FLOW_CTRL_BASE
+ mov32 r7, TEGRA_TMRUS_BASE
+
+ /* change page table pointer to tegra_pgd_phys, so that IRAM
+ * and MMU shut-off will be mapped virtual == physical */
+ adr r3, __tear_down_master_data
+ ldr r3, [r3] @ &tegra_pgd_phys
+ ldr r3, [r3]
+ orr r3, r3, #TTB_FLAGS
+ mov r2, #0
+ mcr p15, 0, r2, c13, c0, 1 @ reserved context
+ isb
+ mcr p15, 0, r3, c2, c0, 0 @ TTB 0
+ isb
+
+ /* Obtain LP1 information.
+ * R10 = LP1 branch target */
+ mov32 r2, __tegra_lp1_reset
+ mov32 r3, __tear_down_master_sdram
+ sub r2, r3, r2
+ mov32 r3, (TEGRA_IRAM_CODE_AREA)
+ add r10, r2, r3
+
+ mov32 r3, __shut_off_mmu
+
+ /* R9 = LP2 branch target */
+ mov32 r9, __tear_down_master_pll_cpu
+
+ /* Convert the branch targets
+ * to physical addresses */
+ sub r3, r3, #(PAGE_OFFSET - PHYS_OFFSET)
+ sub r9, r9, #(PAGE_OFFSET - PHYS_OFFSET)
+ tst sp, #TEGRA_POWER_SDRAM_SELFREFRESH
+ movne r9, r10
+ bx r3
+ENDPROC(__tear_down_master)
+ .type __tear_down_master_data, %object
+__tear_down_master_data:
+ .long tegra_pgd_phys
+ .size __tear_down_master_data, . - __tear_down_master_data
+
+/* START OF ROUTINES COPIED TO IRAM */
+/*
+ * __tegra_lp1_reset
+ *
+ * reset vector for LP1 restore; copied into IRAM during suspend.
+ * brings the system back up to a safe starting point (SDRAM out of
+ * self-refresh, PLLC, PLLM and PLLP reenabled, CPU running on PLLP,
+ * system clock running on the same PLL that it suspended at), and
+ * jumps to tegra_lp2_startup to restore PLLX and virtual addressing.
+ * physical address of tegra_lp2_startup expected to be stored in
+ * PMC_SCRATCH1
+ */
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_lp1_reset)
+ /* the CPU and system bus are running at 32KHz and executing from
+ * IRAM when this code is executed; immediately switch to CLKM and
+ * enable PLLP, PLLM, PLLC, and PLLX. */
+ mov32 r0, TEGRA_CLK_RESET_BASE
+ mov r1, #(1<<28)
+ str r1, [r0, #CLK_RESET_SCLK_BURST]
+ str r1, [r0, #CLK_RESET_CCLK_BURST]
+ mov r1, #0
+ str r1, [r0, #CLK_RESET_SCLK_DIVIDER]
+ str r1, [r0, #CLK_RESET_CCLK_DIVIDER]
+
+ pll_enable r1, r0, CLK_RESET_PLLM_BASE, CLK_RESET_PLLM_MISC
+ pll_enable r1, r0, CLK_RESET_PLLP_BASE, CLK_RESET_PLLP_MISC
+ pll_enable r1, r0, CLK_RESET_PLLC_BASE, CLK_RESET_PLLC_MISC
+ pll_enable r1, r0, CLK_RESET_PLLX_BASE, CLK_RESET_PLLX_MISC
+ mov32 r7, TEGRA_TMRUS_BASE
+ ldr r1, [r7]
+
+ /* since the optimized settings are still in SDRAM, there is
+ * no need to store them back into the IRAM-local __lp1_pad_area */
+ add r2, pc, #__lp1_pad_area-(.+8)
+padload:ldmia r2!, {r3-r4}
+ cmp r3, #0
+ beq padload_done
+ str r4, [r3]
+ b padload
+padload_done:
+ ldr r2, [r7]
+ add r2, r2, #0x4 @ 4uS delay for DRAM pad restoration
+ wait_until r2, r7, r3
+#if USE_PLL_LOCK_BITS
+ pll_locked r1, r0, CLK_RESET_PLLM_BASE
+ pll_locked r1, r0, CLK_RESET_PLLP_BASE
+ pll_locked r1, r0, CLK_RESET_PLLC_BASE
+ pll_locked r1, r0, CLK_RESET_PLLX_BASE
+#else
+ add r1, r1, #0xff @ 255uS delay for PLL stabilization
+ wait_until r1, r7, r3
+#endif
+
+ str r4, [r0, #CLK_RESET_SCLK_BURST]
+ mov32 r4, ((1<<28) | (8)) @ burst policy is PLLX
+ str r4, [r0, #CLK_RESET_CCLK_BURST]
+
+ mov32 r0, TEGRA_EMC_BASE
+ ldr r1, [r0, #EMC_CFG]
+ bic r1, r1, #(1<<31) @ disable DRAM_CLK_STOP
+ str r1, [r0, #EMC_CFG]
+
+ mov r1, #0
+ str r1, [r0, #EMC_SELF_REF] @ take DRAM out of self refresh
+ mov r1, #1
+ str r1, [r0, #EMC_NOP]
+ str r1, [r0, #EMC_NOP]
+ str r1, [r0, #EMC_REFRESH]
+
+ emc_device_mask r1, r0
+
+exit_selfrefresh_loop:
+ ldr r2, [r0, #EMC_EMC_STATUS]
+ ands r2, r2, r1
+ bne exit_selfrefresh_loop
+
+ mov r1, #0
+ str r1, [r0, #EMC_REQ_CTRL]
+
+ mov32 r0, TEGRA_PMC_BASE
+ ldr r0, [r0, #PMC_SCRATCH41]
+ mov pc, r0
+ENDPROC(__tegra_lp1_reset)
+
+/*
+ * __tear_down_master_sdram
+ *
+ * disables MMU, data cache, and puts SDRAM into self-refresh.
+ * must execute from IRAM.
+ */
+ .align L1_CACHE_SHIFT
+__tear_down_master_sdram:
+ mov32 r1, TEGRA_EMC_BASE
+ mov r2, #3
+ str r2, [r1, #EMC_REQ_CTRL] @ stall incoming DRAM requests
+
+emcidle:ldr r2, [r1, #EMC_EMC_STATUS]
+ tst r2, #4
+ beq emcidle
+
+ mov r2, #1
+ str r2, [r1, #EMC_SELF_REF]
+
+ emc_device_mask r2, r1
+
+emcself:ldr r3, [r1, #EMC_EMC_STATUS]
+ and r3, r3, r2
+ cmp r3, r2
+ bne emcself @ loop until DDR in self-refresh
+
+ add r2, pc, #__lp1_pad_area-(.+8)
+
+padsave:ldm r2, {r0-r1}
+ cmp r0, #0
+ beq padsave_done
+ ldr r3, [r0]
+ str r1, [r0]
+ str r3, [r2, #4]
+ add r2, r2, #8
+ b padsave
+padsave_done:
+
+ ldr r0, [r5, #CLK_RESET_SCLK_BURST]
+ str r0, [r2, #4]
+ dsb
+ b __tear_down_master_pll_cpu
+ENDPROC(__tear_down_master_sdram)
+
+ .align L1_CACHE_SHIFT
+ .type __lp1_pad_area, %object
+__lp1_pad_area:
+ /* FIXME: Need pad control (address,value) list */
+ .word 0x0 /* end of pad list */
+ .word 0x0 /* sclk_burst_policy */
+ .size __lp1_pad_area, . - __lp1_pad_area
+
+/*
+ * __tear_down_master_pll_cpu()
+ *
+ * LP2 entry code:
+ *
+ * Physical addressing is in effect:
+ *
+ * r4 = TEGRA_PMC_BASE
+ * r5 = TEGRA_CLK_RESET_BASE
+ * r6 = TEGRA_FLOW_CTRL_BASE
+ * r7 = TEGRA_TMRUS_BASE
+ * sp = power mode flags
+ */
+ .align L1_CACHE_SHIFT
+__tear_down_master_pll_cpu:
+ ldr r0, [r4, #PMC_CTRL]
+ bfi r0, sp, #PMC_CTRL_BFI_SHIFT, #PMC_CTRL_BFI_WIDTH
+ str r0, [r4, #PMC_CTRL]
+ /* in LP2 idle (SDRAM active) leave CPU burst policy as it is */
+ tst sp, #TEGRA_POWER_SDRAM_SELFREFRESH
+ beq __do_halt
+
+ /* In other modes, set system & CPU burst policies to 32KHz.
+ * Start by switching to CLKM to safely disable PLLs, then
+ * switch to CLKS. */
+ mov r0, #(1<<28)
+ str r0, [r5, #CLK_RESET_SCLK_BURST]
+ str r0, [r5, #CLK_RESET_CCLK_BURST]
+ mov r0, #0
+ str r0, [r5, #CLK_RESET_CCLK_DIVIDER]
+ str r0, [r5, #CLK_RESET_SCLK_DIVIDER]
+
+ /* 2 us delay between changing sclk and disabling PLLs */
+ wait_for_us r1, r7, r9
+ add r1, r1, #2
+ wait_until r1, r7, r9
+
+ /* switch to CLKS */
+ mov r0, #0 /* burst policy = 32KHz */
+ str r0, [r5, #CLK_RESET_SCLK_BURST]
+
+ /* disable PLLP, PLLM, PLLC, and PLLX in LP0 and LP1 states */
+ ldr r0, [r5, #CLK_RESET_PLLM_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLM_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLP_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLP_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLC_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLC_BASE]
+ ldr r0, [r5, #CLK_RESET_PLLX_BASE]
+ bic r0, r0, #(1<<30)
+ str r0, [r5, #CLK_RESET_PLLX_BASE]
+
+#if DEBUG_FORCE_RTC_WAKEUP_SEC
+ mov32 r0, TEGRA_RTC_BASE
+ /* Clear all RTC interrupts */
+ mov r2, #0x1f
+ str r2, [r0, #0x2c]
+ mov r2, #0x0
+ str r2, [r0, #0x28]
+ //setup rtc wake
+ ldr r2, [r0, #0x10] /* milli */
+ ldr r2, [r0, #0x8] /* shadow */
+
+ add r2, r2, #DEBUG_FORCE_RTC_WAKEUP_SEC
+rtc_idle1:
+ ldr r1, [r0, #0x4]
+ tst r1, #0x1
+ bne rtc_idle1
+ str r2, [r0, #0x14]
+rtc_idle2:
+ ldr r1, [r0, #0x4]
+ tst r1, #0x1
+ bne rtc_idle2
+ /* intr mask alarm0 */
+ mov r2, #1
+ str r2, [r0, #0x28]
+rtc_idle3:
+ ldr r1, [r0, #0x4]
+ tst r1, #0x1
+ bne rtc_idle3
+#endif
+
+__do_halt:
+ mov r0, #(4<<29) /* STOP_UNTIL_IRQ */
+ orr r0, r0, #(1<<10) | (1<<8) /* IRQ_0, FIQ_0 */
+ ldr r1, [r7]
+ str r1, [r4, #PMC_SCRATCH38]
+ dsb
+ str r0, [r6, #FLOW_CTRL_HALT_CPU_EVENTS]
+ dsb
+ ldr r0, [r6, #FLOW_CTRL_HALT_CPU_EVENTS] /* memory barrier */
+
+__halted:
+ dsb
+ wfi /* CPU should be power gated here */
+ isb
+ b __halted
+ENDPROC(__tear_down_master_pll_cpu)
+
+/*
+ * __put_cpu_in_reset(cpu_nr)
+ *
+ * puts the specified CPU in wait-for-event mode on the flow controller
+ * and puts the CPU in reset
+ */
+ENTRY(__put_cpu_in_reset)
+ subs r1, r0, #1
+ bmi . @ should never come here for CPU0
+ mov r1, r1, lsl #3
+ add r2, r1, #0x18
+ add r1, r1, #0x14
+ mov32 r7, (TEGRA_FLOW_CTRL_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+
+ /* Clear this CPU's "event" and "interrupt" flags and power gate
+ it when halting but not before it is in the "WFI" state. */
+ mov32 r3, (1 << 15) | (1 << 14) | 1
+ mov r4, #(1 << 8)
+ orr r3, r3, r4, lsl r0
+ str r3, [r7, r2]
+
+ /* Halt this CPU unconditionally */
+ mov r3, #(2<<29)
+ str r3, [r7, r1]
+ dsb
+ wfi
+
+#if DEBUG_SLAVE_HALT_FAIL
+ b .
+#endif
+
+ /* If the WFI doesn't take effect force the CPU into reset. */
+ movw r1, 0x1011
+ mov r1, r1, lsl r0
+ mov32 r7, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT)
+ str r1, [r7, #0x340]
+ isb
+ dsb
+ b . @ should never get here
+ENDPROC(__put_cpu_in_reset)
+
+/* dummy symbol for end of IRAM */
+ .align L1_CACHE_SHIFT
+ENTRY(__tegra_iram_end)
+ b .
+ENDPROC(__tegra_iram_end)
diff --git a/arch/arm/mach-tegra/timer.c b/arch/arm/mach-tegra/timer.c
index 732a60cab93f..36ef3fd89bf3 100644
--- a/arch/arm/mach-tegra/timer.c
+++ b/arch/arm/mach-tegra/timer.c
@@ -249,6 +249,17 @@ static void __init tegra_init_timer(void)
case 26000000:
timer_writel(0x0019, TIMERUS_USEC_CFG);
break;
+#ifndef CONFIG_ARCH_TEGRA_2x_SOC
+ case 16800000:
+ timer_writel(0x0453, TIMERUS_USEC_CFG);
+ break;
+ case 38400000:
+ timer_writel(0x04BF, TIMERUS_USEC_CFG);
+ break;
+ case 48000000:
+ timer_writel(0x002F, TIMERUS_USEC_CFG);
+ break;
+#endif
default:
WARN(1, "Unknown clock rate");
}
diff --git a/arch/arm/mach-tegra/wakeups-t3.c b/arch/arm/mach-tegra/wakeups-t3.c
new file mode 100644
index 000000000000..da9ecbfd00d7
--- /dev/null
+++ b/arch/arm/mach-tegra/wakeups-t3.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2011, 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/gpio.h>
+#include <linux/init.h>
+#include <linux/io.h>
+
+#include <mach/iomap.h>
+#include <mach/irqs.h>
+
+#include "gpio-names.h"
+
+#define NUM_WAKE_EVENTS 39
+
+static int tegra_wake_event_irq[NUM_WAKE_EVENTS] = {
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO5), /* wake0 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV1), /* wake1 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PL1), /* wake2 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PB6), /* wake3 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN7), /* wake4 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PBB6), /* wake5 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU5), /* wake6 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PU6), /* wake7 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PC7), /* wake8 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS2), /* wake9 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PAA1), /* wake10 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW3), /* wake11 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PW2), /* wake12 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PY6), /* wake13 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PDD3), /* wake14 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ2), /* wake15 */
+ INT_RTC, /* wake16 */
+ INT_KBC, /* wake17 */
+ INT_EXTERNAL_PMU, /* wake18 */
+ -EINVAL, /* TEGRA_USB1_VBUS, */ /* wake19 */
+ -EINVAL, /* TEGRA_USB2_VBUS, */ /* wake20 */
+ -EINVAL, /* TEGRA_USB1_ID, */ /* wake21 */
+ -EINVAL, /* TEGRA_USB2_ID, */ /* wake22 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI5), /* wake23 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PV0), /* wake24 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS4), /* wake25 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS5), /* wake26 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS0), /* wake27 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS6), /* wake28 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PS7), /* wake29 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PN2), /* wake30 */
+ -EINVAL, /* not used */ /* wake31 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PO4), /* wake32 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PJ0), /* wake33 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PK2), /* wake34 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PI6), /* wake35 */
+ TEGRA_GPIO_TO_IRQ(TEGRA_GPIO_PBB1), /* wake36 */
+ -EINVAL, /* TEGRA_USB3_VBUS, */ /* wake37 */
+ -EINVAL, /* TEGRA_USB3_ID, */ /* wake38 */
+};
+
+int tegra_irq_to_wake(int irq)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(tegra_wake_event_irq); i++)
+ if (tegra_wake_event_irq[i] == irq)
+ return i;
+
+ return -EINVAL;
+}
+
+int tegra_wake_to_irq(int wake)
+{
+ if (wake < 0)
+ return -EINVAL;
+
+ if (wake >= NUM_WAKE_EVENTS)
+ return -EINVAL;
+
+ return tegra_wake_event_irq[wake];
+}
diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c
index 5837b8c1dd79..9a9c8b188c08 100644
--- a/drivers/gpio/gpio-tegra.c
+++ b/drivers/gpio/gpio-tegra.c
@@ -20,6 +20,7 @@
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
+#include <linux/delay.h>
#include <linux/io.h>
#include <linux/gpio.h>
@@ -36,10 +37,6 @@
#define GPIO_PORT(x) (((x) >> 3) & 0x3)
#define GPIO_BIT(x) ((x) & 0x7)
-#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \
- GPIO_BANK(x) * 0x80 + \
- GPIO_PORT(x) * 4)
-
#define GPIO_CNF(x) (GPIO_REG(x) + 0x00)
#define GPIO_OE(x) (GPIO_REG(x) + 0x10)
#define GPIO_OUT(x) (GPIO_REG(x) + 0X20)
@@ -49,12 +46,29 @@
#define GPIO_INT_LVL(x) (GPIO_REG(x) + 0x60)
#define GPIO_INT_CLR(x) (GPIO_REG(x) + 0x70)
+#if defined(CONFIG_ARCH_TEGRA_2x_SOC)
+#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \
+ GPIO_BANK(x) * 0x80 + \
+ GPIO_PORT(x) * 4)
+
#define GPIO_MSK_CNF(x) (GPIO_REG(x) + 0x800)
#define GPIO_MSK_OE(x) (GPIO_REG(x) + 0x810)
#define GPIO_MSK_OUT(x) (GPIO_REG(x) + 0X820)
#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + 0x840)
#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + 0x850)
#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + 0x860)
+#else
+#define GPIO_REG(x) (IO_TO_VIRT(TEGRA_GPIO_BASE) + \
+ GPIO_BANK(x) * 0x100 + \
+ GPIO_PORT(x) * 4)
+
+#define GPIO_MSK_CNF(x) (GPIO_REG(x) + 0x80)
+#define GPIO_MSK_OE(x) (GPIO_REG(x) + 0x90)
+#define GPIO_MSK_OUT(x) (GPIO_REG(x) + 0XA0)
+#define GPIO_MSK_INT_STA(x) (GPIO_REG(x) + 0xC0)
+#define GPIO_MSK_INT_ENB(x) (GPIO_REG(x) + 0xD0)
+#define GPIO_MSK_INT_LVL(x) (GPIO_REG(x) + 0xE0)
+#endif
#define GPIO_INT_LVL_MASK 0x010101
#define GPIO_INT_LVL_EDGE_RISING 0x000101
@@ -85,6 +99,9 @@ static struct tegra_gpio_bank tegra_gpio_banks[] = {
{.bank = 4, .irq = INT_GPIO5},
{.bank = 5, .irq = INT_GPIO6},
{.bank = 6, .irq = INT_GPIO7},
+#if defined(CONFIG_ARCH_TEGRA_3x_SOC)
+ {.bank = 7, .irq = INT_GPIO8},
+#endif
};
static int tegra_gpio_compose(int bank, int port, int bit)
@@ -153,6 +170,15 @@ static void tegra_gpio_irq_ack(struct irq_data *d)
int gpio = d->irq - INT_GPIO_BASE;
__raw_writel(1 << GPIO_BIT(gpio), GPIO_INT_CLR(gpio));
+
+#ifdef CONFIG_TEGRA_FPGA_PLATFORM
+ /* FPGA platforms have a serializer between the GPIO
+ block and interrupt controller. Allow time for
+ clearing of the GPIO interrupt to propagate to the
+ interrupt controller before re-enabling the IRQ
+ to prevent double interrupts. */
+ udelay(15);
+#endif
}
static void tegra_gpio_irq_mask(struct irq_data *d)