diff options
author | Scott Williams <scwilliams@nvidia.com> | 2010-12-07 11:19:20 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-04-26 15:48:38 -0700 |
commit | 5e5807fa30957aeac0a6ce727e9ce98c79ec6b8b (patch) | |
tree | 65e08bdaeede5b93cc2db9c9ef9ed8e79c07703f | |
parent | 464936c9afcab046e38fb8f7851627a7ac0520de (diff) |
[ARM/tegra] Add Tegra3 support
Bug 764354
Original-Change-Id: I8a390eb4dae87dceacb97461f23d13554868b046
Reviewed-on: http://git-master/r/12228
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Tested-by: Scott Williams <scwilliams@nvidia.com>
Change-Id: I8e6b8303898796419fb5a759cd16edff9aeac081
50 files changed, 7576 insertions, 198 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index 97419f957321..a2a44e396655 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -18,6 +18,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 comment "Tegra board type" @@ -37,6 +51,16 @@ config MACH_WHISTLER help Support for NVIDIA Whistler development platform +config MACH_ARUBA + bool "Aruba board" + select TEGRA_FPGA_PLATFORM + help + Support for NVIDIA Aruba2 FPGA development platform + +config TEGRA_FPGA_PLATFORM + bool + + choice prompt "Low-level debug console UART" default TEGRA_DEBUG_UART_NONE @@ -115,6 +139,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 fe45f5c3fb89..6f447f9ca50e 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -29,26 +29,33 @@ obj-$(CONFIG_TEGRA_FIQ_DEBUGGER) += tegra_fiq_debugger.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) += 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_2x_SOC) += suspend-t2.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += suspend-t3.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_save.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += tegra3_save.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o obj-$(CONFIG_CPU_V7) += cortex-a9.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pinmux-t2-tables.o +obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pinmux-t3-tables.o obj-$(CONFIG_SMP) += localtimer.o obj-$(CONFIG_SMP) += platsmp.o obj-y += headsmp.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += headsmp-t2.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_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-y += nv/ @@ -70,6 +77,13 @@ obj-${CONFIG_MACH_VENTANA} += board-ventana-sensors.o obj-${CONFIG_MACH_VENTANA} += board-ventana-kbc.o obj-${CONFIG_MACH_VENTANA} += board-ventana-memory.o +obj-${CONFIG_MACH_ARUBA} += board-aruba.o +obj-${CONFIG_MACH_ARUBA} += board-aruba-panel.o +obj-${CONFIG_MACH_ARUBA} += board-aruba-pinmux.o +obj-${CONFIG_MACH_ARUBA} += board-aruba-power.o +obj-${CONFIG_MACH_ARUBA} += board-aruba-sdhci.o +obj-${CONFIG_MACH_ARUBA} += board-aruba-sensors.o + obj-${CONFIG_MACH_WHISTLER} += board-whistler.o obj-${CONFIG_MACH_WHISTLER} += board-whistler-pinmux.o obj-${CONFIG_MACH_WHISTLER} += board-whistler-sdhci.o diff --git a/arch/arm/mach-tegra/Makefile.boot b/arch/arm/mach-tegra/Makefile.boot index db52d61a7386..09ce11970289 100644 --- a/arch/arm/mach-tegra/Makefile.boot +++ b/arch/arm/mach-tegra/Makefile.boot @@ -1,3 +1,7 @@ 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 diff --git a/arch/arm/mach-tegra/clock.c b/arch/arm/mach-tegra/clock.c index 2ee342f35e2d..ce90eb97cc08 100644 --- a/arch/arm/mach-tegra/clock.c +++ b/arch/arm/mach-tegra/clock.c @@ -142,6 +142,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 / 2; /* round up */ @@ -351,7 +354,7 @@ int clk_set_rate(struct clk *c, unsigned long rate) { int ret = 0; unsigned long flags; - unsigned long old_rate; + unsigned long old_rate, max_rate; long new_rate; clk_lock_save(c, flags); @@ -363,8 +366,11 @@ int clk_set_rate(struct clk *c, unsigned long 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); @@ -406,6 +412,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; @@ -422,7 +430,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); @@ -432,8 +440,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); @@ -506,20 +517,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(); } /* @@ -697,6 +710,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"; @@ -722,10 +740,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 9337f6afc4c9..61c727835f0b 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/list.h> -#include <linux/mutex.h> -#include <linux/spinlock.h> -#include <asm/clkdev.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/list.h> +#include <linux/mutex.h> +#include <linux/spinlock.h> +#include <asm/clkdev.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); }; @@ -122,6 +136,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; @@ -130,6 +145,7 @@ struct clk { struct { struct clk *main; struct clk *backup; + unsigned long lp_max_rate; } cpu; struct { struct list_head node; @@ -160,6 +176,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); @@ -184,3 +201,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 4504624cc1fd..6e0e37a77bd5 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -38,7 +38,7 @@ #include "clock.h" #include "fuse.h" -#define MC_SECURITY_CFG2 0x7c +#define MC_SECURITY_CFG2 0x7c unsigned long tegra_bootloader_fb_start; unsigned long tegra_bootloader_fb_size; @@ -68,7 +68,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 }, @@ -85,7 +85,7 @@ static __initdata struct tegra_clk_init_table common_clk_init_table[] = { { "rtc", NULL, 0, true }, /* set frequencies of some device clocks */ - { "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}, @@ -98,6 +98,7 @@ void __init 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) #ifndef CONFIG_TRUSTED_FOUNDATIONS /* ISSUE : Some registers of PL310 controler must be called from Secure context! @@ -114,6 +115,35 @@ void __init tegra_init_cache(void) writel(2, p + L2X0_PWR_CTRL); #endif +#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_OFFSET); + } else { + writel(7, p + L2X0_PREFETCH_OFFSET); + } + } +#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_OFFSET); + writel(2, p + L2X0_PWR_CTRL); +#endif + + /* Enable PL310 double line fill feature. */ + writel(((1<<30) | 7), p + L2X0_PREFETCH_OFFSET); +#endif +#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 c5a1f41e7f11..b99499ce0110 100644 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ b/arch/arm/mach-tegra/cpu-tegra.c @@ -41,12 +41,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; @@ -196,7 +195,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; @@ -312,7 +311,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 e9a2f25da9e8..d1b03b2edc4a 100644 --- a/arch/arm/mach-tegra/cpuidle.c +++ b/arch/arm/mach-tegra/cpuidle.c @@ -48,8 +48,14 @@ #include "power.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 #define EVP_CPU_RESET_VECTOR \ (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) @@ -64,7 +70,9 @@ static bool lp2_in_idle __read_mostly = true; static bool lp2_disabled_by_suspend; module_param(lp2_in_idle, bool, 0644); +#ifdef CONFIG_ARCH_TEGRA_2x_SOC /* !!!DELETEME -- TEMPORARY FOR T30 */ static s64 tegra_cpu1_idle_time = LLONG_MAX;; +#endif static int tegra_lp2_exit_latency; static int tegra_lp2_power_off_time; @@ -269,6 +277,8 @@ static inline void tegra_wake_cpu1(void) static void tegra_idle_enter_lp2_cpu0(struct cpuidle_device *dev, struct cpuidle_state *state) { +/* !!!FIXME!!!! PANICS ON TEGRA3.......................................................................*/ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC s64 request; ktime_t enter; ktime_t exit; @@ -311,7 +321,7 @@ restart: idle_stats.lp2_count++; idle_stats.lp2_count_bin[bin]++; - if (tegra_suspend_lp2(sleep_time) == 0) + if (tegra_suspend_lp2(sleep_time, 0) == 0) sleep_completed = true; else idle_stats.lp2_int_count[tegra_pending_interrupt()]++; @@ -351,12 +361,15 @@ restart: ktime_to_us(ktime_sub(exit, enter)), offset, bin); } +#endif } #ifdef CONFIG_SMP static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev, struct cpuidle_state *state) { +/* !!!FIXME!!!! PANICS ON TEGRA3.......................................................................*/ +#ifdef CONFIG_ARCH_TEGRA_2x_SOC u32 twd_ctrl; u32 twd_load; s64 request; @@ -423,6 +436,7 @@ static void tegra_idle_enter_lp2_cpu1(struct cpuidle_device *dev, out: tegra_legacy_force_irq_clr(TEGRA_CPUIDLE_BOTH_IDLE); +#endif } #endif @@ -545,6 +559,7 @@ static irqreturn_t tegra_cpuidle_irq(int irq, void *dev) pr_err("%s: unexpected interrupt %d on cpu %d\n", __func__, irq, smp_processor_id()); BUG(); + return 0; } static int tegra_cpuidle_pm_notify(struct notifier_block *nb, diff --git a/arch/arm/mach-tegra/devices.c b/arch/arm/mach-tegra/devices.c index c2a1b7f4d828..4f86a47f613f 100644 --- a/arch/arm/mach-tegra/devices.c +++ b/arch/arm/mach-tegra/devices.c @@ -68,6 +68,7 @@ static struct resource i2c_resource3[] = { }, }; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) static struct resource i2c_resource4[] = { [0] = { .start = INT_DVC, @@ -81,6 +82,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 + struct platform_device tegra_i2c_device1 = { .name = "tegra-i2c", .id = 0, @@ -121,10 +150,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] = { @@ -443,6 +484,7 @@ struct platform_device tegra_otg_device = { .num_resources = ARRAY_SIZE(tegra_otg_resources), }; +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) static struct resource i2s_resource1[] = { [0] = { .start = INT_I2S1, @@ -461,6 +503,13 @@ static struct resource i2s_resource1[] = { } }; +struct platform_device tegra_i2s_device1 = { + .name = "i2s", + .id = 0, + .resource = i2s_resource1, + .num_resources = ARRAY_SIZE(i2s_resource1), +}; + static struct resource i2s_resource2[] = { [0] = { .start = INT_I2S2, @@ -479,6 +528,13 @@ static struct resource i2s_resource2[] = { } }; +struct platform_device tegra_i2s_device2 = { + .name = "i2s", + .id = 1, + .resource = i2s_resource2, + .num_resources = ARRAY_SIZE(i2s_resource2), +}; + static struct resource spdif_resource[] = { [0] = { .start = INT_SPDIF, @@ -497,27 +553,6 @@ static struct resource spdif_resource[] = { } }; -struct platform_device tegra_i2s_device1 = { - .name = "i2s", - .id = 0, - .resource = i2s_resource1, - .num_resources = ARRAY_SIZE(i2s_resource1), -}; - -struct platform_device tegra_i2s_device2 = { - .name = "i2s", - .id = 1, - .resource = i2s_resource2, - .num_resources = ARRAY_SIZE(i2s_resource2), -}; - -struct platform_device tegra_spdif_device = { - .name = "spdif_out", - .id = -1, - .resource = spdif_resource, - .num_resources = ARRAY_SIZE(spdif_resource), -}; - static struct resource das_resource[] = { [0] = { .start = TEGRA_APB_MISC_BASE, @@ -533,6 +568,51 @@ struct platform_device tegra_das_device = { .num_resources = ARRAY_SIZE(das_resource), }; +#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 + +struct platform_device tegra_spdif_device = { + .name = "spdif_out", + .id = -1, + .resource = spdif_resource, + .num_resources = ARRAY_SIZE(spdif_resource), +}; + +#if defined(CONFIG_TEGRA_IOVMM_GART) static struct resource tegra_gart_resources[] = { [0] = { .name = "mc", @@ -554,6 +634,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 pmu_resources[] = { [0] = { @@ -566,6 +671,18 @@ static struct resource 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 pmu_device = { diff --git a/arch/arm/mach-tegra/devices.h b/arch/arm/mach-tegra/devices.h index ca5934965a11..14d35866aa6c 100644 --- a/arch/arm/mach-tegra/devices.h +++ b/arch/arm/mach-tegra/devices.h @@ -31,6 +31,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; @@ -42,7 +45,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 pmu_device; extern struct platform_device tegra_wdt_device; extern struct platform_device tegra_pwfm0_device; diff --git a/arch/arm/mach-tegra/dma.c b/arch/arm/mach-tegra/dma.c index a80c71819c19..03c964d28175 100644 --- a/arch/arm/mach-tegra/dma.c +++ b/arch/arm/mach-tegra/dma.c @@ -95,7 +95,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 \ @@ -879,7 +879,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/gpio.c b/arch/arm/mach-tegra/gpio.c index 0187aaa61cd9..8bdef82870e3 100644 --- a/arch/arm/mach-tegra/gpio.c +++ b/arch/arm/mach-tegra/gpio.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> @@ -31,10 +32,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) @@ -44,12 +41,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 @@ -80,6 +94,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) @@ -148,6 +165,15 @@ static void tegra_gpio_irq_ack(unsigned int irq) int gpio = 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(unsigned int irq) @@ -238,6 +264,15 @@ static void tegra_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) for_each_set_bit(pin, &sta, 8) { __raw_writel(1 << pin, 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 + /* if gpio is edge triggered, clear condition * before executing the hander so that we don't * miss edges diff --git a/arch/arm/mach-tegra/headsmp-t3.S b/arch/arm/mach-tegra/headsmp-t3.S new file mode 100644 index 000000000000..5554f3634aee --- /dev/null +++ b/arch/arm/mach-tegra/headsmp-t3.S @@ -0,0 +1,210 @@ +/* + * 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 TTB_FLAGS 0x6A @ IRGN_WBWA, OC_RGN_WBWA, S, NOS + +#define DEBUG_HOTPLUG_STARTUP 0 /* Nonzero for hotplug startup debug */ +#define DEBUG_LP2_STARTUP 0 /* Nonzero for LP2 startup debug */ + +#define PMC_DPD_SAMPLE 0x20 +#define PMC_DPD_ENABLE 0x24 +#define PMC_SCRATCH39 0x138 +#define CLK_RESET_PLLX_BASE 0xe0 +#define CLK_RESET_PLLX_MISC 0xe4 +#define CLK_RESET_PLLP_BASE 0xa0 +#define CLK_RESET_PLLP_OUTA 0xa4 +#define CLK_RESET_PLLP_OUTB 0xa8 +#define CLK_RESET_PLLP_MISC 0xac + +/* .section ".cpuinit.text", "ax"*/ + +/* + * __restart_plls + * + * Loads the saved PLLX and PLLP parameters into the PLLs, to + * allow them to stabilize while the rest of the CPU state is restored. + * Should be called after the MMU is enabled. Jumps directly + * to __cortex_a9_restore + */ + .align L1_CACHE_SHIFT +__restart_plls: + mov32 r0, tegra_sctx + mov32 r3, (TEGRA_CLK_RESET_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT) + mov32 r4, (TEGRA_TMRUS_BASE-IO_PPSB_PHYS+IO_PPSB_VIRT) + + ldr r1, [r0, #0x0] @ pllx_misc + ldr r2, [r0, #0x4] @ pllx_base + str r1, [r3, #CLK_RESET_PLLX_MISC] + str r2, [r3, #CLK_RESET_PLLX_BASE] + + ldr r1, [r0, #0x8] @ pllp_misc + ldr r2, [r0, #0xc] @ pllp_base + str r1, [r3, #CLK_RESET_PLLP_MISC] + str r2, [r3, #CLK_RESET_PLLP_BASE] + + ldr r1, [r0, #0x10] @ pllp_outa + ldr r2, [r0, #0x14] @ pllp_outb + str r1, [r3, #CLK_RESET_PLLP_OUTA] + str r2, [r3, #CLK_RESET_PLLP_OUTB] + + /* record the time that PLLX and PLLP will be stable */ + ldr r1, [r4] + add r1, r1, #300 + str r1, [r0, #0x18] @ pll_timeout + /* FIXME: need to record actual power transition here */ + mov r0, #0 + b __cortex_a9_l2x0_restart +ENDPROC(__restart_plls) + +/* + * tegra_lp2_startup + * + * Secondary CPU boot vector when restarting the master CPU following + * an LP2 idle transition. Re-enable coresight access, re-enable + * MMU, re-start PLLX, restore processor context. + */ + .align L1_CACHE_SHIFT +ENTRY(tegra_lp2_startup) +#if DEBUG_LP2_STARTUP + b . +#endif + setmode PSR_F_BIT | PSR_I_BIT | SVC_MODE, r9 + + mov32 r0, TEGRA_TMRUS_BASE + ldr r1, [r0] + mov32 r0, TEGRA_PMC_BASE + str r1, [r0, #PMC_SCRATCH39] @ save off exact lp2 exit time + mov r1, #0 + str r1, [r0, #PMC_DPD_SAMPLE] + str r1, [r0, #PMC_DPD_ENABLE] + + bl __invalidate_cpu_state + bl __enable_coresite_access + +#ifdef DEBUG + cpu_id r0 + cmp r0, #0 + bne . @ should only come here for CPU0 +#endif + + @ 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] + + mrc p15, 0, r0, c1, c0, 1 + orr r0, r0, #(1 << 6) | (1 << 0) @ re-enable coherency + mcr p15, 0, r0, c1, c0, 1 + + /* enable SCU */ + mov32 r0, TEGRA_ARM_PERIF_BASE + ldr r1, [r0] + orr r1, r1, #1 + str r1, [r0] + + adr r4, __tegra_lp2_data + ldmia r4, {r5, r7, r12} + mov r1, r12 @ ctx_restore = __cortex_a9_restore + sub r4, r4, r5 + ldr r0, [r7, r4] @ pgdir = tegra_pgd_phys + b __return_to_virtual +ENDPROC(tegra_lp2_startup) + .type __tegra_lp2_data, %object +__tegra_lp2_data: + .long . + .long tegra_pgd_phys + .long __restart_plls + .size __tegra_lp2_data, . - __tegra_lp2_data + +#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 0421a5f6e087..974270cb5884 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -40,6 +40,7 @@ str \val, [\tmp] .endm +#ifdef CONFIG_SMP /* * tegra_secondary_startup * 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 30617e5cd8f6..2648e8e02f47 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 8 @@ -98,6 +116,28 @@ #define TEGRA_TMR4_BASE 0x60005058 #define TEGRA_TMR4_SIZE 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 @@ -116,9 +156,13 @@ #define TEGRA_APB_DMA_CH0_BASE 0x6000B000 #define TEGRA_APB_DMA_CH0_SIZE 32 +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + #define TEGRA_AVP_CACHE_BASE 0x6000C000 #define TEGRA_AVP_CACHE_SIZE 4 +#endif + #define TEGRA_AHB_GIZMO_BASE 0x6000C004 #define TEGRA_AHB_GIZMO_SIZE 0x10C @@ -140,6 +184,8 @@ #define TEGRA_APB_MISC_BASE 0x70000000 #define TEGRA_APB_MISC_SIZE SZ_4K +#if defined(CONFIG_ARCH_TEGRA_2x_SOC) + #define TEGRA_AC97_BASE 0x70002000 #define TEGRA_AC97_SIZE SZ_512 @@ -152,6 +198,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 +298,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 +319,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 +346,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 +380,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 +403,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 8f111605e065..b0f348a608a3 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); @@ -201,7 +203,7 @@ void tegra_iovmm_resume(void); #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 7c225b1d4d63..e2a500ef5ae0 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 PHYS_OFFSET UL(0) +#elif defined(CONFIG_ARCH_TEGRA_3x_SOC) +#define 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 b67c9fcd243e..29eaef7cc982 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..3c959bed318d --- /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 + + init_MUTEX(&as->sem); + + 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 1f9e49902188..c127f823286d 100644 --- a/arch/arm/mach-tegra/iovmm.c +++ b/arch/arm/mach-tegra/iovmm.c @@ -280,11 +280,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; @@ -295,8 +295,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 { @@ -354,16 +355,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; @@ -372,7 +373,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); @@ -380,68 +381,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); } @@ -513,7 +512,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; @@ -536,7 +535,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); } } } @@ -580,9 +579,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); @@ -615,26 +614,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_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); @@ -647,7 +648,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; @@ -656,6 +657,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_list_lock); if (share_group) { @@ -684,7 +686,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 47b7064ae061..102cf0c5d7f7 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -44,6 +44,8 @@ static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); +/* !!!FIXME!!! T30 HAS MORE THAN 32 WAKEUP SOURCES SO A SINGLE u32 WON'T WORK ..........................*/ + static u32 tegra_lp0_wake_enb; static u32 tegra_lp0_wake_level; static u32 tegra_lp0_wake_level_any; diff --git a/arch/arm/mach-tegra/legacy_irq.c b/arch/arm/mach-tegra/legacy_irq.c index 5a6197bacbb1..ece56370e56c 100644 --- a/arch/arm/mach-tegra/legacy_irq.c +++ b/arch/arm/mach-tegra/legacy_irq.c @@ -40,17 +40,20 @@ #define ICTLR_COP_IER_CLR 0x38 #define ICTLR_COP_IEP_CLASS 0x3c -#define NUM_ICTLRS 4 +#define NUM_ICTLRS (INT_MAIN_NR/32) static void __iomem *ictlr_reg_base[] = { IO_ADDRESS(TEGRA_PRIMARY_ICTLR_BASE), IO_ADDRESS(TEGRA_SECONDARY_ICTLR_BASE), IO_ADDRESS(TEGRA_TERTIARY_ICTLR_BASE), IO_ADDRESS(TEGRA_QUATERNARY_ICTLR_BASE), +#if (NUM_ICTLRS > 4) + IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), +#endif }; -static u32 tegra_legacy_wake_mask[4]; -static u32 tegra_legacy_saved_mask[4]; +static u32 tegra_legacy_wake_mask[NUM_ICTLRS]; +static u32 tegra_legacy_saved_mask[NUM_ICTLRS]; /* When going into deep sleep, the CPU is powered down, taking the GIC with it In order to wake, the wake interrupts need to be enabled in the legacy 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 a475367befa3..d8ddf0bf55c2 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 b58541009a77..7487758a6989 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); @@ -712,7 +776,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"); @@ -720,10 +784,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]]); @@ -731,6 +797,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 6cacdb910af4..12a4cc260a77 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -35,6 +35,7 @@ #include <asm/mmu_context.h> #include <mach/iomap.h> +#include <mach/powergate.h> #include "power.h" @@ -60,6 +61,40 @@ const struct cpumask *const cpu_init_mask = to_cpumask(cpu_init_bits); (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 void __cpuinit platform_secondary_init(unsigned int cpu) { @@ -88,6 +123,22 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) u32 reg; static void __iomem *vector_base = (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100); #endif + 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; + } /* * set synchronisation state between this boot processor @@ -111,23 +162,44 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) old_boot_vector = readl(vector_base); writel(boot_vector, vector_base); + /* 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); timeout = jiffies + HZ; while (time_before(jiffies, timeout)) { - if (readl(vector_base) != boot_vector) - break; + if (readl(vector_base) != boot_vector) { + status = 0; + goto done; + } udelay(10); } + status = -ETIMEDOUT; +done: /* put the old boot vector back */ writel(old_boot_vector, vector_base); #endif @@ -138,7 +210,7 @@ int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle) */ spin_unlock(&boot_lock); - return 0; + return status; } /* @@ -147,7 +219,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(); for (i = 0; i < ncores; i++) cpu_set(i, cpu_possible_map); @@ -155,7 +227,7 @@ void __init smp_init_cpus(void) void __init smp_prepare_cpus(unsigned int max_cpus) { - unsigned int ncores = scu_get_core_count(scu_base); + unsigned int ncores = available_cpus(); unsigned int cpu = smp_processor_id(); int i; @@ -179,6 +251,9 @@ void __init smp_prepare_cpus(unsigned int max_cpus) init_completion(&per_cpu(cpu_killed, i)); } #endif + /* Switch to the CPU local timer whether there is more than one + CPU present or not. */ + percpu_timer_setup(); /* * Initialise the SCU if there are more than one CPU and let @@ -188,7 +263,6 @@ void __init smp_prepare_cpus(unsigned int max_cpus) * WFI */ if (max_cpus > 1) { - percpu_timer_setup(); scu_enable(scu_base); } } @@ -209,17 +283,18 @@ int platform_cpu_kill(unsigned int cpu) if (e) { do { - reg = readl(CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + reg = readl(CLK_RST_CONTROLLER_CPU_CMPLX_STATUS); cpu_relax(); } while (!(reg & (1<<cpu))); } else { - writel(0x1111<<cpu, CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); + writel(CPU_RESET(cpu), CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET); /* put flow controller in WAIT_EVENT mode */ - writel(2<<29, IO_ADDRESS(TEGRA_FLOW_CTRL_BASE)+0x14 + 0x8*(cpu-1)); + writel(2<<29, FLOW_CTRL_HALT_CPUx_EVENTS(cpu)); } + spin_lock(&boot_lock); 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); spin_unlock(&boot_lock); return e; } @@ -260,3 +335,83 @@ int platform_cpu_disable(unsigned int cpu) return cpu == 0 ? -EPERM : 0; } #endif + +#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/power.h b/arch/arm/mach-tegra/power.h index 37992415b9c6..5e8efcd9d497 100644 --- a/arch/arm/mach-tegra/power.h +++ b/arch/arm/mach-tegra/power.h @@ -25,7 +25,6 @@ #include <asm/page.h> -#define TEGRA_POWER_SDRAM_SELFREFRESH 0x400 /* SDRAM is in self-refresh */ #define TEGRA_POWER_PWRREQ_POLARITY 0x1 /* core power request polarity */ #define TEGRA_POWER_PWRREQ_OE 0x2 /* core power request enable */ @@ -37,6 +36,17 @@ #define TEGRA_POWER_CPU_PWRREQ_OE 0x100 /* CPU power request enable */ #define TEGRA_POWER_PMC_SHIFT 8 #define TEGRA_POWER_PMC_MASK 0x1ff +#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 */ + +/* CPU Context area (1KB per CPU) */ +#define CONTEXT_SIZE_BYTES_SHIFT 10 +#define CONTEXT_SIZE_BYTES (1<<CONTEXT_SIZE_BYTES_SHIFT) /* CPU Context area (1KB per CPU) */ #define CONTEXT_SIZE_BYTES_SHIFT 10 @@ -56,8 +66,21 @@ void __cortex_a9_save(unsigned int mode); void __cortex_a9_restore(void); void __shut_off_mmu(void); void tegra_lp2_startup(void); -unsigned int tegra_suspend_lp2(unsigned int us); void tegra_hotplug_startup(void); +unsigned int tegra_suspend_lp2(unsigned int us, 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 #endif diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index aad47f45831e..1e83837f37da 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 *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/suspend-t3.c b/arch/arm/mach-tegra/suspend-t3.c new file mode 100644 index 000000000000..261c1a8d6abe --- /dev/null +++ b/arch/arm/mach-tegra/suspend-t3.c @@ -0,0 +1,434 @@ +/* + * arch/arm/mach-tegra/suspend-t3.c + * + * Tegra3 suspend + * + * 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/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 "power.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_suspend_lp2(us, flags); + local_irq_enable(); + + DEBUG_SUSPEND(("%s: %s\r\n", __func__, is_lp_cluster() ? "LP" : "G")); + + return 0; +} + +#ifdef CONFIG_PM +void __init lp0_suspend_init(void) +{ + /* Nothing to do for Tegra3 */ +} +#endif + + +#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 < NUM_WAKE_EVENTS; 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/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c index 77c3c1c0fec0..e1989d9d4905 100644 --- a/arch/arm/mach-tegra/suspend.c +++ b/arch/arm/mach-tegra/suspend.c @@ -54,6 +54,7 @@ #include <mach/suspend.h> #include "board.h" +#include "clock.h" #include "power.h" #ifdef CONFIG_TRUSTED_FOUNDATIONS @@ -146,8 +147,15 @@ static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE); #define CLK_RESET_CCLK_BURST_POLICY_PLLM 3 #define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 -#define FLOW_CTRL_CPU_CSR 0x8 -#define FLOW_CTRL_CPU1_CSR 0x18 +#define FLOW_CTRL_CPUx_CSR(cpu) ((cpu) ? 0x18 + 8*((cpu)-1) : 0x8) + +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +#define FLOW_CTRL_BITMAP_MASK (3<<4) +#define FLOW_CTRL_BITMAP_CPU0 (1<<4) /* CPU0 WFE bitmap */ +#else +#define FLOW_CTRL_BITMAP_MASK ((0xF<<4) | (0xF<<8)) +#define FLOW_CTRL_BITMAP_CPU0 (1<<8) /* CPU0 WFI bitmap */ +#endif #define EMC_MRW_0 0x0e8 #define EMC_MRW_DEV_SELECTN 30 @@ -292,25 +300,46 @@ static noinline void restore_cpu_complex(void) { unsigned int reg; - /* restore original burst policy setting; PLLX state restored - * by CPU boot-up code - wait for PLL stabilization if PLLX - * was enabled, or if explicitly requested by caller */ - - BUG_ON(readl(clk_rst + CLK_RESET_PLLX_BASE) != tegra_sctx.pllx_base); - - if (tegra_sctx.pllx_base & (1<<30)) { - while (readl(tmrus)-tegra_sctx.pll_timeout >= 0x80000000UL) - cpu_relax(); + /* 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 + while (readl(tmrus)-tegra_sctx.pll_timeout + >= 0x80000000UL) + cpu_relax(); +#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.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 */ - reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR); - reg &= ~((1<<5) | (1<<4) | 1); /* clear WFE bitmask */ - reg |= (1<<14); /* write-1-clear event flag */ - writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR); + reg = readl(flow_ctrl + FLOW_CTRL_CPUx_CSR(0)); + reg |= (1<<15)|(1<<14); /* write to clear: INTR_FLAG|EVENT_FLAG */ + /* Clear the WFE/WFI bitmaps and power-gate enable. */ + reg &= ~(FLOW_CTRL_BITMAP_MASK | 1); + writel(reg, flow_ctrl + FLOW_CTRL_CPUx_CSR(0)); wmb(); #ifdef CONFIG_HAVE_ARM_TWD @@ -350,18 +379,19 @@ static noinline void suspend_cpu_complex(void) local_timer_stop(); #endif - reg = readl(flow_ctrl + FLOW_CTRL_CPU_CSR); - /* clear any pending events, set the WFE bitmap to specify just - * CPU0, and clear any pending events for this CPU */ - reg &= ~(1<<5); /* clear CPU1 WFE */ - reg |= (1<<14) | (1<<4) | 1; /* enable CPU0 WFE */ - writel(reg, flow_ctrl + FLOW_CTRL_CPU_CSR); + reg = readl(flow_ctrl + FLOW_CTRL_CPUx_CSR(0)); + reg |= (1<<15)|(1<<14); /* write to clear: INTR_FLAG|EVENT_FLAG */ + reg &= ~FLOW_CTRL_BITMAP_MASK; /* WFE/WFI bit maps*/ + /* Set the flow controller bitmap to specify just CPU0. */ + reg |= FLOW_CTRL_BITMAP_CPU0 | 1; /* CPU0 bitmap | power-gate enable */ + writel(reg, flow_ctrl + FLOW_CTRL_CPUx_CSR(0)); wmb(); for (i=1; i<num_present_cpus(); i++) { - unsigned int offs = FLOW_CTRL_CPU1_CSR + (i-1)*8; - reg = readl(flow_ctrl + offs); - writel(reg | (1<<14), flow_ctrl + offs); + reg = readl(flow_ctrl + FLOW_CTRL_CPUx_CSR(i)); + /* write to clear: EVENT_FLAG | INTR_FLAG*/ + reg |= (1<<15) | (1<<14); + writel(reg, flow_ctrl + FLOW_CTRL_CPUx_CSR(i)); wmb(); } @@ -369,7 +399,7 @@ static noinline void suspend_cpu_complex(void) gic_dist_save(0); } -unsigned int tegra_suspend_lp2(unsigned int us) +unsigned int tegra_suspend_lp2(unsigned int us, unsigned int flags) { unsigned int mode; unsigned long orig, reg; @@ -377,6 +407,7 @@ unsigned int tegra_suspend_lp2(unsigned int us) reg = readl(pmc + PMC_CTRL); mode = (reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK; + mode |= flags; mode |= TEGRA_POWER_CPU_PWRREQ_OE; if (pdata->separate_req) mode |= TEGRA_POWER_PWRREQ_OE; @@ -390,6 +421,9 @@ unsigned int tegra_suspend_lp2(unsigned int us) 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); + if (us) tegra_lp2_set_trigger(us); @@ -416,6 +450,9 @@ unsigned int tegra_suspend_lp2(unsigned int us) if (us) tegra_lp2_set_trigger(0); + if (flags & TEGRA_POWER_CLUSTER_MASK) + tegra_cluster_switch_epilog(mode); + writel(orig, evp_reset); return remain; @@ -433,7 +470,7 @@ static u8 *iram_save = NULL; static unsigned int iram_save_size = 0; static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA); -static void tegra_suspend_dram(bool do_lp0) +void tegra_suspend_dram(bool do_lp0) { unsigned int mode = TEGRA_POWER_SDRAM_SELFREFRESH; unsigned long orig, reg; @@ -655,7 +692,7 @@ static int tegra_suspend_enter(suspend_state_t state) rtc_before = tegra_rtc_read_ms(); if (do_lp2) - tegra_suspend_lp2(0); + tegra_suspend_lp2(0, 0); else tegra_suspend_dram(do_lp0); 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 67022f8d1c69..c5c48ef0e734 100644 --- a/arch/arm/mach-tegra/tegra2_clocks.c +++ b/arch/arm/mach-tegra/tegra2_clocks.c @@ -272,18 +272,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 @@ -2250,7 +2238,7 @@ static void tegra2_init_one_clock(struct clk *c) clkdev_add(&c->lookup); } -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..66700b44a73c --- /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 <mach/suspend.h> + +#include "clock.h" +#include "fuse.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 632c58e3e399..a39d4246e09a 100644 --- a/arch/arm/mach-tegra/timer.c +++ b/arch/arm/mach-tegra/timer.c @@ -230,6 +230,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/tools/mach-types b/arch/arm/tools/mach-types index e640a4478e76..54cfc75b9e1b 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -3044,4 +3044,5 @@ harvest_desoto MACH_HARVEST_DESOTO HARVEST_DESOTO 3059 msm8x60_qrdc MACH_MSM8X60_QRDC MSM8X60_QRDC 3060 spear900 MACH_SPEAR900 SPEAR900 3061 pcontrol_g20 MACH_PCONTROL_G20 PCONTROL_G20 3062 -whistler MACH_WHISTLER WHISTLER 3241
\ No newline at end of file +aruba MACH_ARUBA ARUBA 3063 +whistler MACH_WHISTLER WHISTLER 3241 |