diff options
author | Scott Williams <scwilliams@nvidia.com> | 2011-02-02 10:32:42 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-04-26 15:51:40 -0700 |
commit | 7df947103b10608106d2b10fbc951af68926438e (patch) | |
tree | 0d9e914ae54859f4ebb590a26b91d74adcadbf64 /arch/arm/mach-tegra/suspend.c | |
parent | 4c609cbf4d1f0cf1a3f78c72695e3168b6088a4a (diff) |
arm: tegra: Redesign Tegra CPU reset handling
- Add a single unified handler for all CPU resets.
- Don't write boot confirmation notification to the reset vector.
- Write the EVP CPU reset vector only once per cold/warm boot session.
- Don't allow Tegra3 LP2 until all CPUs have booted.
- Don't restart online secondary CPUs that are also in LP2 state
when restarting CPU0 for Tegra3.
- Prevent the compiler from rearranging order-sensitive register
writes in boot_secondary().
- Fix incorrect return status in tegra_powergate_is_powered().
- In LP2 entry code, if a WFI request fails, retry a limited number
of times.
- Eliminate duplicate macro definitions.
- Improve commentary in assembly functions.
Bug 786290
Bug 790458
Original-Change-Id: I7582112938aa80303d1b8b1d1948d278ca662043
Reviewed-on: http://git-master/r/18091
Tested-by: Scott Williams <scwilliams@nvidia.com>
Reviewed-by: Narendra Damahe <ndamahe@nvidia.com>
Tested-by: Jin Qian <jqian@nvidia.com>
Reviewed-by: Aleksandr Frid <afrid@nvidia.com>
Tested-by: Aleksandr Frid <afrid@nvidia.com>
Reviewed-by: Scott Williams <scwilliams@nvidia.com>
Change-Id: I56a686e2e9fc00e61e97eec4fbf5a49944ffa77c
Diffstat (limited to 'arch/arm/mach-tegra/suspend.c')
-rw-r--r-- | arch/arm/mach-tegra/suspend.c | 60 |
1 files changed, 44 insertions, 16 deletions
diff --git a/arch/arm/mach-tegra/suspend.c b/arch/arm/mach-tegra/suspend.c index 8e289119c49b..2443ad99e9ea 100644 --- a/arch/arm/mach-tegra/suspend.c +++ b/arch/arm/mach-tegra/suspend.c @@ -3,7 +3,7 @@ * * CPU complex suspend & resume functions for Tegra SoCs * - * Copyright (c) 2009-2010, NVIDIA Corporation. + * Copyright (c) 2009-2011, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ */ #include <linux/kernel.h> +#include <linux/cpumask.h> #include <linux/init.h> #include <linux/io.h> #include <linux/sched.h> @@ -31,6 +32,7 @@ #include <linux/err.h> #include <linux/debugfs.h> #include <linux/delay.h> +#include <linux/ratelimit.h> #include <linux/suspend.h> #include <linux/slab.h> #include <linux/serial_reg.h> @@ -56,6 +58,7 @@ #include "board.h" #include "clock.h" #include "power.h" +#include "reset.h" #ifdef CONFIG_TRUSTED_FOUNDATIONS void callGenericSMC(u32 param0, u32 param1, u32 param2) @@ -103,13 +106,13 @@ volatile struct suspend_context tegra_sctx; #if defined(CONFIG_PM) || defined(CONFIG_CPU_IDLE) || !defined(CONFIG_ARCH_TEGRA_2x_SOC) static void __iomem *clk_rst = IO_ADDRESS(TEGRA_CLK_RESET_BASE); -static void __iomem *evp_reset = IO_ADDRESS(TEGRA_EXCEPTION_VECTORS_BASE)+0x100; static void __iomem *tmrus = IO_ADDRESS(TEGRA_TMRUS_BASE); static void __iomem *pmc = IO_ADDRESS(TEGRA_PMC_BASE); #endif #ifdef CONFIG_PM static unsigned long wb0_restore = 0; #endif +unsigned long tegra_wfi_fail_count[CONFIG_NR_CPUS]; #define PMC_CTRL 0x0 #define PMC_CTRL_LATCH_WAKEUPS (1 << 5) @@ -144,7 +147,6 @@ static unsigned long wb0_restore = 0; #define CLK_RESET_SOURCE_CSITE 0x1d4 - #define CLK_RESET_CCLK_BURST_POLICY_SHIFT 28 #define CLK_RESET_CCLK_BURST_POLICY_PLLM 3 #define CLK_RESET_CCLK_BURST_POLICY_PLLX 8 @@ -257,6 +259,9 @@ static int create_suspend_pgtable(void) { int i; pmd_t *pmd; + void __tegra_cpu_reset_handler_start(void); + void __put_cpu_in_reset(void); + /* arrays of virtual-to-physical mappings which must be * present to safely boot hotplugged / LP2-idled CPUs. * tegra_hotplug_startup (hotplug reset vector) is mapped @@ -272,6 +277,8 @@ static int create_suspend_pgtable(void) #endif (unsigned long)__cortex_a9_restore, (unsigned long)virt_to_phys(__shut_off_mmu), + (unsigned long)virt_to_phys(__tegra_cpu_reset_handler_start), + (unsigned long)virt_to_phys(__put_cpu_in_reset), }; unsigned long addr_p[] = { PHYS_OFFSET, @@ -282,6 +289,8 @@ static int create_suspend_pgtable(void) #endif (unsigned long)virt_to_phys(__cortex_a9_restore), (unsigned long)virt_to_phys(__shut_off_mmu), + (unsigned long)virt_to_phys(__tegra_cpu_reset_handler_start), + (unsigned long)virt_to_phys(__put_cpu_in_reset), }; unsigned int flags = PMD_TYPE_SECT | PMD_SECT_AP_WRITE | PMD_SECT_WBWA | PMD_SECT_S; @@ -312,8 +321,6 @@ static int create_suspend_pgtable(void) return 0; } - - #if defined(CONFIG_PM) || defined(CONFIG_CPU_IDLE) || !defined(CONFIG_ARCH_TEGRA_2x_SOC) /* * suspend_cpu_complex @@ -426,8 +433,9 @@ static noinline void suspend_cpu_complex(void) unsigned int tegra_suspend_lp2(unsigned int us, unsigned int flags) { unsigned int mode; - unsigned long orig, reg; + unsigned long reg; unsigned int remain; + unsigned int cpu = hard_smp_processor_id(); reg = readl(pmc + PMC_CTRL); mode = (reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK; @@ -439,11 +447,9 @@ unsigned int tegra_suspend_lp2(unsigned int us, unsigned int flags) mode &= ~TEGRA_POWER_PWRREQ_OE; mode &= ~TEGRA_POWER_EFFECT_LP0; + /* pr_info_ratelimited("CPU %d entering LP2\n", cpu); */ tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_start); - orig = readl(evp_reset); - writel(virt_to_phys(tegra_lp2_startup), evp_reset); - set_power_timers(pdata->cpu_timer, pdata->cpu_off_timer, clk_get_rate_all_locked(tegra_pclk)); @@ -456,10 +462,12 @@ unsigned int tegra_suspend_lp2(unsigned int us, unsigned int flags) suspend_cpu_complex(); stop_critical_timings(); tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_prolog); + cpu_set(cpu, tegra_cpu_lp2_map); flush_cache_all(); - /* structure is written by reset code, so the L2 lines + /* structure is read by reset code, so the L2 lines * must be invalidated */ outer_flush_range(__pa(&tegra_sctx),__pa(&tegra_sctx+1)); + tegra_cpu_reset_handler_flush(false); barrier(); #ifdef CONFIG_TRUSTED_FOUNDATIONS @@ -471,9 +479,15 @@ unsigned int tegra_suspend_lp2(unsigned int us, unsigned int flags) /* return from __cortex_a9_restore */ barrier(); tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_switch); + if (suspend_wfi_failed()) { + tegra_wfi_fail_count[cpu]++; + pr_err_ratelimited("WFI for LP2 failed for CPU %d: count %lu\n", + cpu, tegra_wfi_fail_count[cpu]); + } + restore_cpu_complex(); start_critical_timings(); - + cpu_clear(cpu, tegra_cpu_lp2_map); remain = tegra_lp2_timer_remain(); if (us) tegra_lp2_set_trigger(0); @@ -481,8 +495,8 @@ unsigned int tegra_suspend_lp2(unsigned int us, unsigned int flags) if (flags & TEGRA_POWER_CLUSTER_MASK) tegra_cluster_switch_epilog(mode); - writel(orig, evp_reset); tegra_cluster_switch_time(flags, tegra_cluster_switch_time_id_epilog); + /* pr_info_ratelimited("CPU %d return from LP2\n", cpu); */ #if INSTRUMENT_CLUSTER_SWITCH if (flags & TEGRA_POWER_CLUSTER_MASK) { @@ -519,9 +533,9 @@ static void __iomem *iram_code = IO_ADDRESS(TEGRA_IRAM_CODE_AREA); void tegra_suspend_dram(bool do_lp0) { unsigned int mode = TEGRA_POWER_SDRAM_SELFREFRESH; - unsigned long orig, reg; + unsigned long reg; + unsigned int cpu; - orig = readl(evp_reset); /* copy the reset vector and SDRAM shutdown code into IRAM */ memcpy(iram_save, iram_code, iram_save_size); memcpy(iram_code, (void *)__tegra_lp1_reset, iram_save_size); @@ -532,7 +546,7 @@ void tegra_suspend_dram(bool do_lp0) mode |= ((reg >> TEGRA_POWER_PMC_SHIFT) & TEGRA_POWER_PMC_MASK); if (!do_lp0) { - writel(TEGRA_IRAM_CODE_AREA, evp_reset); + cpu = hard_smp_processor_id(); mode |= TEGRA_POWER_CPU_PWRREQ_OE; if (pdata->separate_req) @@ -567,7 +581,10 @@ void tegra_suspend_dram(bool do_lp0) } suspend_cpu_complex(); + if (!do_lp0) + cpu_set(cpu, tegra_cpu_lp1_map); flush_cache_all(); + tegra_cpu_reset_handler_flush(false); #ifdef CONFIG_CACHE_L2X0 l2x0_shutdown(); #endif @@ -577,9 +594,20 @@ void tegra_suspend_dram(bool do_lp0) callGenericSMC(0xFFFFFFFC, 0xFFFFFFE3, virt_to_phys(buffer_rdv)); #endif __cortex_a9_save(mode); + + if (!do_lp0) { + cpu_clear(cpu, tegra_cpu_lp1_map); + if (suspend_wfi_failed()) { + tegra_wfi_fail_count[cpu]++; + pr_err_ratelimited("WFI for LP1 failed for CPU %d: count %lu\n", + cpu, tegra_wfi_fail_count[cpu]); + } + } + else + tegra_cpu_reset_handler_enable(); + restore_cpu_complex(); - writel(orig, evp_reset); #ifdef CONFIG_CACHE_L2X0 l2x0_restart(); #endif |