Commit 0573b957 authored by Tony Lindgren's avatar Tony Lindgren

ARM: OMAP4+: Prevent CPU1 related hang with kexec

Kexec booted kernels on omap4 will hang early during the boot if the
booted kernel is different version from the previous kernel.

This is because the previous kernel may have configured low-power mode
using CPU1_WAKEUP_NS_PA_ADDR. In that case it points to the previous
kernel's omap4_secondary_startup(), and CPU1 can be in low power mode
from the previous kernel. When the new kernel configures the CPU1
clockdomain, CPU1 can wake from low power state prematurely during
omap44xx_clockdomains_init() running random code.

Let's fix the issue by configuring CPU1_WAKEUP_NS_PA_ADDR before we
call omap44xx_clockdomains_init(). Note that this is very early during
the init, and we will do proper CPU1 reset during SMP init a bit later
on in omap4_smp_prepare_cpus(). And we need to do this when SMP is
not enabled as the previous kernel may have had it enabled.
Acked-by: default avatarSantosh Shilimkar <ssantosh@kernel.org>
Tested-by: default avatarKeerthy <j-keerthy@ti.com>
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent f4b9f40a
...@@ -8,7 +8,7 @@ ccflags-y := -I$(srctree)/$(src)/include \ ...@@ -8,7 +8,7 @@ ccflags-y := -I$(srctree)/$(src)/include \
# Common support # Common support
obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o timer.o pm.o \ obj-y := id.o io.o control.o mux.o devices.o fb.o serial.o timer.o pm.o \
common.o gpio.o dma.o wd_timer.o display.o i2c.o hdq1w.o omap_hwmod.o \ common.o gpio.o dma.o wd_timer.o display.o i2c.o hdq1w.o omap_hwmod.o \
omap_device.o sram.o drm.o omap_device.o omap-headsmp.o sram.o drm.o
hwmod-common = omap_hwmod.o omap_hwmod_reset.o \ hwmod-common = omap_hwmod.o omap_hwmod_reset.o \
omap_hwmod_common_data.o omap_hwmod_common_data.o
...@@ -32,7 +32,7 @@ obj-$(CONFIG_SOC_HAS_OMAP2_SDRC) += sdrc.o ...@@ -32,7 +32,7 @@ obj-$(CONFIG_SOC_HAS_OMAP2_SDRC) += sdrc.o
# SMP support ONLY available for OMAP4 # SMP support ONLY available for OMAP4
smp-$(CONFIG_SMP) += omap-smp.o omap-headsmp.o smp-$(CONFIG_SMP) += omap-smp.o
smp-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o smp-$(CONFIG_HOTPLUG_CPU) += omap-hotplug.o
omap-4-5-common = omap4-common.o omap-wakeupgen.o omap-4-5-common = omap4-common.o omap-wakeupgen.o
obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-common) $(smp-y) sleep44xx.o obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-common) $(smp-y) sleep44xx.o
......
...@@ -259,12 +259,14 @@ extern void gic_timer_retrigger(void); ...@@ -259,12 +259,14 @@ extern void gic_timer_retrigger(void);
extern void omap_smc1(u32 fn, u32 arg); extern void omap_smc1(u32 fn, u32 arg);
extern void omap4_sar_ram_init(void); extern void omap4_sar_ram_init(void);
extern void __iomem *omap4_get_sar_ram_base(void); extern void __iomem *omap4_get_sar_ram_base(void);
extern void omap4_mpuss_early_init(void);
extern void omap_do_wfi(void); extern void omap_do_wfi(void);
#ifdef CONFIG_SMP
/* Needed for secondary core boot */
extern void omap4_secondary_startup(void); extern void omap4_secondary_startup(void);
extern void omap4460_secondary_startup(void); extern void omap4460_secondary_startup(void);
#ifdef CONFIG_SMP
/* Needed for secondary core boot */
extern u32 omap_modify_auxcoreboot0(u32 set_mask, u32 clear_mask); extern u32 omap_modify_auxcoreboot0(u32 set_mask, u32 clear_mask);
extern void omap_auxcoreboot_addr(u32 cpu_addr); extern void omap_auxcoreboot_addr(u32 cpu_addr);
extern u32 omap_read_auxcoreboot0(void); extern u32 omap_read_auxcoreboot0(void);
......
...@@ -691,6 +691,7 @@ void __init omap4430_init_early(void) ...@@ -691,6 +691,7 @@ void __init omap4430_init_early(void)
omap4xxx_check_features(); omap4xxx_check_features();
omap2_prcm_base_init(); omap2_prcm_base_init();
omap4_sar_ram_init(); omap4_sar_ram_init();
omap4_mpuss_early_init();
omap4_pm_init_early(); omap4_pm_init_early();
omap44xx_voltagedomains_init(); omap44xx_voltagedomains_init();
omap44xx_powerdomains_init(); omap44xx_powerdomains_init();
......
...@@ -62,6 +62,8 @@ ...@@ -62,6 +62,8 @@
#include "prm44xx.h" #include "prm44xx.h"
#include "prm-regbits-44xx.h" #include "prm-regbits-44xx.h"
static void __iomem *sar_base;
#ifdef CONFIG_SMP #ifdef CONFIG_SMP
struct omap4_cpu_pm_info { struct omap4_cpu_pm_info {
...@@ -90,7 +92,6 @@ struct cpu_pm_ops { ...@@ -90,7 +92,6 @@ struct cpu_pm_ops {
static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info); static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info);
static struct powerdomain *mpuss_pd; static struct powerdomain *mpuss_pd;
static void __iomem *sar_base;
static u32 cpu_context_offset; static u32 cpu_context_offset;
static int default_finish_suspend(unsigned long cpu_state) static int default_finish_suspend(unsigned long cpu_state)
...@@ -366,9 +367,6 @@ int __init omap4_mpuss_init(void) ...@@ -366,9 +367,6 @@ int __init omap4_mpuss_init(void)
return -ENODEV; return -ENODEV;
} }
if (cpu_is_omap44xx())
sar_base = omap4_get_sar_ram_base();
/* Initilaise per CPU PM information */ /* Initilaise per CPU PM information */
pm_info = &per_cpu(omap4_pm_info, 0x0); pm_info = &per_cpu(omap4_pm_info, 0x0);
if (sar_base) { if (sar_base) {
...@@ -444,3 +442,26 @@ int __init omap4_mpuss_init(void) ...@@ -444,3 +442,26 @@ int __init omap4_mpuss_init(void)
} }
#endif #endif
/*
* For kexec, we must set CPU1_WAKEUP_NS_PA_ADDR to point to
* current kernel's secondary_startup() early before
* clockdomains_init(). Otherwise clockdomain_init() can
* wake CPU1 and cause a hang.
*/
void __init omap4_mpuss_early_init(void)
{
unsigned long startup_pa;
if (!cpu_is_omap44xx())
return;
sar_base = omap4_get_sar_ram_base();
if (cpu_is_omap443x())
startup_pa = virt_to_phys(omap4_secondary_startup);
else
startup_pa = virt_to_phys(omap4460_secondary_startup);
writel_relaxed(startup_pa, sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment