mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 17:23:36 +00:00
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>
This commit is contained in:
parent
859a6f6ee1
commit
d90bdb72bb
@ -65,28 +65,8 @@ static struct cpuidle_driver tegra_idle_driver = {
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
#ifdef CONFIG_SMP
|
||||
static int tegra20_reset_sleeping_cpu_1(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
tegra_pen_lock();
|
||||
|
||||
if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE)
|
||||
tegra20_cpu_shutdown(1);
|
||||
else
|
||||
ret = -EINVAL;
|
||||
|
||||
tegra_pen_unlock();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void tegra20_wake_cpu1_from_reset(void)
|
||||
{
|
||||
tegra_pen_lock();
|
||||
|
||||
tegra20_cpu_clear_resettable();
|
||||
|
||||
/* enable cpu clock on cpu */
|
||||
tegra_enable_cpu_clock(1);
|
||||
|
||||
@ -95,39 +75,20 @@ static void tegra20_wake_cpu1_from_reset(void)
|
||||
|
||||
/* unhalt the cpu */
|
||||
flowctrl_write_cpu_halt(1, 0);
|
||||
|
||||
tegra_pen_unlock();
|
||||
}
|
||||
|
||||
static int tegra20_reset_cpu_1(void)
|
||||
{
|
||||
if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
|
||||
return 0;
|
||||
|
||||
tegra20_wake_cpu1_from_reset();
|
||||
return -EBUSY;
|
||||
}
|
||||
#else
|
||||
static inline void tegra20_wake_cpu1_from_reset(void)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int tegra20_reset_cpu_1(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
while (tegra20_cpu_is_resettable_soon())
|
||||
while (!tegra_cpu_rail_off_ready())
|
||||
cpu_relax();
|
||||
|
||||
if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
|
||||
return false;
|
||||
|
||||
tegra_idle_lp2_last();
|
||||
|
||||
if (cpu_online(1))
|
||||
@ -141,9 +102,7 @@ static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
|
||||
struct cpuidle_driver *drv,
|
||||
int index)
|
||||
{
|
||||
cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
|
||||
|
||||
tegra20_cpu_clear_resettable();
|
||||
cpu_suspend(dev->cpu, tegra_pm_park_secondary_cpu);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -137,18 +137,11 @@ bool tegra_set_cpu_in_lp2(void)
|
||||
|
||||
if ((phy_cpu_id == 0) && cpumask_equal(cpu_lp2_mask, cpu_online_mask))
|
||||
last_cpu = true;
|
||||
else if (tegra_get_chip_id() == TEGRA20 && phy_cpu_id == 1)
|
||||
tegra20_cpu_set_resettable_soon();
|
||||
|
||||
spin_unlock(&tegra_lp2_lock);
|
||||
return last_cpu;
|
||||
}
|
||||
|
||||
int tegra_cpu_do_idle(void)
|
||||
{
|
||||
return cpu_do_idle();
|
||||
}
|
||||
|
||||
static int tegra_sleep_cpu(unsigned long v2p)
|
||||
{
|
||||
/*
|
||||
|
@ -25,7 +25,6 @@ void tegra30_sleep_core_init(void);
|
||||
|
||||
void tegra_clear_cpu_in_lp2(void);
|
||||
bool tegra_set_cpu_in_lp2(void);
|
||||
int tegra_cpu_do_idle(void);
|
||||
void tegra_idle_lp2_last(void);
|
||||
extern void (*tegra_tear_down_cpu)(void);
|
||||
|
||||
|
@ -183,17 +183,6 @@ after_errata:
|
||||
bleq __die @ CPU not present (to OS)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
/* Are we on Tegra20? */
|
||||
cmp r6, #TEGRA20
|
||||
bne 1f
|
||||
/* If not CPU0, don't let CPU0 reset CPU1 now that CPU1 is coming up. */
|
||||
mov r0, #CPU_NOT_RESETTABLE
|
||||
cmp r10, #0
|
||||
strbne r0, [r12, #RESET_DATA(RESETTABLE_STATUS)]
|
||||
1:
|
||||
#endif
|
||||
|
||||
/* Waking up from LP1? */
|
||||
ldr r8, [r12, #RESET_DATA(MASK_LP1)]
|
||||
tst r8, r11 @ if in_lp1
|
||||
|
@ -16,9 +16,8 @@
|
||||
#define TEGRA_RESET_STARTUP_SECONDARY 3
|
||||
#define TEGRA_RESET_STARTUP_LP2 4
|
||||
#define TEGRA_RESET_STARTUP_LP1 5
|
||||
#define TEGRA_RESET_RESETTABLE_STATUS 6
|
||||
#define TEGRA_RESET_TF_PRESENT 7
|
||||
#define TEGRA_RESET_DATA_SIZE 8
|
||||
#define TEGRA_RESET_TF_PRESENT 6
|
||||
#define TEGRA_RESET_DATA_SIZE 7
|
||||
|
||||
#define RESET_DATA(x) ((TEGRA_RESET_##x)*4)
|
||||
|
||||
@ -42,10 +41,6 @@ void __tegra_cpu_reset_handler_end(void);
|
||||
(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
|
||||
((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_MASK_LP2] - \
|
||||
(u32)__tegra_cpu_reset_handler_start)))
|
||||
#define tegra20_cpu1_resettable_status \
|
||||
(IO_ADDRESS(TEGRA_IRAM_BASE + TEGRA_IRAM_RESET_HANDLER_OFFSET + \
|
||||
((u32)&__tegra_cpu_reset_handler_data[TEGRA_RESET_RESETTABLE_STATUS] - \
|
||||
(u32)__tegra_cpu_reset_handler_start)))
|
||||
#endif
|
||||
|
||||
#define tegra_cpu_reset_handler_offset \
|
||||
|
@ -43,9 +43,6 @@
|
||||
#define APB_MISC_XM2CFGCPADCTRL2 0x8e4
|
||||
#define APB_MISC_XM2CFGDPADCTRL2 0x8e8
|
||||
|
||||
#define __tegra20_cpu1_resettable_status_offset \
|
||||
(__tegra_cpu_reset_handler_data_offset + RESET_DATA(RESETTABLE_STATUS))
|
||||
|
||||
.macro pll_enable, rd, r_car_base, pll_base
|
||||
ldr \rd, [\r_car_base, #\pll_base]
|
||||
tst \rd, #(1 << 30)
|
||||
@ -90,10 +87,6 @@ ENDPROC(tegra20_hotplug_shutdown)
|
||||
ENTRY(tegra20_cpu_shutdown)
|
||||
cmp r0, #0
|
||||
reteq lr @ must not be called for CPU 0
|
||||
mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
|
||||
ldr r2, =__tegra20_cpu1_resettable_status_offset
|
||||
mov r12, #CPU_RESETTABLE
|
||||
strb r12, [r1, r2]
|
||||
|
||||
cpu_to_halt_reg r1, r0
|
||||
ldr r3, =TEGRA_FLOW_CTRL_VIRT
|
||||
@ -116,107 +109,6 @@ ENDPROC(tegra20_cpu_shutdown)
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
/*
|
||||
* tegra_pen_lock
|
||||
*
|
||||
* spinlock implementation with no atomic test-and-set and no coherence
|
||||
* using Peterson's algorithm on strongly-ordered registers
|
||||
* used to synchronize a cpu waking up from wfi with entering lp2 on idle
|
||||
*
|
||||
* The reference link of Peterson's algorithm:
|
||||
* http://en.wikipedia.org/wiki/Peterson's_algorithm
|
||||
*
|
||||
* SCRATCH37 = r1 = !turn (inverted from Peterson's algorithm)
|
||||
* on cpu 0:
|
||||
* r2 = flag[0] (in SCRATCH38)
|
||||
* r3 = flag[1] (in SCRATCH39)
|
||||
* on cpu1:
|
||||
* r2 = flag[1] (in SCRATCH39)
|
||||
* r3 = flag[0] (in SCRATCH38)
|
||||
*
|
||||
* must be called with MMU on
|
||||
* corrupts r0-r3, r12
|
||||
*/
|
||||
ENTRY(tegra_pen_lock)
|
||||
mov32 r3, TEGRA_PMC_VIRT
|
||||
cpu_id r0
|
||||
add r1, r3, #PMC_SCRATCH37
|
||||
cmp r0, #0
|
||||
addeq r2, r3, #PMC_SCRATCH38
|
||||
addeq r3, r3, #PMC_SCRATCH39
|
||||
addne r2, r3, #PMC_SCRATCH39
|
||||
addne r3, r3, #PMC_SCRATCH38
|
||||
|
||||
mov r12, #1
|
||||
str r12, [r2] @ flag[cpu] = 1
|
||||
dsb
|
||||
str r12, [r1] @ !turn = cpu
|
||||
1: dsb
|
||||
ldr r12, [r3]
|
||||
cmp r12, #1 @ flag[!cpu] == 1?
|
||||
ldreq r12, [r1]
|
||||
cmpeq r12, r0 @ !turn == cpu?
|
||||
beq 1b @ while !turn == cpu && flag[!cpu] == 1
|
||||
|
||||
ret lr @ locked
|
||||
ENDPROC(tegra_pen_lock)
|
||||
|
||||
ENTRY(tegra_pen_unlock)
|
||||
dsb
|
||||
mov32 r3, TEGRA_PMC_VIRT
|
||||
cpu_id r0
|
||||
cmp r0, #0
|
||||
addeq r2, r3, #PMC_SCRATCH38
|
||||
addne r2, r3, #PMC_SCRATCH39
|
||||
mov r12, #0
|
||||
str r12, [r2]
|
||||
ret lr
|
||||
ENDPROC(tegra_pen_unlock)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_clear_resettable(void)
|
||||
*
|
||||
* Called to clear the "resettable soon" flag in IRAM variable when
|
||||
* it is expected that the secondary CPU will be idle soon.
|
||||
*/
|
||||
ENTRY(tegra20_cpu_clear_resettable)
|
||||
mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
|
||||
ldr r2, =__tegra20_cpu1_resettable_status_offset
|
||||
mov r12, #CPU_NOT_RESETTABLE
|
||||
strb r12, [r1, r2]
|
||||
ret lr
|
||||
ENDPROC(tegra20_cpu_clear_resettable)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_set_resettable_soon(void)
|
||||
*
|
||||
* Called to set the "resettable soon" flag in IRAM variable when
|
||||
* it is expected that the secondary CPU will be idle soon.
|
||||
*/
|
||||
ENTRY(tegra20_cpu_set_resettable_soon)
|
||||
mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
|
||||
ldr r2, =__tegra20_cpu1_resettable_status_offset
|
||||
mov r12, #CPU_RESETTABLE_SOON
|
||||
strb r12, [r1, r2]
|
||||
ret lr
|
||||
ENDPROC(tegra20_cpu_set_resettable_soon)
|
||||
|
||||
/*
|
||||
* tegra20_cpu_is_resettable_soon(void)
|
||||
*
|
||||
* Returns true if the "resettable soon" flag in IRAM variable has been
|
||||
* set because it is expected that the secondary CPU will be idle soon.
|
||||
*/
|
||||
ENTRY(tegra20_cpu_is_resettable_soon)
|
||||
mov32 r1, TEGRA_IRAM_RESET_BASE_VIRT
|
||||
ldr r2, =__tegra20_cpu1_resettable_status_offset
|
||||
ldrb r12, [r1, r2]
|
||||
cmp r12, #CPU_RESETTABLE_SOON
|
||||
moveq r0, #1
|
||||
movne r0, #0
|
||||
ret lr
|
||||
ENDPROC(tegra20_cpu_is_resettable_soon)
|
||||
|
||||
/*
|
||||
* tegra20_sleep_core_finish(unsigned long v2p)
|
||||
*
|
||||
@ -242,68 +134,6 @@ ENTRY(tegra20_sleep_core_finish)
|
||||
ret r3
|
||||
ENDPROC(tegra20_sleep_core_finish)
|
||||
|
||||
/*
|
||||
* tegra20_sleep_cpu_secondary_finish(unsigned long v2p)
|
||||
*
|
||||
* Enters WFI on secondary CPU by exiting coherency.
|
||||
*/
|
||||
ENTRY(tegra20_sleep_cpu_secondary_finish)
|
||||
stmfd sp!, {r4-r11, lr}
|
||||
|
||||
mrc p15, 0, r11, c1, c0, 1 @ save actlr before exiting coherency
|
||||
|
||||
/* Flush and disable the L1 data cache */
|
||||
mov r0, #TEGRA_FLUSH_CACHE_LOUIS
|
||||
bl tegra_disable_clean_inv_dcache
|
||||
|
||||
mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT
|
||||
ldr r4, =__tegra20_cpu1_resettable_status_offset
|
||||
mov r3, #CPU_RESETTABLE
|
||||
strb r3, [r0, r4]
|
||||
|
||||
bl tegra_cpu_do_idle
|
||||
|
||||
/*
|
||||
* cpu may be reset while in wfi, which will return through
|
||||
* tegra_resume to cpu_resume
|
||||
* or interrupt may wake wfi, which will return here
|
||||
* cpu state is unchanged - MMU is on, cache is on, coherency
|
||||
* is off, and the data cache is off
|
||||
*
|
||||
* r11 contains the original actlr
|
||||
*/
|
||||
|
||||
bl tegra_pen_lock
|
||||
|
||||
mov32 r0, TEGRA_IRAM_RESET_BASE_VIRT
|
||||
ldr r4, =__tegra20_cpu1_resettable_status_offset
|
||||
mov r3, #CPU_NOT_RESETTABLE
|
||||
strb r3, [r0, r4]
|
||||
|
||||
bl tegra_pen_unlock
|
||||
|
||||
/* Re-enable the data cache */
|
||||
mrc p15, 0, r10, c1, c0, 0
|
||||
orr r10, r10, #CR_C
|
||||
mcr p15, 0, r10, c1, c0, 0
|
||||
isb
|
||||
|
||||
mcr p15, 0, r11, c1, c0, 1 @ reenable coherency
|
||||
|
||||
/* Invalidate the TLBs & BTAC */
|
||||
mov r1, #0
|
||||
mcr p15, 0, r1, c8, c3, 0 @ invalidate shared TLBs
|
||||
mcr p15, 0, r1, c7, c1, 6 @ invalidate shared BTAC
|
||||
dsb
|
||||
isb
|
||||
|
||||
/* the cpu was running with coherency disabled,
|
||||
* caches may be out of date */
|
||||
bl v7_flush_kern_cache_louis
|
||||
|
||||
ldmfd sp!, {r4 - r11, pc}
|
||||
ENDPROC(tegra20_sleep_cpu_secondary_finish)
|
||||
|
||||
/*
|
||||
* tegra20_tear_down_cpu
|
||||
*
|
||||
|
@ -114,8 +114,6 @@
|
||||
.endm
|
||||
|
||||
#else
|
||||
void tegra_pen_lock(void);
|
||||
void tegra_pen_unlock(void);
|
||||
void tegra_resume(void);
|
||||
int tegra_sleep_cpu_finish(unsigned long);
|
||||
void tegra_disable_clean_inv_dcache(u32 flag);
|
||||
@ -123,16 +121,6 @@ void tegra_disable_clean_inv_dcache(u32 flag);
|
||||
void tegra20_hotplug_shutdown(void);
|
||||
void tegra30_hotplug_shutdown(void);
|
||||
|
||||
void tegra20_cpu_shutdown(int cpu);
|
||||
int tegra20_cpu_is_resettable_soon(void);
|
||||
void tegra20_cpu_clear_resettable(void);
|
||||
#ifdef CONFIG_ARCH_TEGRA_2x_SOC
|
||||
void tegra20_cpu_set_resettable_soon(void);
|
||||
#else
|
||||
static inline void tegra20_cpu_set_resettable_soon(void) {}
|
||||
#endif
|
||||
|
||||
int tegra20_sleep_cpu_secondary_finish(unsigned long);
|
||||
void tegra20_tear_down_cpu(void);
|
||||
int tegra30_sleep_cpu_secondary_finish(unsigned long);
|
||||
void tegra30_tear_down_cpu(void);
|
||||
|
Loading…
Reference in New Issue
Block a user