diff options
Diffstat (limited to 'arch/arm/mach-tegra')
36 files changed, 851 insertions, 1065 deletions
diff --git a/arch/arm/mach-tegra/Kconfig b/arch/arm/mach-tegra/Kconfig index d1c4893894ce..20c3b372cdf5 100644 --- a/arch/arm/mach-tegra/Kconfig +++ b/arch/arm/mach-tegra/Kconfig @@ -1,13 +1,30 @@ -if ARCH_TEGRA +config ARCH_TEGRA + bool "NVIDIA Tegra" if ARCH_MULTI_V7 + select ARCH_HAS_CPUFREQ + select ARCH_REQUIRE_GPIOLIB + select CLKDEV_LOOKUP + select CLKSRC_MMIO + select CLKSRC_OF + select COMMON_CLK + select GENERIC_CLOCKEVENTS + select HAVE_ARM_SCU if SMP + select HAVE_ARM_TWD if LOCAL_TIMERS + select HAVE_CLK + select HAVE_SMP + select MIGHT_HAVE_CACHE_L2X0 + select SOC_BUS + select SPARSE_IRQ + select USE_OF + help + This enables support for NVIDIA Tegra based systems. -comment "NVIDIA Tegra options" +menu "NVIDIA Tegra options" + depends on ARCH_TEGRA config ARCH_TEGRA_2x_SOC bool "Enable support for Tegra20 family" select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP select ARM_ERRATA_720789 - select ARM_ERRATA_742230 if SMP - select ARM_ERRATA_751472 select ARM_ERRATA_754327 if SMP select ARM_ERRATA_764369 if SMP select ARM_GIC @@ -18,16 +35,14 @@ config ARCH_TEGRA_2x_SOC select PL310_ERRATA_727915 if CACHE_L2X0 select PL310_ERRATA_769419 if CACHE_L2X0 select USB_ARCH_HAS_EHCI if USB_SUPPORT - select USB_ULPI if USB - select USB_ULPI_VIEWPORT if USB_SUPPORT + select USB_ULPI if USB_PHY + select USB_ULPI_VIEWPORT if USB_PHY help 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 "Enable support for Tegra30 family" - select ARM_ERRATA_743622 - select ARM_ERRATA_751472 select ARM_ERRATA_754322 select ARM_ERRATA_764369 if SMP select ARM_GIC @@ -37,8 +52,8 @@ config ARCH_TEGRA_3x_SOC select PINCTRL_TEGRA30 select PL310_ERRATA_769419 if CACHE_L2X0 select USB_ARCH_HAS_EHCI if USB_SUPPORT - select USB_ULPI if USB - select USB_ULPI_VIEWPORT if USB_SUPPORT + select USB_ULPI if USB_PHY + select USB_ULPI_VIEWPORT if USB_PHY help Support for NVIDIA Tegra T30 processor family, based on the ARM CortexA9MP CPU and the ARM PL310 L2 cache controller @@ -71,4 +86,4 @@ config TEGRA_AHB config TEGRA_EMC_SCALING_ENABLE bool "Enable scaling the memory frequency" -endif +endmenu diff --git a/arch/arm/mach-tegra/Makefile b/arch/arm/mach-tegra/Makefile index f6b46ae2b7f8..d011f0ad49c4 100644 --- a/arch/arm/mach-tegra/Makefile +++ b/arch/arm/mach-tegra/Makefile @@ -1,3 +1,5 @@ +asflags-y += -march=armv7-a + obj-y += common.o obj-y += io.o obj-y += irq.o @@ -10,6 +12,7 @@ obj-y += pm.o obj-y += reset.o obj-y += reset-handler.o obj-y += sleep.o +obj-y += tegra.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra20_speedo.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += tegra2_emc.o @@ -24,12 +27,9 @@ obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o endif obj-$(CONFIG_SMP) += platsmp.o headsmp.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o -obj-$(CONFIG_CPU_FREQ) += cpu-tegra.o obj-$(CONFIG_TEGRA_PCI) += pcie.o -obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += board-dt-tegra20.o -obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += board-dt-tegra30.o -obj-$(CONFIG_ARCH_TEGRA_114_SOC) += board-dt-tegra114.o +obj-$(CONFIG_ARCH_TEGRA_114_SOC) += tegra114_speedo.o ifeq ($(CONFIG_CPU_IDLE),y) obj-$(CONFIG_ARCH_TEGRA_114_SOC) += cpuidle-tegra114.o endif diff --git a/arch/arm/mach-tegra/Makefile.boot b/arch/arm/mach-tegra/Makefile.boot deleted file mode 100644 index 29433816233c..000000000000 --- a/arch/arm/mach-tegra/Makefile.boot +++ /dev/null @@ -1,3 +0,0 @@ -zreladdr-$(CONFIG_ARCH_TEGRA_2x_SOC) += 0x00008000 -params_phys-$(CONFIG_ARCH_TEGRA_2x_SOC) := 0x00000100 -initrd_phys-$(CONFIG_ARCH_TEGRA_2x_SOC) := 0x00800000 diff --git a/arch/arm/mach-tegra/board-dt-tegra114.c b/arch/arm/mach-tegra/board-dt-tegra114.c deleted file mode 100644 index 085d63637b62..000000000000 --- a/arch/arm/mach-tegra/board-dt-tegra114.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * NVIDIA Tegra114 device tree board support - * - * Copyright (C) 2013 NVIDIA Corporation - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * 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/of.h> -#include <linux/of_platform.h> -#include <linux/clocksource.h> - -#include <asm/mach/arch.h> - -#include "board.h" -#include "common.h" - -static void __init tegra114_dt_init(void) -{ - of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -} - -static const char * const tegra114_dt_board_compat[] = { - "nvidia,tegra114", - NULL, -}; - -DT_MACHINE_START(TEGRA114_DT, "NVIDIA Tegra114 (Flattened Device Tree)") - .smp = smp_ops(tegra_smp_ops), - .map_io = tegra_map_common_io, - .init_early = tegra114_init_early, - .init_irq = tegra_dt_init_irq, - .init_time = clocksource_of_init, - .init_machine = tegra114_dt_init, - .init_late = tegra_init_late, - .restart = tegra_assert_system_reset, - .dt_compat = tegra114_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-tegra/board-dt-tegra30.c b/arch/arm/mach-tegra/board-dt-tegra30.c deleted file mode 100644 index bf68567e549d..000000000000 --- a/arch/arm/mach-tegra/board-dt-tegra30.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * arch/arm/mach-tegra/board-dt-tegra30.c - * - * NVIDIA Tegra30 device tree board support - * - * Copyright (C) 2011 NVIDIA Corporation - * - * Derived from: - * - * arch/arm/mach-tegra/board-dt-tegra20.c - * - * Copyright (C) 2010 Secret Lab Technologies, Ltd. - * 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. - * - */ - -#include <linux/clocksource.h> -#include <linux/kernel.h> -#include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_fdt.h> -#include <linux/of_irq.h> -#include <linux/of_platform.h> - -#include <asm/mach/arch.h> - -#include "board.h" -#include "common.h" -#include "iomap.h" - -static void __init tegra30_dt_init(void) -{ - of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); -} - -static const char *tegra30_dt_board_compat[] = { - "nvidia,tegra30", - NULL -}; - -DT_MACHINE_START(TEGRA30_DT, "NVIDIA Tegra30 (Flattened Device Tree)") - .smp = smp_ops(tegra_smp_ops), - .map_io = tegra_map_common_io, - .init_early = tegra30_init_early, - .init_irq = tegra_dt_init_irq, - .init_time = clocksource_of_init, - .init_machine = tegra30_dt_init, - .init_late = tegra_init_late, - .restart = tegra_assert_system_reset, - .dt_compat = tegra30_dt_board_compat, -MACHINE_END diff --git a/arch/arm/mach-tegra/board-harmony-pcie.c b/arch/arm/mach-tegra/board-harmony-pcie.c index 3cdc1bb8254c..035b240b9e15 100644 --- a/arch/arm/mach-tegra/board-harmony-pcie.c +++ b/arch/arm/mach-tegra/board-harmony-pcie.c @@ -56,13 +56,17 @@ int __init harmony_pcie_init(void) gpio_direction_output(en_vdd_1v05, 1); regulator = regulator_get(NULL, "vdd_ldo0,vddio_pex_clk"); - if (IS_ERR_OR_NULL(regulator)) { - pr_err("%s: regulator_get failed: %d\n", __func__, - (int)PTR_ERR(regulator)); + if (IS_ERR(regulator)) { + err = PTR_ERR(regulator); + pr_err("%s: regulator_get failed: %d\n", __func__, err); goto err_reg; } - regulator_enable(regulator); + err = regulator_enable(regulator); + if (err) { + pr_err("%s: regulator_enable failed: %d\n", __func__, err); + goto err_en; + } err = tegra_pcie_init(true, true); if (err) { @@ -74,6 +78,7 @@ int __init harmony_pcie_init(void) err_pcie: regulator_disable(regulator); +err_en: regulator_put(regulator); err_reg: gpio_free(en_vdd_1v05); diff --git a/arch/arm/mach-tegra/board.h b/arch/arm/mach-tegra/board.h index 86851c81a350..1787327fae3a 100644 --- a/arch/arm/mach-tegra/board.h +++ b/arch/arm/mach-tegra/board.h @@ -26,9 +26,7 @@ void tegra_assert_system_reset(char mode, const char *cmd); -void __init tegra20_init_early(void); -void __init tegra30_init_early(void); -void __init tegra114_init_early(void); +void __init tegra_init_early(void); void __init tegra_map_common_io(void); void __init tegra_init_irq(void); void __init tegra_dt_init_irq(void); @@ -42,6 +40,7 @@ int tegra_clk_debugfs_init(void); static inline int tegra_clk_debugfs_init(void) { return 0; } #endif +int __init tegra_powergate_init(void); #if defined(CONFIG_ARCH_TEGRA_2x_SOC) && defined(CONFIG_DEBUG_FS) int __init tegra_powergate_debugfs_init(void); #else diff --git a/arch/arm/mach-tegra/common.c b/arch/arm/mach-tegra/common.c index 5449a3f2977b..9f852c6fe5b9 100644 --- a/arch/arm/mach-tegra/common.c +++ b/arch/arm/mach-tegra/common.c @@ -27,12 +27,11 @@ #include <asm/hardware/cache-l2x0.h> -#include <mach/powergate.h> - #include "board.h" #include "common.h" #include "fuse.h" #include "iomap.h" +#include "irq.h" #include "pmc.h" #include "apbio.h" #include "sleep.h" @@ -61,8 +60,10 @@ u32 tegra_uart_config[4] = { void __init tegra_dt_init_irq(void) { tegra_clocks_init(); + tegra_pmc_init(); tegra_init_irq(); irqchip_init(); + tegra_legacy_irq_syscore_init(); } #endif @@ -94,40 +95,18 @@ static void __init tegra_init_cache(void) } -static void __init tegra_init_early(void) +void __init tegra_init_early(void) { tegra_cpu_reset_handler_init(); tegra_apb_io_init(); tegra_init_fuse(); tegra_init_cache(); - tegra_pmc_init(); tegra_powergate_init(); + tegra_hotplug_init(); } -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -void __init tegra20_init_early(void) -{ - tegra_init_early(); - tegra20_hotplug_init(); -} -#endif - -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -void __init tegra30_init_early(void) -{ - tegra_init_early(); - tegra30_hotplug_init(); -} -#endif - -#ifdef CONFIG_ARCH_TEGRA_114_SOC -void __init tegra114_init_early(void) -{ - tegra_init_early(); -} -#endif - void __init tegra_init_late(void) { + tegra_init_suspend(); tegra_powergate_debugfs_init(); } diff --git a/arch/arm/mach-tegra/common.h b/arch/arm/mach-tegra/common.h index 32f8eb3fe344..5900cc44f780 100644 --- a/arch/arm/mach-tegra/common.h +++ b/arch/arm/mach-tegra/common.h @@ -2,4 +2,3 @@ extern struct smp_operations tegra_smp_ops; extern int tegra_cpu_kill(unsigned int cpu); extern void tegra_cpu_die(unsigned int cpu); -extern int tegra_cpu_disable(unsigned int cpu); diff --git a/arch/arm/mach-tegra/cpu-tegra.c b/arch/arm/mach-tegra/cpu-tegra.c deleted file mode 100644 index e3d6e15ff188..000000000000 --- a/arch/arm/mach-tegra/cpu-tegra.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * arch/arm/mach-tegra/cpu-tegra.c - * - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Colin Cross <ccross@google.com> - * Based on arch/arm/plat-omap/cpu-omap.c, (C) 2005 Nokia Corporation - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/sched.h> -#include <linux/cpufreq.h> -#include <linux/delay.h> -#include <linux/init.h> -#include <linux/err.h> -#include <linux/clk.h> -#include <linux/io.h> -#include <linux/suspend.h> - -/* Frequency table index must be sequential starting at 0 */ -static struct cpufreq_frequency_table freq_table[] = { - { 0, 216000 }, - { 1, 312000 }, - { 2, 456000 }, - { 3, 608000 }, - { 4, 760000 }, - { 5, 816000 }, - { 6, 912000 }, - { 7, 1000000 }, - { 8, CPUFREQ_TABLE_END }, -}; - -#define NUM_CPUS 2 - -static struct clk *cpu_clk; -static struct clk *pll_x_clk; -static struct clk *pll_p_clk; -static struct clk *emc_clk; - -static unsigned long target_cpu_speed[NUM_CPUS]; -static DEFINE_MUTEX(tegra_cpu_lock); -static bool is_suspended; - -static int tegra_verify_speed(struct cpufreq_policy *policy) -{ - return cpufreq_frequency_table_verify(policy, freq_table); -} - -static unsigned int tegra_getspeed(unsigned int cpu) -{ - unsigned long rate; - - if (cpu >= NUM_CPUS) - return 0; - - rate = clk_get_rate(cpu_clk) / 1000; - return rate; -} - -static int tegra_cpu_clk_set_rate(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_prepare_enable(pll_x_clk); - - ret = clk_set_parent(cpu_clk, pll_p_clk); - if (ret) { - pr_err("Failed to switch cpu to clock pll_p\n"); - goto out; - } - - if (rate == clk_get_rate(pll_p_clk)) - goto out; - - ret = clk_set_rate(pll_x_clk, rate); - if (ret) { - pr_err("Failed to change pll_x to %lu\n", rate); - goto out; - } - - ret = clk_set_parent(cpu_clk, pll_x_clk); - if (ret) { - pr_err("Failed to switch cpu to clock pll_x\n"); - goto out; - } - -out: - clk_disable_unprepare(pll_x_clk); - return ret; -} - -static int tegra_update_cpu_speed(unsigned long rate) -{ - int ret = 0; - struct cpufreq_freqs freqs; - - freqs.old = tegra_getspeed(0); - freqs.new = rate; - - if (freqs.old == freqs.new) - return ret; - - /* - * Vote on memory bus frequency based on cpu frequency - * This sets the minimum frequency, display or avp may request higher - */ - if (rate >= 816000) - clk_set_rate(emc_clk, 600000000); /* cpu 816 MHz, emc max */ - else if (rate >= 456000) - clk_set_rate(emc_clk, 300000000); /* cpu 456 MHz, emc 150Mhz */ - else - clk_set_rate(emc_clk, 100000000); /* emc 50Mhz */ - - for_each_online_cpu(freqs.cpu) - cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); - -#ifdef CONFIG_CPU_FREQ_DEBUG - printk(KERN_DEBUG "cpufreq-tegra: transition: %u --> %u\n", - freqs.old, freqs.new); -#endif - - ret = tegra_cpu_clk_set_rate(freqs.new * 1000); - if (ret) { - pr_err("cpu-tegra: Failed to set cpu frequency to %d kHz\n", - freqs.new); - return ret; - } - - for_each_online_cpu(freqs.cpu) - cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); - - return 0; -} - -static unsigned long tegra_cpu_highest_speed(void) -{ - unsigned long rate = 0; - int i; - - for_each_online_cpu(i) - rate = max(rate, target_cpu_speed[i]); - return rate; -} - -static int tegra_target(struct cpufreq_policy *policy, - unsigned int target_freq, - unsigned int relation) -{ - unsigned int idx; - unsigned int freq; - int ret = 0; - - mutex_lock(&tegra_cpu_lock); - - if (is_suspended) { - ret = -EBUSY; - goto out; - } - - cpufreq_frequency_table_target(policy, freq_table, target_freq, - relation, &idx); - - freq = freq_table[idx].frequency; - - target_cpu_speed[policy->cpu] = freq; - - ret = tegra_update_cpu_speed(tegra_cpu_highest_speed()); - -out: - mutex_unlock(&tegra_cpu_lock); - return ret; -} - -static int tegra_pm_notify(struct notifier_block *nb, unsigned long event, - void *dummy) -{ - mutex_lock(&tegra_cpu_lock); - if (event == PM_SUSPEND_PREPARE) { - is_suspended = true; - pr_info("Tegra cpufreq suspend: setting frequency to %d kHz\n", - freq_table[0].frequency); - tegra_update_cpu_speed(freq_table[0].frequency); - } else if (event == PM_POST_SUSPEND) { - is_suspended = false; - } - mutex_unlock(&tegra_cpu_lock); - - return NOTIFY_OK; -} - -static struct notifier_block tegra_cpu_pm_notifier = { - .notifier_call = tegra_pm_notify, -}; - -static int tegra_cpu_init(struct cpufreq_policy *policy) -{ - if (policy->cpu >= NUM_CPUS) - return -EINVAL; - - clk_prepare_enable(emc_clk); - clk_prepare_enable(cpu_clk); - - cpufreq_frequency_table_cpuinfo(policy, freq_table); - cpufreq_frequency_table_get_attr(freq_table, policy->cpu); - policy->cur = tegra_getspeed(policy->cpu); - target_cpu_speed[policy->cpu] = policy->cur; - - /* FIXME: what's the actual transition time? */ - policy->cpuinfo.transition_latency = 300 * 1000; - - cpumask_copy(policy->cpus, cpu_possible_mask); - - if (policy->cpu == 0) - register_pm_notifier(&tegra_cpu_pm_notifier); - - return 0; -} - -static int tegra_cpu_exit(struct cpufreq_policy *policy) -{ - cpufreq_frequency_table_cpuinfo(policy, freq_table); - clk_disable_unprepare(emc_clk); - return 0; -} - -static struct freq_attr *tegra_cpufreq_attr[] = { - &cpufreq_freq_attr_scaling_available_freqs, - NULL, -}; - -static struct cpufreq_driver tegra_cpufreq_driver = { - .verify = tegra_verify_speed, - .target = tegra_target, - .get = tegra_getspeed, - .init = tegra_cpu_init, - .exit = tegra_cpu_exit, - .name = "tegra", - .attr = tegra_cpufreq_attr, -}; - -static int __init tegra_cpufreq_init(void) -{ - cpu_clk = clk_get_sys(NULL, "cpu"); - if (IS_ERR(cpu_clk)) - return PTR_ERR(cpu_clk); - - pll_x_clk = clk_get_sys(NULL, "pll_x"); - if (IS_ERR(pll_x_clk)) - return PTR_ERR(pll_x_clk); - - pll_p_clk = clk_get_sys(NULL, "pll_p_cclk"); - if (IS_ERR(pll_p_clk)) - return PTR_ERR(pll_p_clk); - - emc_clk = clk_get_sys("cpu", "emc"); - if (IS_ERR(emc_clk)) { - clk_put(cpu_clk); - return PTR_ERR(emc_clk); - } - - return cpufreq_register_driver(&tegra_cpufreq_driver); -} - -static void __exit tegra_cpufreq_exit(void) -{ - cpufreq_unregister_driver(&tegra_cpufreq_driver); - clk_put(emc_clk); - clk_put(cpu_clk); -} - - -MODULE_AUTHOR("Colin Cross <ccross@android.com>"); -MODULE_DESCRIPTION("cpufreq driver for Nvidia Tegra2"); -MODULE_LICENSE("GPL"); -module_init(tegra_cpufreq_init); -module_exit(tegra_cpufreq_exit); diff --git a/arch/arm/mach-tegra/cpuidle-tegra114.c b/arch/arm/mach-tegra/cpuidle-tegra114.c index 0f4e8c483b34..1d1c6023f4a2 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra114.c +++ b/arch/arm/mach-tegra/cpuidle-tegra114.c @@ -23,39 +23,13 @@ static struct cpuidle_driver tegra_idle_driver = { .name = "tegra_idle", .owner = THIS_MODULE, - .en_core_tk_irqen = 1, .state_count = 1, .states = { [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), }, }; -static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); - int __init tegra114_cpuidle_init(void) { - int ret; - unsigned int cpu; - struct cpuidle_device *dev; - struct cpuidle_driver *drv = &tegra_idle_driver; - - ret = cpuidle_register_driver(&tegra_idle_driver); - if (ret) { - pr_err("CPUidle driver registration failed\n"); - return ret; - } - - for_each_possible_cpu(cpu) { - dev = &per_cpu(tegra_idle_device, cpu); - dev->cpu = cpu; - - dev->state_count = drv->state_count; - ret = cpuidle_register_device(dev); - if (ret) { - pr_err("CPU%u: CPUidle device registration failed\n", - cpu); - return ret; - } - } - return 0; + return cpuidle_register(&tegra_idle_driver, NULL); } diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 825ced4f7a40..0cdba8de8c77 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -43,32 +43,33 @@ static atomic_t abort_barrier; static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); +#define TEGRA20_MAX_STATES 2 +#else +#define TEGRA20_MAX_STATES 1 #endif -static struct cpuidle_state tegra_idle_states[] = { - [0] = ARM_CPUIDLE_WFI_STATE_PWR(600), -#ifdef CONFIG_PM_SLEEP - [1] = { - .enter = tegra20_idle_lp2_coupled, - .exit_latency = 5000, - .target_residency = 10000, - .power_usage = 0, - .flags = CPUIDLE_FLAG_TIME_VALID | - CPUIDLE_FLAG_COUPLED, - .name = "powered-down", - .desc = "CPU power gated", - }, -#endif -}; - static struct cpuidle_driver tegra_idle_driver = { .name = "tegra_idle", .owner = THIS_MODULE, - .en_core_tk_irqen = 1, + .states = { + ARM_CPUIDLE_WFI_STATE_PWR(600), +#ifdef CONFIG_PM_SLEEP + { + .enter = tegra20_idle_lp2_coupled, + .exit_latency = 5000, + .target_residency = 10000, + .power_usage = 0, + .flags = CPUIDLE_FLAG_TIME_VALID | + CPUIDLE_FLAG_COUPLED, + .name = "powered-down", + .desc = "CPU power gated", + }, +#endif + }, + .state_count = TEGRA20_MAX_STATES, + .safe_state_index = 0, }; -static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); - #ifdef CONFIG_PM_SLEEP #ifdef CONFIG_SMP static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); @@ -130,10 +131,6 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - struct cpuidle_state *state = &drv->states[index]; - u32 cpu_on_time = state->exit_latency; - u32 cpu_off_time = state->target_residency - state->exit_latency; - while (tegra20_cpu_is_resettable_soon()) cpu_relax(); @@ -142,7 +139,7 @@ static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev, clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); - tegra_idle_lp2_last(cpu_on_time, cpu_off_time); + tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); @@ -217,39 +214,8 @@ static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev, int __init tegra20_cpuidle_init(void) { - int ret; - unsigned int cpu; - struct cpuidle_device *dev; - struct cpuidle_driver *drv = &tegra_idle_driver; - #ifdef CONFIG_PM_SLEEP tegra_tear_down_cpu = tegra20_tear_down_cpu; #endif - - drv->state_count = ARRAY_SIZE(tegra_idle_states); - memcpy(drv->states, tegra_idle_states, - drv->state_count * sizeof(drv->states[0])); - - ret = cpuidle_register_driver(&tegra_idle_driver); - if (ret) { - pr_err("CPUidle driver registration failed\n"); - return ret; - } - - for_each_possible_cpu(cpu) { - dev = &per_cpu(tegra_idle_device, cpu); - dev->cpu = cpu; -#ifdef CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED - dev->coupled_cpus = *cpu_possible_mask; -#endif - - dev->state_count = drv->state_count; - ret = cpuidle_register_device(dev); - if (ret) { - pr_err("CPU%u: CPUidle device registration failed\n", - cpu); - return ret; - } - } - return 0; + return cpuidle_register(&tegra_idle_driver, cpu_possible_mask); } diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index 8b50cf4ddd6f..3cf9aca5f3ea 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -43,7 +43,6 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, static struct cpuidle_driver tegra_idle_driver = { .name = "tegra_idle", .owner = THIS_MODULE, - .en_core_tk_irqen = 1, #ifdef CONFIG_PM_SLEEP .state_count = 2, #else @@ -65,17 +64,11 @@ static struct cpuidle_driver tegra_idle_driver = { }, }; -static DEFINE_PER_CPU(struct cpuidle_device, tegra_idle_device); - #ifdef CONFIG_PM_SLEEP static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index) { - struct cpuidle_state *state = &drv->states[index]; - u32 cpu_on_time = state->exit_latency; - u32 cpu_off_time = state->target_residency - state->exit_latency; - /* All CPUs entering LP2 is not working. * Don't let CPU0 enter LP2 when any secondary CPU is online. */ @@ -86,7 +79,7 @@ static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev, clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu); - tegra_idle_lp2_last(cpu_on_time, cpu_off_time); + tegra_idle_lp2_last(); clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); @@ -102,12 +95,8 @@ static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev, smp_wmb(); - save_cpu_arch_register(); - cpu_suspend(0, tegra30_sleep_cpu_secondary_finish); - restore_cpu_arch_register(); - clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu); return true; @@ -157,32 +146,8 @@ static int tegra30_idle_lp2(struct cpuidle_device *dev, int __init tegra30_cpuidle_init(void) { - int ret; - unsigned int cpu; - struct cpuidle_device *dev; - struct cpuidle_driver *drv = &tegra_idle_driver; - #ifdef CONFIG_PM_SLEEP tegra_tear_down_cpu = tegra30_tear_down_cpu; #endif - - ret = cpuidle_register_driver(&tegra_idle_driver); - if (ret) { - pr_err("CPUidle driver registration failed\n"); - return ret; - } - - for_each_possible_cpu(cpu) { - dev = &per_cpu(tegra_idle_device, cpu); - dev->cpu = cpu; - - dev->state_count = drv->state_count; - ret = cpuidle_register_device(dev); - if (ret) { - pr_err("CPU%u: CPUidle device registration failed\n", - cpu); - return ret; - } - } - return 0; + return cpuidle_register(&tegra_idle_driver, NULL); } diff --git a/arch/arm/mach-tegra/fuse.c b/arch/arm/mach-tegra/fuse.c index f7db0782a6b6..e035cd284a6e 100644 --- a/arch/arm/mach-tegra/fuse.c +++ b/arch/arm/mach-tegra/fuse.c @@ -2,6 +2,7 @@ * arch/arm/mach-tegra/fuse.c * * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross <ccross@android.com> @@ -137,6 +138,9 @@ void tegra_init_fuse(void) tegra_fuse_spare_bit = TEGRA30_FUSE_SPARE_BIT; tegra_init_speedo_data = &tegra30_init_speedo_data; break; + case TEGRA114: + tegra_init_speedo_data = &tegra114_init_speedo_data; + break; default: pr_warn("Tegra: unknown chip id %d\n", tegra_chip_id); tegra_fuse_spare_bit = TEGRA20_FUSE_SPARE_BIT; diff --git a/arch/arm/mach-tegra/fuse.h b/arch/arm/mach-tegra/fuse.h index da78434678c7..aacc00d05980 100644 --- a/arch/arm/mach-tegra/fuse.h +++ b/arch/arm/mach-tegra/fuse.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2010 Google, Inc. + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. * * Author: * Colin Cross <ccross@android.com> @@ -66,4 +67,10 @@ void tegra30_init_speedo_data(void); static inline void tegra30_init_speedo_data(void) {} #endif +#ifdef CONFIG_ARCH_TEGRA_114_SOC +void tegra114_init_speedo_data(void); +#else +static inline void tegra114_init_speedo_data(void) {} +#endif + #endif diff --git a/arch/arm/mach-tegra/headsmp.S b/arch/arm/mach-tegra/headsmp.S index fd473f2b4c3d..045c16f2dd51 100644 --- a/arch/arm/mach-tegra/headsmp.S +++ b/arch/arm/mach-tegra/headsmp.S @@ -7,8 +7,5 @@ ENTRY(tegra_secondary_startup) bl v7_invalidate_l1 - /* Enable coresight */ - mov32 r0, 0xC5ACCE55 - mcr p14, 0, r0, c7, c12, 6 b secondary_startup ENDPROC(tegra_secondary_startup) diff --git a/arch/arm/mach-tegra/hotplug.c b/arch/arm/mach-tegra/hotplug.c index a599f6e36dea..184914a68d73 100644 --- a/arch/arm/mach-tegra/hotplug.c +++ b/arch/arm/mach-tegra/hotplug.c @@ -1,8 +1,7 @@ /* - * * Copyright (C) 2002 ARM Ltd. * All Rights Reserved - * Copyright (c) 2010, 2012 NVIDIA Corporation. All rights reserved. + * Copyright (c) 2010, 2012-2013, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,9 +11,9 @@ #include <linux/smp.h> #include <linux/clk/tegra.h> -#include <asm/cacheflush.h> #include <asm/smp_plat.h> +#include "fuse.h" #include "sleep.h" static void (*tegra_hotplug_shutdown)(void); @@ -47,27 +46,13 @@ void __ref tegra_cpu_die(unsigned int cpu) BUG(); } -int tegra_cpu_disable(unsigned int cpu) -{ - /* - * we don't allow CPU 0 to be shutdown (it is still too special - * e.g. clock tick interrupts) - */ - return cpu == 0 ? -EPERM : 0; -} - -#ifdef CONFIG_ARCH_TEGRA_2x_SOC -extern void tegra20_hotplug_shutdown(void); -void __init tegra20_hotplug_init(void) +void __init tegra_hotplug_init(void) { - tegra_hotplug_shutdown = tegra20_hotplug_shutdown; -} -#endif + if (!IS_ENABLED(CONFIG_HOTPLUG_CPU)) + return; -#ifdef CONFIG_ARCH_TEGRA_3x_SOC -extern void tegra30_hotplug_shutdown(void); -void __init tegra30_hotplug_init(void) -{ - tegra_hotplug_shutdown = tegra30_hotplug_shutdown; + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) + tegra_hotplug_shutdown = tegra20_hotplug_shutdown; + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) + tegra_hotplug_shutdown = tegra30_hotplug_shutdown; } -#endif diff --git a/arch/arm/mach-tegra/include/mach/powergate.h b/arch/arm/mach-tegra/include/mach/powergate.h deleted file mode 100644 index 06763fe7529d..000000000000 --- a/arch/arm/mach-tegra/include/mach/powergate.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * drivers/regulator/tegra-regulator.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. - * - */ - -#ifndef _MACH_TEGRA_POWERGATE_H_ -#define _MACH_TEGRA_POWERGATE_H_ - -struct clk; - -#define TEGRA_POWERGATE_CPU 0 -#define TEGRA_POWERGATE_3D 1 -#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 -#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_CELP 12 -#define TEGRA_POWERGATE_3D1 13 - -#define TEGRA_POWERGATE_CPU0 TEGRA_POWERGATE_CPU -#define TEGRA_POWERGATE_3D0 TEGRA_POWERGATE_3D - -int __init tegra_powergate_init(void); - -int tegra_cpu_powergate_id(int cpuid); -int tegra_powergate_is_powered(int id); -int tegra_powergate_power_on(int id); -int tegra_powergate_power_off(int id); -int tegra_powergate_remove_clamping(int id); - -/* Must be called with clk disabled, and returns with clk enabled */ -int tegra_powergate_sequence_power_up(int id, struct clk *clk); - -#endif /* _MACH_TEGRA_POWERGATE_H_ */ diff --git a/arch/arm/mach-tegra/include/mach/timex.h b/arch/arm/mach-tegra/include/mach/timex.h deleted file mode 100644 index a44ccbdb7dbf..000000000000 --- a/arch/arm/mach-tegra/include/mach/timex.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * arch/arm/mach-tegra/include/mach/timex.h - * - * Copyright (C) 2010 Google, Inc. - * - * Author: - * Colin Cross <ccross@google.com> - * Erik Gilling <konkers@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. - * - */ - -#ifndef __MACH_TEGRA_TIMEX_H -#define __MACH_TEGRA_TIMEX_H - -#define CLOCK_TICK_RATE 1000000 - -#endif diff --git a/arch/arm/mach-tegra/include/mach/uncompress.h b/arch/arm/mach-tegra/include/mach/uncompress.h deleted file mode 100644 index 08386418196f..000000000000 --- a/arch/arm/mach-tegra/include/mach/uncompress.h +++ /dev/null @@ -1,175 +0,0 @@ -/* - * arch/arm/mach-tegra/include/mach/uncompress.h - * - * Copyright (C) 2010 Google, Inc. - * Copyright (C) 2011 Google, Inc. - * Copyright (C) 2011-2012 NVIDIA CORPORATION. All Rights Reserved. - * - * Author: - * Colin Cross <ccross@google.com> - * Erik Gilling <konkers@google.com> - * Doug Anderson <dianders@chromium.org> - * Stephen Warren <swarren@nvidia.com> - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef __MACH_TEGRA_UNCOMPRESS_H -#define __MACH_TEGRA_UNCOMPRESS_H - -#include <linux/types.h> -#include <linux/serial_reg.h> - -#include "../../iomap.h" - -#define BIT(x) (1 << (x)) -#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) - -#define DEBUG_UART_SHIFT 2 - -volatile u8 *uart; - -static void putc(int c) -{ - if (uart == NULL) - return; - - while (!(uart[UART_LSR << DEBUG_UART_SHIFT] & UART_LSR_THRE)) - barrier(); - uart[UART_TX << DEBUG_UART_SHIFT] = c; -} - -static inline void flush(void) -{ -} - -static const struct { - u32 base; - u32 reset_reg; - u32 clock_reg; - u32 bit; -} uarts[] = { - { - TEGRA_UARTA_BASE, - TEGRA_CLK_RESET_BASE + 0x04, - TEGRA_CLK_RESET_BASE + 0x10, - 6, - }, - { - TEGRA_UARTB_BASE, - TEGRA_CLK_RESET_BASE + 0x04, - TEGRA_CLK_RESET_BASE + 0x10, - 7, - }, - { - TEGRA_UARTC_BASE, - TEGRA_CLK_RESET_BASE + 0x08, - TEGRA_CLK_RESET_BASE + 0x14, - 23, - }, - { - TEGRA_UARTD_BASE, - TEGRA_CLK_RESET_BASE + 0x0c, - TEGRA_CLK_RESET_BASE + 0x18, - 1, - }, - { - TEGRA_UARTE_BASE, - TEGRA_CLK_RESET_BASE + 0x0c, - TEGRA_CLK_RESET_BASE + 0x18, - 2, - }, -}; - -static inline bool uart_clocked(int i) -{ - if (*(u8 *)uarts[i].reset_reg & BIT(uarts[i].bit)) - return false; - - if (!(*(u8 *)uarts[i].clock_reg & BIT(uarts[i].bit))) - return false; - - return true; -} - -#ifdef CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA -int auto_odmdata(void) -{ - volatile u32 *pmc = (volatile u32 *)TEGRA_PMC_BASE; - u32 odmdata = pmc[0xa0 / 4]; - - /* - * Bits 19:18 are the console type: 0=default, 1=none, 2==DCC, 3==UART - * Some boards apparently swap the last two values, but we don't have - * any way of catering for that here, so we just accept either. If this - * doesn't make sense for your board, just don't enable this feature. - * - * Bits 17:15 indicate the UART to use, 0/1/2/3/4 are UART A/B/C/D/E. - */ - - switch ((odmdata >> 18) & 3) { - case 2: - case 3: - break; - default: - return -1; - } - - return (odmdata >> 15) & 7; -} -#endif - -/* - * Setup before decompression. This is where we do UART selection for - * earlyprintk and init the uart_base register. - */ -static inline void arch_decomp_setup(void) -{ - int uart_id; - volatile u32 *apb_misc = (volatile u32 *)TEGRA_APB_MISC_BASE; - u32 chip, div; - -#if defined(CONFIG_TEGRA_DEBUG_UART_AUTO_ODMDATA) - uart_id = auto_odmdata(); -#elif defined(CONFIG_TEGRA_DEBUG_UARTA) - uart_id = 0; -#elif defined(CONFIG_TEGRA_DEBUG_UARTB) - uart_id = 1; -#elif defined(CONFIG_TEGRA_DEBUG_UARTC) - uart_id = 2; -#elif defined(CONFIG_TEGRA_DEBUG_UARTD) - uart_id = 3; -#elif defined(CONFIG_TEGRA_DEBUG_UARTE) - uart_id = 4; -#endif - - if (uart_id < 0 || uart_id >= ARRAY_SIZE(uarts) || - !uart_clocked(uart_id)) - uart = NULL; - else - uart = (volatile u8 *)uarts[uart_id].base; - - if (uart == NULL) - return; - - chip = (apb_misc[0x804 / 4] >> 8) & 0xff; - if (chip == 0x20) - div = 0x0075; - else - div = 0x00dd; - - uart[UART_LCR << DEBUG_UART_SHIFT] |= UART_LCR_DLAB; - uart[UART_DLL << DEBUG_UART_SHIFT] = div & 0xff; - uart[UART_DLM << DEBUG_UART_SHIFT] = div >> 8; - uart[UART_LCR << DEBUG_UART_SHIFT] = 3; -} - -#endif diff --git a/arch/arm/mach-tegra/irq.c b/arch/arm/mach-tegra/irq.c index 1952e82797cc..0de4eed1493d 100644 --- a/arch/arm/mach-tegra/irq.c +++ b/arch/arm/mach-tegra/irq.c @@ -4,7 +4,7 @@ * Author: * Colin Cross <ccross@android.com> * - * Copyright (C) 2010, NVIDIA Corporation + * Copyright (C) 2010,2013, NVIDIA Corporation * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and @@ -23,6 +23,7 @@ #include <linux/io.h> #include <linux/of.h> #include <linux/irqchip/arm-gic.h> +#include <linux/syscore_ops.h> #include "board.h" #include "iomap.h" @@ -43,6 +44,7 @@ #define ICTLR_COP_IEP_CLASS 0x3c #define FIRST_LEGACY_IRQ 32 +#define TEGRA_MAX_NUM_ICTLRS 5 #define SGI_MASK 0xFFFF @@ -56,6 +58,15 @@ static void __iomem *ictlr_reg_base[] = { IO_ADDRESS(TEGRA_QUINARY_ICTLR_BASE), }; +#ifdef CONFIG_PM_SLEEP +static u32 cop_ier[TEGRA_MAX_NUM_ICTLRS]; +static u32 cop_iep[TEGRA_MAX_NUM_ICTLRS]; +static u32 cpu_ier[TEGRA_MAX_NUM_ICTLRS]; +static u32 cpu_iep[TEGRA_MAX_NUM_ICTLRS]; + +static u32 ictlr_wake_mask[TEGRA_MAX_NUM_ICTLRS]; +#endif + bool tegra_pending_sgi(void) { u32 pending_set; @@ -125,6 +136,87 @@ static int tegra_retrigger(struct irq_data *d) return 1; } +#ifdef CONFIG_PM_SLEEP +static int tegra_set_wake(struct irq_data *d, unsigned int enable) +{ + u32 irq = d->irq; + u32 index, mask; + + if (irq < FIRST_LEGACY_IRQ || + irq >= FIRST_LEGACY_IRQ + num_ictlrs * 32) + return -EINVAL; + + index = ((irq - FIRST_LEGACY_IRQ) / 32); + mask = BIT((irq - FIRST_LEGACY_IRQ) % 32); + if (enable) + ictlr_wake_mask[index] |= mask; + else + ictlr_wake_mask[index] &= ~mask; + + return 0; +} + +static int tegra_legacy_irq_suspend(void) +{ + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = ictlr_reg_base[i]; + /* Save interrupt state */ + cpu_ier[i] = readl_relaxed(ictlr + ICTLR_CPU_IER); + cpu_iep[i] = readl_relaxed(ictlr + ICTLR_CPU_IEP_CLASS); + cop_ier[i] = readl_relaxed(ictlr + ICTLR_COP_IER); + cop_iep[i] = readl_relaxed(ictlr + ICTLR_COP_IEP_CLASS); + + /* Disable COP interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + + /* Disable CPU interrupts */ + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + + /* Enable the wakeup sources of ictlr */ + writel_relaxed(ictlr_wake_mask[i], ictlr + ICTLR_CPU_IER_SET); + } + local_irq_restore(flags); + + return 0; +} + +static void tegra_legacy_irq_resume(void) +{ + unsigned long flags; + int i; + + local_irq_save(flags); + for (i = 0; i < num_ictlrs; i++) { + void __iomem *ictlr = ictlr_reg_base[i]; + writel_relaxed(cpu_iep[i], ictlr + ICTLR_CPU_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_CPU_IER_CLR); + writel_relaxed(cpu_ier[i], ictlr + ICTLR_CPU_IER_SET); + writel_relaxed(cop_iep[i], ictlr + ICTLR_COP_IEP_CLASS); + writel_relaxed(~0ul, ictlr + ICTLR_COP_IER_CLR); + writel_relaxed(cop_ier[i], ictlr + ICTLR_COP_IER_SET); + } + local_irq_restore(flags); +} + +static struct syscore_ops tegra_legacy_irq_syscore_ops = { + .suspend = tegra_legacy_irq_suspend, + .resume = tegra_legacy_irq_resume, +}; + +int tegra_legacy_irq_syscore_init(void) +{ + register_syscore_ops(&tegra_legacy_irq_syscore_ops); + + return 0; +} +#else +#define tegra_set_wake NULL +#endif + void __init tegra_init_irq(void) { int i; @@ -150,6 +242,8 @@ void __init tegra_init_irq(void) gic_arch_extn.irq_mask = tegra_mask; gic_arch_extn.irq_unmask = tegra_unmask; gic_arch_extn.irq_retrigger = tegra_retrigger; + gic_arch_extn.irq_set_wake = tegra_set_wake; + gic_arch_extn.flags = IRQCHIP_MASK_ON_SUSPEND; /* * Check if there is a devicetree present, since the GIC will be diff --git a/arch/arm/mach-tegra/irq.h b/arch/arm/mach-tegra/irq.h index 5142649bba05..bc05ce5613fb 100644 --- a/arch/arm/mach-tegra/irq.h +++ b/arch/arm/mach-tegra/irq.h @@ -19,4 +19,10 @@ bool tegra_pending_sgi(void); +#ifdef CONFIG_PM_SLEEP +int tegra_legacy_irq_syscore_init(void); +#else +static inline int tegra_legacy_irq_syscore_init(void) { return 0; } +#endif + #endif diff --git a/arch/arm/mach-tegra/pcie.c b/arch/arm/mach-tegra/pcie.c index b60165f1ca02..46144a19a7e7 100644 --- a/arch/arm/mach-tegra/pcie.c +++ b/arch/arm/mach-tegra/pcie.c @@ -34,12 +34,11 @@ #include <linux/delay.h> #include <linux/export.h> #include <linux/clk/tegra.h> +#include <linux/tegra-powergate.h> #include <asm/sizes.h> #include <asm/mach/pci.h> -#include <mach/powergate.h> - #include "board.h" #include "iomap.h" diff --git a/arch/arm/mach-tegra/platsmp.c b/arch/arm/mach-tegra/platsmp.c index 2c6b3d55213b..fad4226ef710 100644 --- a/arch/arm/mach-tegra/platsmp.c +++ b/arch/arm/mach-tegra/platsmp.c @@ -18,7 +18,6 @@ #include <linux/jiffies.h> #include <linux/smp.h> #include <linux/io.h> -#include <linux/irqchip/arm-gic.h> #include <linux/clk/tegra.h> #include <asm/cacheflush.h> @@ -26,53 +25,58 @@ #include <asm/smp_scu.h> #include <asm/smp_plat.h> -#include <mach/powergate.h> - #include "fuse.h" #include "flowctrl.h" #include "reset.h" +#include "pmc.h" #include "common.h" #include "iomap.h" -extern void tegra_secondary_startup(void); - static cpumask_t tegra_cpu_init_mask; -#define EVP_CPU_RESET_VECTOR \ - (IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE) + 0x100) - static void __cpuinit tegra_secondary_init(unsigned int cpu) { - /* - * if any interrupts are already enabled for the primary - * core (e.g. timer irq), then they will not have been enabled - * for us: do so - */ - gic_secondary_init(0); - cpumask_set_cpu(cpu, &tegra_cpu_init_mask); } -static int tegra20_power_up_cpu(unsigned int cpu) + +static int tegra20_boot_secondary(unsigned int cpu, struct task_struct *idle) { - /* Enable the CPU clock. */ - tegra_enable_cpu_clock(cpu); + cpu = cpu_logical_map(cpu); + + /* + * 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. + */ + tegra_put_cpu_in_reset(cpu); - /* Clear flow controller CSR. */ - flowctrl_write_cpu_csr(cpu, 0); + /* + * 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. + */ + flowctrl_write_cpu_halt(cpu, 0); + tegra_enable_cpu_clock(cpu); + flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ + tegra_cpu_out_of_reset(cpu); return 0; } -static int tegra30_power_up_cpu(unsigned int cpu) +static int tegra30_boot_secondary(unsigned int cpu, struct task_struct *idle) { - int ret, pwrgateid; + int ret; unsigned long timeout; - pwrgateid = tegra_cpu_powergate_id(cpu); - if (pwrgateid < 0) - return pwrgateid; + cpu = cpu_logical_map(cpu); + tegra_put_cpu_in_reset(cpu); + flowctrl_write_cpu_halt(cpu, 0); /* * The power up sequence of cold boot CPU and warm boot CPU @@ -85,13 +89,13 @@ static int tegra30_power_up_cpu(unsigned int cpu) * the IO clamps. * For cold boot CPU, do not wait. After the cold boot CPU be * booted, it will run to tegra_secondary_init() and set - * tegra_cpu_init_mask which influences what tegra30_power_up_cpu() + * tegra_cpu_init_mask which influences what tegra30_boot_secondary() * next time around. */ if (cpumask_test_cpu(cpu, &tegra_cpu_init_mask)) { timeout = jiffies + msecs_to_jiffies(50); do { - if (!tegra_powergate_is_powered(pwrgateid)) + if (tegra_pmc_cpu_is_powered(cpu)) goto remove_clamps; udelay(10); } while (time_before(jiffies, timeout)); @@ -103,14 +107,14 @@ static int tegra30_power_up_cpu(unsigned int cpu) * be un-gated by un-toggling the power gate register * manually. */ - if (!tegra_powergate_is_powered(pwrgateid)) { - ret = tegra_powergate_power_on(pwrgateid); + if (!tegra_pmc_cpu_is_powered(cpu)) { + ret = tegra_pmc_cpu_power_on(cpu); if (ret) return ret; /* Wait for the power to come up. */ timeout = jiffies + msecs_to_jiffies(100); - while (tegra_powergate_is_powered(pwrgateid)) { + while (tegra_pmc_cpu_is_powered(cpu)) { if (time_after(jiffies, timeout)) return -ETIMEDOUT; udelay(10); @@ -123,57 +127,34 @@ remove_clamps: udelay(10); /* Remove I/O clamps. */ - ret = tegra_powergate_remove_clamping(pwrgateid); - udelay(10); + ret = tegra_pmc_cpu_remove_clamping(cpu); + if (ret) + return ret; - /* Clear flow controller CSR. */ - flowctrl_write_cpu_csr(cpu, 0); + udelay(10); + flowctrl_write_cpu_csr(cpu, 0); /* Clear flow controller CSR. */ + tegra_cpu_out_of_reset(cpu); return 0; } -static int __cpuinit tegra_boot_secondary(unsigned int cpu, struct task_struct *idle) +static int tegra114_boot_secondary(unsigned int cpu, struct task_struct *idle) { - int status; - cpu = cpu_logical_map(cpu); + return tegra_pmc_cpu_power_on(cpu); +} - /* - * 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. - */ - tegra_put_cpu_in_reset(cpu); - - /* - * 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. - */ - flowctrl_write_cpu_halt(cpu, 0); - - switch (tegra_chip_id) { - case TEGRA20: - status = tegra20_power_up_cpu(cpu); - break; - case TEGRA30: - status = tegra30_power_up_cpu(cpu); - break; - default: - status = -EINVAL; - break; - } - - if (status) - goto done; - - /* Take the CPU out of reset. */ - tegra_cpu_out_of_reset(cpu); -done: - return status; +static int __cpuinit tegra_boot_secondary(unsigned int cpu, + struct task_struct *idle) +{ + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC) && tegra_chip_id == TEGRA20) + return tegra20_boot_secondary(cpu, idle); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC) && tegra_chip_id == TEGRA30) + return tegra30_boot_secondary(cpu, idle); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_114_SOC) && tegra_chip_id == TEGRA114) + return tegra114_boot_secondary(cpu, idle); + + return -EINVAL; } static void __init tegra_smp_prepare_cpus(unsigned int max_cpus) @@ -192,6 +173,5 @@ struct smp_operations tegra_smp_ops __initdata = { #ifdef CONFIG_HOTPLUG_CPU .cpu_kill = tegra_cpu_kill, .cpu_die = tegra_cpu_die, - .cpu_disable = tegra_cpu_disable, #endif }; diff --git a/arch/arm/mach-tegra/pm.c b/arch/arm/mach-tegra/pm.c index 523604de666f..45cf52c7e528 100644 --- a/arch/arm/mach-tegra/pm.c +++ b/arch/arm/mach-tegra/pm.c @@ -22,7 +22,7 @@ #include <linux/cpumask.h> #include <linux/delay.h> #include <linux/cpu_pm.h> -#include <linux/clk.h> +#include <linux/suspend.h> #include <linux/err.h> #include <linux/clk/tegra.h> @@ -37,67 +37,13 @@ #include "reset.h" #include "flowctrl.h" #include "fuse.h" +#include "pmc.h" #include "sleep.h" -#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ - -#define PMC_CTRL 0x0 -#define PMC_CPUPWRGOOD_TIMER 0xc8 -#define PMC_CPUPWROFF_TIMER 0xcc - #ifdef CONFIG_PM_SLEEP -static unsigned int g_diag_reg; static DEFINE_SPINLOCK(tegra_lp2_lock); -static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); -static struct clk *tegra_pclk; void (*tegra_tear_down_cpu)(void); -void save_cpu_arch_register(void) -{ - /* read diagnostic register */ - asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg) : : "cc"); - return; -} - -void restore_cpu_arch_register(void) -{ - /* write diagnostic register */ - asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg) : "cc"); - return; -} - -static void set_power_timers(unsigned long us_on, unsigned long us_off) -{ - unsigned long long ticks; - unsigned long long pclk; - unsigned long rate; - static unsigned long tegra_last_pclk; - - if (tegra_pclk == NULL) { - tegra_pclk = clk_get_sys(NULL, "pclk"); - WARN_ON(IS_ERR(tegra_pclk)); - } - - rate = clk_get_rate(tegra_pclk); - - if (WARN_ON_ONCE(rate <= 0)) - pclk = 100000000; - else - pclk = rate; - - if ((rate != tegra_last_pclk)) { - ticks = (us_on * pclk) + 999999ull; - do_div(ticks, 1000000); - writel((unsigned long)ticks, pmc + PMC_CPUPWRGOOD_TIMER); - - ticks = (us_off * pclk) + 999999ull; - do_div(ticks, 1000000); - writel((unsigned long)ticks, pmc + PMC_CPUPWROFF_TIMER); - wmb(); - } - tegra_last_pclk = pclk; -} - /* * restore_cpu_complex * @@ -119,8 +65,6 @@ static void restore_cpu_complex(void) tegra_cpu_clock_resume(); flowctrl_cpu_suspend_exit(cpu); - - restore_cpu_arch_register(); } /* @@ -145,8 +89,6 @@ static void suspend_cpu_complex(void) tegra_cpu_clock_suspend(); flowctrl_cpu_suspend_enter(cpu); - - save_cpu_arch_register(); } void tegra_clear_cpu_in_lp2(int phy_cpu_id) @@ -181,14 +123,14 @@ bool tegra_set_cpu_in_lp2(int phy_cpu_id) return last_cpu; } -static int tegra_sleep_cpu(unsigned long v2p) +int tegra_cpu_do_idle(void) { - /* Switch to the identity mapping. */ - cpu_switch_mm(idmap_pgd, &init_mm); - - /* Flush the TLB. */ - local_flush_tlb_all(); + return cpu_do_idle(); +} +static int tegra_sleep_cpu(unsigned long v2p) +{ + setup_mm_for_reboot(); tegra_sleep_cpu_finish(v2p); /* should never here */ @@ -197,16 +139,9 @@ static int tegra_sleep_cpu(unsigned long v2p) return 0; } -void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) +void tegra_idle_lp2_last(void) { - u32 mode; - - /* Only the last cpu down does the final suspend steps */ - mode = readl(pmc + PMC_CTRL); - mode |= TEGRA_POWER_CPU_PWRREQ_OE; - writel(mode, pmc + PMC_CTRL); - - set_power_timers(cpu_on_time, cpu_off_time); + tegra_pmc_pm_set(TEGRA_SUSPEND_LP2); cpu_cluster_pm_enter(); suspend_cpu_complex(); @@ -216,4 +151,81 @@ void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time) restore_cpu_complex(); cpu_cluster_pm_exit(); } + +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + enum tegra_suspend_mode mode) +{ + /* Tegra114 didn't support any suspending mode yet. */ + if (tegra_chip_id == TEGRA114) + return TEGRA_SUSPEND_NONE; + + /* + * The Tegra devices only support suspending to LP2 currently. + */ + if (mode > TEGRA_SUSPEND_LP2) + return TEGRA_SUSPEND_LP2; + + return mode; +} + +static const char *lp_state[TEGRA_MAX_SUSPEND_MODE] = { + [TEGRA_SUSPEND_NONE] = "none", + [TEGRA_SUSPEND_LP2] = "LP2", + [TEGRA_SUSPEND_LP1] = "LP1", + [TEGRA_SUSPEND_LP0] = "LP0", +}; + +static int __cpuinit tegra_suspend_enter(suspend_state_t state) +{ + enum tegra_suspend_mode mode = tegra_pmc_get_suspend_mode(); + + if (WARN_ON(mode < TEGRA_SUSPEND_NONE || + mode >= TEGRA_MAX_SUSPEND_MODE)) + return -EINVAL; + + pr_info("Entering suspend state %s\n", lp_state[mode]); + + tegra_pmc_pm_set(mode); + + local_fiq_disable(); + + suspend_cpu_complex(); + switch (mode) { + case TEGRA_SUSPEND_LP2: + tegra_set_cpu_in_lp2(0); + break; + default: + break; + } + + cpu_suspend(PHYS_OFFSET - PAGE_OFFSET, &tegra_sleep_cpu); + + switch (mode) { + case TEGRA_SUSPEND_LP2: + tegra_clear_cpu_in_lp2(0); + break; + default: + break; + } + restore_cpu_complex(); + + local_fiq_enable(); + + return 0; +} + +static const struct platform_suspend_ops tegra_suspend_ops = { + .valid = suspend_valid_only_mem, + .enter = tegra_suspend_enter, +}; + +void __init tegra_init_suspend(void) +{ + if (tegra_pmc_get_suspend_mode() == TEGRA_SUSPEND_NONE) + return; + + tegra_pmc_suspend_init(); + + suspend_set_ops(&tegra_suspend_ops); +} #endif diff --git a/arch/arm/mach-tegra/pm.h b/arch/arm/mach-tegra/pm.h index 787335cc964c..778a4aa7c3fa 100644 --- a/arch/arm/mach-tegra/pm.h +++ b/arch/arm/mach-tegra/pm.h @@ -21,6 +21,8 @@ #ifndef _MACH_TEGRA_PM_H_ #define _MACH_TEGRA_PM_H_ +#include "pmc.h" + extern unsigned long l2x0_saved_regs_addr; void save_cpu_arch_register(void); @@ -29,7 +31,20 @@ void restore_cpu_arch_register(void); void tegra_clear_cpu_in_lp2(int phy_cpu_id); bool tegra_set_cpu_in_lp2(int phy_cpu_id); -void tegra_idle_lp2_last(u32 cpu_on_time, u32 cpu_off_time); +void tegra_idle_lp2_last(void); extern void (*tegra_tear_down_cpu)(void); +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + enum tegra_suspend_mode mode); +void tegra_init_suspend(void); +#else +static inline enum tegra_suspend_mode tegra_pm_validate_suspend_mode( + enum tegra_suspend_mode mode) +{ + return TEGRA_SUSPEND_NONE; +} +static inline void tegra_init_suspend(void) {} +#endif + #endif /* _MACH_TEGRA_PM_H_ */ diff --git a/arch/arm/mach-tegra/pmc.c b/arch/arm/mach-tegra/pmc.c index d4fdb5fcec20..32360e540ce6 100644 --- a/arch/arm/mach-tegra/pmc.c +++ b/arch/arm/mach-tegra/pmc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 NVIDIA CORPORATION. All rights reserved. + * Copyright (C) 2012,2013 NVIDIA CORPORATION. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -16,59 +16,313 @@ */ #include <linux/kernel.h> +#include <linux/clk.h> #include <linux/io.h> #include <linux/of.h> +#include <linux/of_address.h> -#include "iomap.h" +#include "fuse.h" +#include "pm.h" +#include "pmc.h" +#include "sleep.h" -#define PMC_CTRL 0x0 -#define PMC_CTRL_INTR_LOW (1 << 17) +#define TEGRA_POWER_EFFECT_LP0 (1 << 14) /* LP0 when CPU pwr gated */ +#define TEGRA_POWER_CPU_PWRREQ_POLARITY (1 << 15) /* CPU pwr req polarity */ +#define TEGRA_POWER_CPU_PWRREQ_OE (1 << 16) /* CPU pwr req enable */ + +#define PMC_CTRL 0x0 +#define PMC_CTRL_INTR_LOW (1 << 17) +#define PMC_PWRGATE_TOGGLE 0x30 +#define PMC_PWRGATE_TOGGLE_START (1 << 8) +#define PMC_REMOVE_CLAMPING 0x34 +#define PMC_PWRGATE_STATUS 0x38 + +#define PMC_CPUPWRGOOD_TIMER 0xc8 +#define PMC_CPUPWROFF_TIMER 0xcc + +#define TEGRA_POWERGATE_PCIE 3 +#define TEGRA_POWERGATE_VDEC 4 +#define TEGRA_POWERGATE_CPU1 9 +#define TEGRA_POWERGATE_CPU2 10 +#define TEGRA_POWERGATE_CPU3 11 + +static u8 tegra_cpu_domains[] = { + 0xFF, /* not available for CPU0 */ + TEGRA_POWERGATE_CPU1, + TEGRA_POWERGATE_CPU2, + TEGRA_POWERGATE_CPU3, +}; +static DEFINE_SPINLOCK(tegra_powergate_lock); + +static void __iomem *tegra_pmc_base; +static bool tegra_pmc_invert_interrupt; +static struct clk *tegra_pclk; + +struct pmc_pm_data { + u32 cpu_good_time; /* CPU power good time in uS */ + u32 cpu_off_time; /* CPU power off time in uS */ + u32 core_osc_time; /* Core power good osc time in uS */ + u32 core_pmu_time; /* Core power good pmu time in uS */ + u32 core_off_time; /* Core power off time in uS */ + bool corereq_high; /* Core power request active-high */ + bool sysclkreq_high; /* System clock request active-high */ + bool combined_req; /* Combined pwr req for CPU & Core */ + bool cpu_pwr_good_en; /* CPU power good signal is enabled */ + u32 lp0_vec_phy_addr; /* The phy addr of LP0 warm boot code */ + u32 lp0_vec_size; /* The size of LP0 warm boot code */ + enum tegra_suspend_mode suspend_mode; +}; +static struct pmc_pm_data pmc_pm_data; static inline u32 tegra_pmc_readl(u32 reg) { - return readl(IO_ADDRESS(TEGRA_PMC_BASE + reg)); + return readl(tegra_pmc_base + reg); } static inline void tegra_pmc_writel(u32 val, u32 reg) { - writel(val, IO_ADDRESS(TEGRA_PMC_BASE + reg)); + writel(val, tegra_pmc_base + reg); +} + +static int tegra_pmc_get_cpu_powerdomain_id(int cpuid) +{ + if (cpuid <= 0 || cpuid >= num_possible_cpus()) + return -EINVAL; + return tegra_cpu_domains[cpuid]; +} + +static bool tegra_pmc_powergate_is_powered(int id) +{ + return (tegra_pmc_readl(PMC_PWRGATE_STATUS) >> id) & 1; +} + +static int tegra_pmc_powergate_set(int id, bool new_state) +{ + bool old_state; + unsigned long flags; + + spin_lock_irqsave(&tegra_powergate_lock, flags); + + old_state = tegra_pmc_powergate_is_powered(id); + WARN_ON(old_state == new_state); + + tegra_pmc_writel(PMC_PWRGATE_TOGGLE_START | id, PMC_PWRGATE_TOGGLE); + + spin_unlock_irqrestore(&tegra_powergate_lock, flags); + + return 0; +} + +static int tegra_pmc_powergate_remove_clamping(int id) +{ + u32 mask; + + /* + * Tegra has a bug where PCIE and VDE clamping masks are + * swapped relatively to the partition ids. + */ + if (id == TEGRA_POWERGATE_VDEC) + mask = (1 << TEGRA_POWERGATE_PCIE); + else if (id == TEGRA_POWERGATE_PCIE) + mask = (1 << TEGRA_POWERGATE_VDEC); + else + mask = (1 << id); + + tegra_pmc_writel(mask, PMC_REMOVE_CLAMPING); + + return 0; +} + +bool tegra_pmc_cpu_is_powered(int cpuid) +{ + int id; + + id = tegra_pmc_get_cpu_powerdomain_id(cpuid); + if (id < 0) + return false; + return tegra_pmc_powergate_is_powered(id); } -#ifdef CONFIG_OF +int tegra_pmc_cpu_power_on(int cpuid) +{ + int id; + + id = tegra_pmc_get_cpu_powerdomain_id(cpuid); + if (id < 0) + return id; + return tegra_pmc_powergate_set(id, true); +} + +int tegra_pmc_cpu_remove_clamping(int cpuid) +{ + int id; + + id = tegra_pmc_get_cpu_powerdomain_id(cpuid); + if (id < 0) + return id; + return tegra_pmc_powergate_remove_clamping(id); +} + +#ifdef CONFIG_PM_SLEEP +static void set_power_timers(u32 us_on, u32 us_off, unsigned long rate) +{ + unsigned long long ticks; + unsigned long long pclk; + static unsigned long tegra_last_pclk; + + if (WARN_ON_ONCE(rate <= 0)) + pclk = 100000000; + else + pclk = rate; + + if ((rate != tegra_last_pclk)) { + ticks = (us_on * pclk) + 999999ull; + do_div(ticks, 1000000); + tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWRGOOD_TIMER); + + ticks = (us_off * pclk) + 999999ull; + do_div(ticks, 1000000); + tegra_pmc_writel((unsigned long)ticks, PMC_CPUPWROFF_TIMER); + wmb(); + } + tegra_last_pclk = pclk; +} + +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void) +{ + return pmc_pm_data.suspend_mode; +} + +void tegra_pmc_pm_set(enum tegra_suspend_mode mode) +{ + u32 reg; + unsigned long rate = 0; + + reg = tegra_pmc_readl(PMC_CTRL); + reg |= TEGRA_POWER_CPU_PWRREQ_OE; + reg &= ~TEGRA_POWER_EFFECT_LP0; + + switch (mode) { + case TEGRA_SUSPEND_LP2: + rate = clk_get_rate(tegra_pclk); + break; + default: + break; + } + + set_power_timers(pmc_pm_data.cpu_good_time, pmc_pm_data.cpu_off_time, + rate); + + tegra_pmc_writel(reg, PMC_CTRL); +} + +void tegra_pmc_suspend_init(void) +{ + u32 reg; + + /* Always enable CPU power request */ + reg = tegra_pmc_readl(PMC_CTRL); + reg |= TEGRA_POWER_CPU_PWRREQ_OE; + tegra_pmc_writel(reg, PMC_CTRL); +} +#endif + static const struct of_device_id matches[] __initconst = { + { .compatible = "nvidia,tegra114-pmc" }, + { .compatible = "nvidia,tegra30-pmc" }, { .compatible = "nvidia,tegra20-pmc" }, { } }; -#endif -void __init tegra_pmc_init(void) +static void tegra_pmc_parse_dt(void) { - /* - * For now, Harmony is the only board that uses the PMC, and it wants - * the signal inverted. Seaboard would too if it used the PMC. - * Hopefully by the time other boards want to use the PMC, everything - * will be device-tree, or they also want it inverted. - */ - bool invert_interrupt = true; - u32 val; + struct device_node *np; + u32 prop; + enum tegra_suspend_mode suspend_mode; + u32 core_good_time[2] = {0, 0}; + u32 lp0_vec[2] = {0, 0}; -#ifdef CONFIG_OF - if (of_have_populated_dt()) { - struct device_node *np; + np = of_find_matching_node(NULL, matches); + BUG_ON(!np); - invert_interrupt = false; + tegra_pmc_base = of_iomap(np, 0); - np = of_find_matching_node(NULL, matches); - if (np) { - if (of_find_property(np, "nvidia,invert-interrupt", - NULL)) - invert_interrupt = true; + tegra_pmc_invert_interrupt = of_property_read_bool(np, + "nvidia,invert-interrupt"); + tegra_pclk = of_clk_get_by_name(np, "pclk"); + WARN_ON(IS_ERR(tegra_pclk)); + + /* Grabbing the power management configurations */ + if (of_property_read_u32(np, "nvidia,suspend-mode", &prop)) { + suspend_mode = TEGRA_SUSPEND_NONE; + } else { + switch (prop) { + case 0: + suspend_mode = TEGRA_SUSPEND_LP0; + break; + case 1: + suspend_mode = TEGRA_SUSPEND_LP1; + break; + case 2: + suspend_mode = TEGRA_SUSPEND_LP2; + break; + default: + suspend_mode = TEGRA_SUSPEND_NONE; + break; } } -#endif + suspend_mode = tegra_pm_validate_suspend_mode(suspend_mode); + + if (of_property_read_u32(np, "nvidia,cpu-pwr-good-time", &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.cpu_good_time = prop; + + if (of_property_read_u32(np, "nvidia,cpu-pwr-off-time", &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.cpu_off_time = prop; + + if (of_property_read_u32_array(np, "nvidia,core-pwr-good-time", + core_good_time, ARRAY_SIZE(core_good_time))) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.core_osc_time = core_good_time[0]; + pmc_pm_data.core_pmu_time = core_good_time[1]; + + if (of_property_read_u32(np, "nvidia,core-pwr-off-time", + &prop)) + suspend_mode = TEGRA_SUSPEND_NONE; + pmc_pm_data.core_off_time = prop; + + pmc_pm_data.corereq_high = of_property_read_bool(np, + "nvidia,core-power-req-active-high"); + + pmc_pm_data.sysclkreq_high = of_property_read_bool(np, + "nvidia,sys-clock-req-active-high"); + + pmc_pm_data.combined_req = of_property_read_bool(np, + "nvidia,combined-power-req"); + + pmc_pm_data.cpu_pwr_good_en = of_property_read_bool(np, + "nvidia,cpu-pwr-good-en"); + + if (of_property_read_u32_array(np, "nvidia,lp0-vec", lp0_vec, + ARRAY_SIZE(lp0_vec))) + if (suspend_mode == TEGRA_SUSPEND_LP0) + suspend_mode = TEGRA_SUSPEND_LP1; + + pmc_pm_data.lp0_vec_phy_addr = lp0_vec[0]; + pmc_pm_data.lp0_vec_size = lp0_vec[1]; + + pmc_pm_data.suspend_mode = suspend_mode; +} + +void __init tegra_pmc_init(void) +{ + u32 val; + + tegra_pmc_parse_dt(); val = tegra_pmc_readl(PMC_CTRL); - if (invert_interrupt) + if (tegra_pmc_invert_interrupt) val |= PMC_CTRL_INTR_LOW; else val &= ~PMC_CTRL_INTR_LOW; diff --git a/arch/arm/mach-tegra/pmc.h b/arch/arm/mach-tegra/pmc.h index 8995ee4a8768..e1c2df272f7d 100644 --- a/arch/arm/mach-tegra/pmc.h +++ b/arch/arm/mach-tegra/pmc.h @@ -18,6 +18,24 @@ #ifndef __MACH_TEGRA_PMC_H #define __MACH_TEGRA_PMC_H +enum tegra_suspend_mode { + TEGRA_SUSPEND_NONE = 0, + TEGRA_SUSPEND_LP2, /* CPU voltage off */ + TEGRA_SUSPEND_LP1, /* CPU voltage off, DRAM self-refresh */ + TEGRA_SUSPEND_LP0, /* CPU + core voltage off, DRAM self-refresh */ + TEGRA_MAX_SUSPEND_MODE, +}; + +#ifdef CONFIG_PM_SLEEP +enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void); +void tegra_pmc_pm_set(enum tegra_suspend_mode mode); +void tegra_pmc_suspend_init(void); +#endif + +bool tegra_pmc_cpu_is_powered(int cpuid); +int tegra_pmc_cpu_power_on(int cpuid); +int tegra_pmc_cpu_remove_clamping(int cpuid); + void tegra_pmc_init(void); #endif diff --git a/arch/arm/mach-tegra/powergate.c b/arch/arm/mach-tegra/powergate.c index c6bc8f85759c..f076f0f80fcd 100644 --- a/arch/arm/mach-tegra/powergate.c +++ b/arch/arm/mach-tegra/powergate.c @@ -22,13 +22,13 @@ #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/err.h> +#include <linux/export.h> #include <linux/init.h> #include <linux/io.h> #include <linux/seq_file.h> #include <linux/spinlock.h> #include <linux/clk/tegra.h> - -#include <mach/powergate.h> +#include <linux/tegra-powergate.h> #include "fuse.h" #include "iomap.h" @@ -75,7 +75,7 @@ static int tegra_powergate_set(int id, bool new_state) if (status == new_state) { spin_unlock_irqrestore(&tegra_powergate_lock, flags); - return -EINVAL; + return 0; } pmc_write(PWRGATE_TOGGLE_START | id, PWRGATE_TOGGLE); @@ -168,6 +168,7 @@ err_clk: err_power: return ret; } +EXPORT_SYMBOL(tegra_powergate_sequence_power_up); int tegra_cpu_powergate_id(int cpuid) { diff --git a/arch/arm/mach-tegra/reset-handler.S b/arch/arm/mach-tegra/reset-handler.S index 54382ceade4a..e6de88a2ea06 100644 --- a/arch/arm/mach-tegra/reset-handler.S +++ b/arch/arm/mach-tegra/reset-handler.S @@ -41,12 +41,10 @@ */ ENTRY(tegra_resume) bl v7_invalidate_l1 - /* Enable coresight */ - mov32 r0, 0xC5ACCE55 - mcr p14, 0, r0, c7, c12, 6 cpu_id r0 cmp r0, #0 @ CPU0? + THUMB( it ne ) bne cpu_resume @ no #ifdef CONFIG_ARCH_TEGRA_3x_SOC @@ -99,6 +97,8 @@ ENTRY(__tegra_cpu_reset_handler_start) * * Register usage within the reset handler: * + * Others: scratch + * R6 = SoC ID << 8 * R7 = CPU present (to the OS) mask * R8 = CPU in LP1 state mask * R9 = CPU in LP2 state mask @@ -114,6 +114,40 @@ ENTRY(__tegra_cpu_reset_handler_start) ENTRY(__tegra_cpu_reset_handler) cpsid aif, 0x13 @ SVC mode, interrupts disabled + + mov32 r6, TEGRA_APB_MISC_BASE + ldr r6, [r6, #APB_MISC_GP_HIDREV] + and r6, r6, #0xff00 +#ifdef CONFIG_ARCH_TEGRA_2x_SOC +t20_check: + cmp r6, #(0x20 << 8) + bne after_t20_check +t20_errata: + # Tegra20 is a Cortex-A9 r1p1 + mrc p15, 0, r0, c1, c0, 0 @ read system control register + orr r0, r0, #1 << 14 @ erratum 716044 + mcr p15, 0, r0, c1, c0, 0 @ write system control register + mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register + orr r0, r0, #1 << 4 @ erratum 742230 + orr r0, r0, #1 << 11 @ erratum 751472 + mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register + b after_errata +after_t20_check: +#endif +#ifdef CONFIG_ARCH_TEGRA_3x_SOC +t30_check: + cmp r6, #(0x30 << 8) + bne after_t30_check +t30_errata: + # Tegra30 is a Cortex-A9 r2p9 + mrc p15, 0, r0, c15, c0, 1 @ read diagnostic register + orr r0, r0, #1 << 6 @ erratum 743622 + orr r0, r0, #1 << 11 @ erratum 751472 + mcr p15, 0, r0, c15, c0, 1 @ write diagnostic register + b after_errata +after_t30_check: +#endif +after_errata: mrc p15, 0, r10, c0, c0, 5 @ MPIDR and r10, r10, #0x3 @ R10 = CPU number mov r11, #1 @@ -129,16 +163,13 @@ ENTRY(__tegra_cpu_reset_handler) #ifdef CONFIG_ARCH_TEGRA_2x_SOC /* Are we on Tegra20? */ - mov32 r6, TEGRA_APB_MISC_BASE - ldr r0, [r6, #APB_MISC_GP_HIDREV] - and r0, r0, #0xff00 - cmp r0, #(0x20 << 8) + cmp r6, #(0x20 << 8) bne 1f /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ - mov32 r6, TEGRA_PMC_BASE + mov32 r5, TEGRA_PMC_BASE mov r0, #0 cmp r10, #0 - strne r0, [r6, #PMC_SCRATCH41] + strne r0, [r5, #PMC_SCRATCH41] 1: #endif diff --git a/arch/arm/mach-tegra/sleep-tegra20.S b/arch/arm/mach-tegra/sleep-tegra20.S index 9f6bfafdd512..e3f2417c420e 100644 --- a/arch/arm/mach-tegra/sleep-tegra20.S +++ b/arch/arm/mach-tegra/sleep-tegra20.S @@ -197,7 +197,7 @@ ENTRY(tegra20_sleep_cpu_secondary_finish) mov r3, #CPU_RESETTABLE str r3, [r0] - bl cpu_do_idle + bl tegra_cpu_do_idle /* * cpu may be reset while in wfi, which will return through diff --git a/arch/arm/mach-tegra/sleep-tegra30.S b/arch/arm/mach-tegra/sleep-tegra30.S index 63a15bd9b653..d29dfcce948d 100644 --- a/arch/arm/mach-tegra/sleep-tegra30.S +++ b/arch/arm/mach-tegra/sleep-tegra30.S @@ -66,7 +66,9 @@ ENTRY(tegra30_cpu_shutdown) FLOW_CTRL_CSR_INTR_FLAG | FLOW_CTRL_CSR_EVENT_FLAG | \ FLOW_CTRL_CSR_ENABLE mov r4, #(1 << 4) - orr r12, r12, r4, lsl r3 + ARM( orr r12, r12, r4, lsl r3 ) + THUMB( lsl r4, r4, r3 ) + THUMB( orr r12, r12, r4 ) str r12, [r1] /* Halt this CPU. */ diff --git a/arch/arm/mach-tegra/sleep.h b/arch/arm/mach-tegra/sleep.h index 4ffae541726e..2080fb12ce26 100644 --- a/arch/arm/mach-tegra/sleep.h +++ b/arch/arm/mach-tegra/sleep.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2012, NVIDIA Corporation. All rights reserved. + * Copyright (c) 2010-2013, NVIDIA Corporation. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, @@ -92,7 +92,7 @@ #ifdef CONFIG_CACHE_L2X0 .macro l2_cache_resume, tmp1, tmp2, tmp3, phys_l2x0_saved_regs - adr \tmp1, \phys_l2x0_saved_regs + W(adr) \tmp1, \phys_l2x0_saved_regs ldr \tmp1, [\tmp1] ldr \tmp2, [\tmp1, #L2X0_R_PHY_BASE] ldr \tmp3, [\tmp2, #L2X0_CTRL] @@ -124,11 +124,11 @@ int tegra_sleep_cpu_finish(unsigned long); void tegra_disable_clean_inv_dcache(void); #ifdef CONFIG_HOTPLUG_CPU -void tegra20_hotplug_init(void); -void tegra30_hotplug_init(void); +void tegra20_hotplug_shutdown(void); +void tegra30_hotplug_shutdown(void); +void tegra_hotplug_init(void); #else -static inline void tegra20_hotplug_init(void) {} -static inline void tegra30_hotplug_init(void) {} +static inline void tegra_hotplug_init(void) {} #endif void tegra20_cpu_shutdown(int cpu); diff --git a/arch/arm/mach-tegra/board-dt-tegra20.c b/arch/arm/mach-tegra/tegra.c index a0edf2510280..0d1e4128d460 100644 --- a/arch/arm/mach-tegra/board-dt-tegra20.c +++ b/arch/arm/mach-tegra/tegra.c @@ -1,6 +1,7 @@ /* - * nVidia Tegra device tree board support + * NVIDIA Tegra SoC device tree board support * + * Copyright (C) 2011, 2013, NVIDIA Corporation * Copyright (C) 2010 Secret Lab Technologies, Ltd. * Copyright (C) 2010 Google, Inc. * @@ -30,9 +31,10 @@ #include <linux/pda_power.h> #include <linux/platform_data/tegra_usb.h> #include <linux/io.h> -#include <linux/i2c.h> -#include <linux/i2c-tegra.h> +#include <linux/slab.h> +#include <linux/sys_soc.h> #include <linux/usb/tegra_usb_phy.h> +#include <linux/clk/tegra.h> #include <asm/mach-types.h> #include <asm/mach/arch.h> @@ -41,6 +43,7 @@ #include "board.h" #include "common.h" +#include "fuse.h" #include "iomap.h" static struct tegra_ehci_platform_data tegra_ehci1_pdata = { @@ -79,12 +82,38 @@ static struct of_dev_auxdata tegra20_auxdata_lookup[] __initdata = { static void __init tegra_dt_init(void) { + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device *parent = NULL; + + tegra_clocks_apply_init_table(); + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + goto out; + + soc_dev_attr->family = kasprintf(GFP_KERNEL, "Tegra"); + soc_dev_attr->revision = kasprintf(GFP_KERNEL, "%d", tegra_revision); + soc_dev_attr->soc_id = kasprintf(GFP_KERNEL, "%d", tegra_chip_id); + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) { + kfree(soc_dev_attr->family); + kfree(soc_dev_attr->revision); + kfree(soc_dev_attr->soc_id); + kfree(soc_dev_attr); + goto out; + } + + parent = soc_device_to_device(soc_dev); + /* * Finished with the static registrations now; fill in the missing * devices */ +out: of_platform_populate(NULL, of_default_bus_match_table, - tegra20_auxdata_lookup, NULL); + tegra20_auxdata_lookup, parent); } static void __init trimslice_init(void) @@ -111,7 +140,8 @@ static void __init harmony_init(void) static void __init paz00_init(void) { - tegra_paz00_wifikill_init(); + if (IS_ENABLED(CONFIG_ARCH_TEGRA_2x_SOC)) + tegra_paz00_wifikill_init(); } static struct { @@ -137,19 +167,21 @@ static void __init tegra_dt_init_late(void) } } -static const char *tegra20_dt_board_compat[] = { +static const char * const tegra_dt_board_compat[] = { + "nvidia,tegra114", + "nvidia,tegra30", "nvidia,tegra20", NULL }; -DT_MACHINE_START(TEGRA_DT, "nVidia Tegra20 (Flattened Device Tree)") +DT_MACHINE_START(TEGRA_DT, "NVIDIA Tegra SoC (Flattened Device Tree)") .map_io = tegra_map_common_io, .smp = smp_ops(tegra_smp_ops), - .init_early = tegra20_init_early, + .init_early = tegra_init_early, .init_irq = tegra_dt_init_irq, .init_time = clocksource_of_init, .init_machine = tegra_dt_init, .init_late = tegra_dt_init_late, .restart = tegra_assert_system_reset, - .dt_compat = tegra20_dt_board_compat, + .dt_compat = tegra_dt_board_compat, MACHINE_END diff --git a/arch/arm/mach-tegra/tegra114_speedo.c b/arch/arm/mach-tegra/tegra114_speedo.c new file mode 100644 index 000000000000..5218d4853cd3 --- /dev/null +++ b/arch/arm/mach-tegra/tegra114_speedo.c @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/kernel.h> +#include <linux/bug.h> + +#include "fuse.h" + +#define CORE_PROCESS_CORNERS_NUM 2 +#define CPU_PROCESS_CORNERS_NUM 2 + +enum { + THRESHOLD_INDEX_0, + THRESHOLD_INDEX_1, + THRESHOLD_INDEX_COUNT, +}; + +static const u32 core_process_speedos[][CORE_PROCESS_CORNERS_NUM] = { + {1123, UINT_MAX}, + {0, UINT_MAX}, +}; + +static const u32 cpu_process_speedos[][CPU_PROCESS_CORNERS_NUM] = { + {1695, UINT_MAX}, + {0, UINT_MAX}, +}; + +static void rev_sku_to_speedo_ids(int rev, int sku, int *threshold) +{ + u32 tmp; + + switch (sku) { + case 0x00: + case 0x10: + case 0x05: + case 0x06: + tegra_cpu_speedo_id = 1; + tegra_soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + + case 0x03: + case 0x04: + tegra_cpu_speedo_id = 2; + tegra_soc_speedo_id = 1; + *threshold = THRESHOLD_INDEX_1; + break; + + default: + pr_err("Tegra114 Unknown SKU %d\n", sku); + tegra_cpu_speedo_id = 0; + tegra_soc_speedo_id = 0; + *threshold = THRESHOLD_INDEX_0; + break; + } + + if (rev == TEGRA_REVISION_A01) { + tmp = tegra_fuse_readl(0x270) << 1; + tmp |= tegra_fuse_readl(0x26c); + if (!tmp) + tegra_cpu_speedo_id = 0; + } +} + +void tegra114_init_speedo_data(void) +{ + u32 cpu_speedo_val; + u32 core_speedo_val; + int threshold; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(cpu_process_speedos) != + THRESHOLD_INDEX_COUNT); + BUILD_BUG_ON(ARRAY_SIZE(core_process_speedos) != + THRESHOLD_INDEX_COUNT); + + rev_sku_to_speedo_ids(tegra_revision, tegra_sku_id, &threshold); + + cpu_speedo_val = tegra_fuse_readl(0x12c) + 1024; + core_speedo_val = tegra_fuse_readl(0x134); + + for (i = 0; i < CPU_PROCESS_CORNERS_NUM; i++) + if (cpu_speedo_val < cpu_process_speedos[threshold][i]) + break; + tegra_cpu_process_id = i; + + for (i = 0; i < CORE_PROCESS_CORNERS_NUM; i++) + if (core_speedo_val < core_process_speedos[threshold][i]) + break; + tegra_core_process_id = i; +} diff --git a/arch/arm/mach-tegra/tegra2_emc.c b/arch/arm/mach-tegra/tegra2_emc.c index ce7ce42a1ac9..9e8bdfa2b369 100644 --- a/arch/arm/mach-tegra/tegra2_emc.c +++ b/arch/arm/mach-tegra/tegra2_emc.c @@ -276,7 +276,7 @@ static struct tegra_emc_pdata *tegra_emc_fill_pdata(struct platform_device *pdev int i; WARN_ON(pdev->dev.platform_data); - BUG_ON(IS_ERR_OR_NULL(c)); + BUG_ON(IS_ERR(c)); pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); pdata->tables = devm_kzalloc(&pdev->dev, sizeof(*pdata->tables), |