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:
Dmitry Osipenko 2020-02-25 01:40:43 +03:00 committed by Thierry Reding
parent 859a6f6ee1
commit d90bdb72bb
7 changed files with 4 additions and 251 deletions

View File

@ -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;
}

View File

@ -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)
{
/*

View File

@ -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);

View File

@ -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

View File

@ -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 \

View File

@ -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
*

View File

@ -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);