Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: tegra: Remove pen-locking from cpuidle-tegra20

Pen-locking is meant to block CPU0 if CPU1 wakes up during of entering
into LP2 because of some interrupt firing up, preventing unnecessary LP2
enter that will be resumed immediately. Apparently this case doesn't
happen often in practice, I checked how often it takes place and found
that after ~20 hours of browsing web, managing email, watching videos and
idling (15+ hours) there is only a dozen of early LP2 entering abortions
and they all happened while device was idling. Thus let's remove the
pen-locking and make LP2 entering uninterruptible, simplifying code quite
a lot. This will also become very handy for the upcoming unified cpuidle
driver, allowing to have a common LP2 code-path across of different
hardware generations.

Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com>
Tested-by: Peter Geis <pgwipeout@gmail.com>
Tested-by: Jasper Korten <jja2000@gmail.com>
Tested-by: David Heidelberg <david@ixit.cz>
Tested-by: Nicolas Chauvet <kwizart@gmail.com>
Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: Dmitry Osipenko <digetx@gmail.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>

authored by

Dmitry Osipenko and committed by
Thierry Reding
d90bdb72 859a6f6e

+4 -251
+2 -43
arch/arm/mach-tegra/cpuidle-tegra20.c
··· 65 65 66 66 #ifdef CONFIG_PM_SLEEP 67 67 #ifdef CONFIG_SMP 68 - static int tegra20_reset_sleeping_cpu_1(void) 69 - { 70 - int ret = 0; 71 - 72 - tegra_pen_lock(); 73 - 74 - if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE) 75 - tegra20_cpu_shutdown(1); 76 - else 77 - ret = -EINVAL; 78 - 79 - tegra_pen_unlock(); 80 - 81 - return ret; 82 - } 83 - 84 68 static void tegra20_wake_cpu1_from_reset(void) 85 69 { 86 - tegra_pen_lock(); 87 - 88 - tegra20_cpu_clear_resettable(); 89 - 90 70 /* enable cpu clock on cpu */ 91 71 tegra_enable_cpu_clock(1); 92 72 ··· 75 95 76 96 /* unhalt the cpu */ 77 97 flowctrl_write_cpu_halt(1, 0); 78 - 79 - tegra_pen_unlock(); 80 - } 81 - 82 - static int tegra20_reset_cpu_1(void) 83 - { 84 - if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1()) 85 - return 0; 86 - 87 - tegra20_wake_cpu1_from_reset(); 88 - return -EBUSY; 89 98 } 90 99 #else 91 100 static inline void tegra20_wake_cpu1_from_reset(void) 92 101 { 93 - } 94 - 95 - static inline int tegra20_reset_cpu_1(void) 96 - { 97 - return 0; 98 102 } 99 103 #endif 100 104 ··· 86 122 struct cpuidle_driver *drv, 87 123 int index) 88 124 { 89 - while (tegra20_cpu_is_resettable_soon()) 125 + while (!tegra_cpu_rail_off_ready()) 90 126 cpu_relax(); 91 - 92 - if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready()) 93 - return false; 94 127 95 128 tegra_idle_lp2_last(); 96 129 ··· 102 141 struct cpuidle_driver *drv, 103 142 int index) 104 143 { 105 - cpu_suspend(0, tegra20_sleep_cpu_secondary_finish); 106 - 107 - tegra20_cpu_clear_resettable(); 144 + cpu_suspend(dev->cpu, tegra_pm_park_secondary_cpu); 108 145 109 146 return true; 110 147 }
-7
arch/arm/mach-tegra/pm.c
··· 137 137 138 138 if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask)) 139 139 last_cpu = true; 140 - else if (tegra_get_chip_id() == TEGRA20 && phy_cpu_id == 1) 141 - tegra20_cpu_set_resettable_soon(); 142 140 143 141 spin_unlock(&tegra_lp2_lock); 144 142 return last_cpu; 145 - } 146 - 147 - int tegra_cpu_do_idle(void) 148 - { 149 - return cpu_do_idle(); 150 143 } 151 144 152 145 static int tegra_sleep_cpu(unsigned long v2p)
-1
arch/arm/mach-tegra/pm.h
··· 25 25 26 26 void tegra_clear_cpu_in_lp2(void); 27 27 bool tegra_set_cpu_in_lp2(void); 28 - int tegra_cpu_do_idle(void); 29 28 void tegra_idle_lp2_last(void); 30 29 extern void (*tegra_tear_down_cpu)(void); 31 30
-11
arch/arm/mach-tegra/reset-handler.S
··· 183 183 bleq __die @ CPU not present (to OS) 184 184 #endif 185 185 186 - #ifdef CONFIG_ARCH_TEGRA_2x_SOC 187 - /* Are we on Tegra20? */ 188 - cmp r6, #TEGRA20 189 - bne 1f 190 - /* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */ 191 - mov r0, #CPU_NOT_RESETTABLE 192 - cmp r10, #0 193 - strbne r0, [r12, #RESET_DATA(RESETTABLE_STATUS)] 194 - 1: 195 - #endif 196 - 197 186 /* Waking up from LP1? */ 198 187 ldr r8, [r12, #RESET_DATA(MASK_LP1)] 199 188 tst r8, r11 @ if in_lp1
+2 -7
arch/arm/mach-tegra/reset.h
··· 16 16 #define TEGRA_RESET_STARTUP_SECONDARY 3 17 17 #define TEGRA_RESET_STARTUP_LP2 4 18 18 #define TEGRA_RESET_STARTUP_LP1 5 19 - #define TEGRA_RESET_RESETTABLE_STATUS 6 20 - #define TEGRA_RESET_TF_PRESENT 7 21 - #define TEGRA_RESET_DATA_SIZE 8 19 + #define TEGRA_RESET_TF_PRESENT 6 20 + #define TEGRA_RESET_DATA_SIZE 7 22 21 23 22 #define RESET_DATA(x) ((TEGRA_RESET_##x)*4) 24 23 ··· 40 41 #define tegra_cpu_lp2_mask \ 41 42 (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ 42 43 ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \ 43 - (u32)__tegra_cpu_reset_handler_start))) 44 - #define tegra20_cpu1_resettable_status \ 45 - (IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \ 46 - ((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_RESETTABLE_STATUS] - \ 47 44 (u32)__tegra_cpu_reset_handler_start))) 48 45 #endif 49 46
-170
arch/arm/mach-tegra/sleep-tegra20.S
··· 43 43 #define APB_MISC_XM2CFGCPADCTRL2 0x8e4 44 44 #define APB_MISC_XM2CFGDPADCTRL2 0x8e8 45 45 46 - #define __tegra20_cpu1_resettable_status_offset \ 47 - (__tegra_cpu_reset_handler_data_offset + RESET_DATA(RESETTABLE_STATUS)) 48 - 49 46 .macro pll_enable, rd, r_car_base, pll_base 50 47 ldr \rd, [\r_car_base, #\pll_base] 51 48 tst \rd, #(1 << 30) ··· 87 90 ENTRY(tegra20_cpu_shutdown) 88 91 cmp r0, #0 89 92 reteq lr @ must not be called for CPU 0 90 - mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT 91 - ldr r2, =__tegra20_cpu1_resettable_status_offset 92 - mov r12, #CPU_RESETTABLE 93 - strb r12, [r1, r2] 94 93 95 94 cpu_to_halt_reg r1, r0 96 95 ldr r3, =TEGRA_FLOW_CTRL_VIRT ··· 109 116 #endif 110 117 111 118 #ifdef CONFIG_PM_SLEEP 112 - /* 113 - * tegra_pen_lock 114 - * 115 - * spinlock implementation with no atomic test-and-set and no coherence 116 - * using Peterson's algorithm on strongly-ordered registers 117 - * used to synchronize a cpu waking up from wfi with entering lp2 on idle 118 - * 119 - * The reference link of Peterson's algorithm: 120 - * http://en.wikipedia.org/wiki/Peterson's_algorithm 121 - * 122 - * SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm) 123 - * on cpu 0: 124 - * r2 = flag[0] (in SCRATCH38) 125 - * r3 = flag[1] (in SCRATCH39) 126 - * on cpu1: 127 - * r2 = flag[1] (in SCRATCH39) 128 - * r3 = flag[0] (in SCRATCH38) 129 - * 130 - * must be called with MMU on 131 - * corrupts r0-r3, r12 132 - */ 133 - ENTRY(tegra_pen_lock) 134 - mov32 r3, TEGRA_PMC_VIRT 135 - cpu_id r0 136 - add r1, r3, #PMC_SCRATCH37 137 - cmp r0, #0 138 - addeq r2, r3, #PMC_SCRATCH38 139 - addeq r3, r3, #PMC_SCRATCH39 140 - addne r2, r3, #PMC_SCRATCH39 141 - addne r3, r3, #PMC_SCRATCH38 142 - 143 - mov r12, #1 144 - str r12, [r2] @ flag[cpu] = 1 145 - dsb 146 - str r12, [r1] @ !turn = cpu 147 - 1: dsb 148 - ldr r12, [r3] 149 - cmp r12, #1 @ flag[!cpu] == 1? 150 - ldreq r12, [r1] 151 - cmpeq r12, r0 @ !turn == cpu? 152 - beq 1b @ while !turn == cpu && flag[!cpu] == 1 153 - 154 - ret lr @ locked 155 - ENDPROC(tegra_pen_lock) 156 - 157 - ENTRY(tegra_pen_unlock) 158 - dsb 159 - mov32 r3, TEGRA_PMC_VIRT 160 - cpu_id r0 161 - cmp r0, #0 162 - addeq r2, r3, #PMC_SCRATCH38 163 - addne r2, r3, #PMC_SCRATCH39 164 - mov r12, #0 165 - str r12, [r2] 166 - ret lr 167 - ENDPROC(tegra_pen_unlock) 168 - 169 - /* 170 - * tegra20_cpu_clear_resettable(void) 171 - * 172 - * Called to clear the "resettable soon" flag in IRAM variable when 173 - * it is expected that the secondary CPU will be idle soon. 174 - */ 175 - ENTRY(tegra20_cpu_clear_resettable) 176 - mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT 177 - ldr r2, =__tegra20_cpu1_resettable_status_offset 178 - mov r12, #CPU_NOT_RESETTABLE 179 - strb r12, [r1, r2] 180 - ret lr 181 - ENDPROC(tegra20_cpu_clear_resettable) 182 - 183 - /* 184 - * tegra20_cpu_set_resettable_soon(void) 185 - * 186 - * Called to set the "resettable soon" flag in IRAM variable when 187 - * it is expected that the secondary CPU will be idle soon. 188 - */ 189 - ENTRY(tegra20_cpu_set_resettable_soon) 190 - mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT 191 - ldr r2, =__tegra20_cpu1_resettable_status_offset 192 - mov r12, #CPU_RESETTABLE_SOON 193 - strb r12, [r1, r2] 194 - ret lr 195 - ENDPROC(tegra20_cpu_set_resettable_soon) 196 - 197 - /* 198 - * tegra20_cpu_is_resettable_soon(void) 199 - * 200 - * Returns true if the "resettable soon" flag in IRAM variable has been 201 - * set because it is expected that the secondary CPU will be idle soon. 202 - */ 203 - ENTRY(tegra20_cpu_is_resettable_soon) 204 - mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT 205 - ldr r2, =__tegra20_cpu1_resettable_status_offset 206 - ldrb r12, [r1, r2] 207 - cmp r12, #CPU_RESETTABLE_SOON 208 - moveq r0, #1 209 - movne r0, #0 210 - ret lr 211 - ENDPROC(tegra20_cpu_is_resettable_soon) 212 - 213 119 /* 214 120 * tegra20_sleep_core_finish(unsigned long v2p) 215 121 * ··· 133 241 134 242 ret r3 135 243 ENDPROC(tegra20_sleep_core_finish) 136 - 137 - /* 138 - * tegra20_sleep_cpu_secondary_finish(unsigned long v2p) 139 - * 140 - * Enters WFI on secondary CPU by exiting coherency. 141 - */ 142 - ENTRY(tegra20_sleep_cpu_secondary_finish) 143 - stmfd sp!, {r4-r11, lr} 144 - 145 - mrc p15, 0, r11, c1, c0, 1 @ save actlr before exiting coherency 146 - 147 - /* Flush and disable the L1 data cache */ 148 - mov r0, #TEGRA_FLUSH_CACHE_LOUIS 149 - bl tegra_disable_clean_inv_dcache 150 - 151 - mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT 152 - ldr r4, =__tegra20_cpu1_resettable_status_offset 153 - mov r3, #CPU_RESETTABLE 154 - strb r3, [r0, r4] 155 - 156 - bl tegra_cpu_do_idle 157 - 158 - /* 159 - * cpu may be reset while in wfi, which will return through 160 - * tegra_resume to cpu_resume 161 - * or interrupt may wake wfi, which will return here 162 - * cpu state is unchanged - MMU is on, cache is on, coherency 163 - * is off, and the data cache is off 164 - * 165 - * r11 contains the original actlr 166 - */ 167 - 168 - bl tegra_pen_lock 169 - 170 - mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT 171 - ldr r4, =__tegra20_cpu1_resettable_status_offset 172 - mov r3, #CPU_NOT_RESETTABLE 173 - strb r3, [r0, r4] 174 - 175 - bl tegra_pen_unlock 176 - 177 - /* Re-enable the data cache */ 178 - mrc p15, 0, r10, c1, c0, 0 179 - orr r10, r10, #CR_C 180 - mcr p15, 0, r10, c1, c0, 0 181 - isb 182 - 183 - mcr p15, 0, r11, c1, c0, 1 @ reenable coherency 184 - 185 - /* Invalidate the TLBs & BTAC */ 186 - mov r1, #0 187 - mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs 188 - mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC 189 - dsb 190 - isb 191 - 192 - /* the cpu was running with coherency disabled, 193 - * caches may be out of date */ 194 - bl v7_flush_kern_cache_louis 195 - 196 - ldmfd sp!, {r4 - r11, pc} 197 - ENDPROC(tegra20_sleep_cpu_secondary_finish) 198 244 199 245 /* 200 246 * tegra20_tear_down_cpu
-12
arch/arm/mach-tegra/sleep.h
··· 114 114 .endm 115 115 116 116 #else 117 - void tegra_pen_lock(void); 118 - void tegra_pen_unlock(void); 119 117 void tegra_resume(void); 120 118 int tegra_sleep_cpu_finish(unsigned long); 121 119 void tegra_disable_clean_inv_dcache(u32 flag); ··· 121 123 void tegra20_hotplug_shutdown(void); 122 124 void tegra30_hotplug_shutdown(void); 123 125 124 - void tegra20_cpu_shutdown(int cpu); 125 - int tegra20_cpu_is_resettable_soon(void); 126 - void tegra20_cpu_clear_resettable(void); 127 - #ifdef CONFIG_ARCH_TEGRA_2x_SOC 128 - void tegra20_cpu_set_resettable_soon(void); 129 - #else 130 - static inline void tegra20_cpu_set_resettable_soon(void) {} 131 - #endif 132 - 133 - int tegra20_sleep_cpu_secondary_finish(unsigned long); 134 126 void tegra20_tear_down_cpu(void); 135 127 int tegra30_sleep_cpu_secondary_finish(unsigned long); 136 128 void tegra30_tear_down_cpu(void);