diff --git a/arch/arm/mach-omap2/common.h b/arch/arm/mach-omap2/common.h index 7045e4d61ac39608acb1a46ea60c5c020e067726..70993a9fcb500269e916e6bcb12a061d1743c0d0 100644 --- a/arch/arm/mach-omap2/common.h +++ b/arch/arm/mach-omap2/common.h @@ -275,6 +275,7 @@ static inline void __iomem *omap4_get_scu_base(void) #endif extern void __init gic_init_irq(void); +extern void gic_dist_disable(void); extern void omap_smc1(u32 fn, u32 arg); extern void __iomem *omap4_get_sar_ram_base(void); extern void omap_do_wfi(void); @@ -282,6 +283,7 @@ extern void omap_do_wfi(void); #ifdef CONFIG_SMP /* Needed for secondary core boot */ extern void omap_secondary_startup(void); +extern void omap_secondary_startup_4460(void); extern u32 omap_modify_auxcoreboot0(u32 set_mask, u32 clear_mask); extern void omap_auxcoreboot_addr(u32 cpu_addr); extern u32 omap_read_auxcoreboot0(void); diff --git a/arch/arm/mach-omap2/omap-headsmp.S b/arch/arm/mach-omap2/omap-headsmp.S index 502e3135aad3a7ca4ea41f848ec449447307be0c..0ea09faf327b2faa48d1889d63cc49246ee9eb37 100644 --- a/arch/arm/mach-omap2/omap-headsmp.S +++ b/arch/arm/mach-omap2/omap-headsmp.S @@ -18,6 +18,8 @@ #include #include +#include "omap44xx.h" + __CPUINIT /* Physical address needed since MMU not enabled yet on secondary core */ @@ -64,3 +66,39 @@ hold: ldr r12,=0x103 b secondary_startup ENDPROC(omap_secondary_startup) +ENTRY(omap_secondary_startup_4460) +hold_2: ldr r12,=0x103 + dsb + smc #0 @ read from AuxCoreBoot0 + mov r0, r0, lsr #9 + mrc p15, 0, r4, c0, c0, 5 + and r4, r4, #0x0f + cmp r0, r4 + bne hold_2 + + /* + * GIC distributor control register has changed between + * CortexA9 r1pX and r2pX. The Control Register secure + * banked version is now composed of 2 bits: + * bit 0 == Secure Enable + * bit 1 == Non-Secure Enable + * The Non-Secure banked register has not changed + * Because the ROM Code is based on the r1pX GIC, the CPU1 + * GIC restoration will cause a problem to CPU0 Non-Secure SW. + * The workaround must be: + * 1) Before doing the CPU1 wakeup, CPU0 must disable + * the GIC distributor + * 2) CPU1 must re-enable the GIC distributor on + * it's wakeup path. + */ + ldr r1, =OMAP44XX_GIC_DIST_BASE + ldr r0, [r1] + orr r0, #1 + str r0, [r1] + + /* + * we've been released from the wait loop,secondary_stack + * should now contain the SVC stack for this core + */ + b secondary_startup +ENDPROC(omap_secondary_startup_4460) diff --git a/arch/arm/mach-omap2/omap-mpuss-lowpower.c b/arch/arm/mach-omap2/omap-mpuss-lowpower.c index ff4e6a0e9c7c7ef367ddffe6d92735a7e7e3389c..c8bc3ad85f68e3042201ac1d0a1beaaadbdc482d 100644 --- a/arch/arm/mach-omap2/omap-mpuss-lowpower.c +++ b/arch/arm/mach-omap2/omap-mpuss-lowpower.c @@ -67,6 +67,7 @@ struct omap4_cpu_pm_info { void __iomem *scu_sar_addr; void __iomem *wkup_sar_addr; void __iomem *l2x0_sar_addr; + void (*secondary_startup)(void); }; static DEFINE_PER_CPU(struct omap4_cpu_pm_info, omap4_pm_info); @@ -299,6 +300,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) { unsigned int cpu_state = 0; + struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu); if (omap_rev() == OMAP4430_REV_ES1_0) return -ENXIO; @@ -308,7 +310,7 @@ int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) clear_cpu_prev_pwrst(cpu); set_cpu_next_pwrst(cpu, power_state); - set_cpu_wakeup_addr(cpu, virt_to_phys(omap_secondary_startup)); + set_cpu_wakeup_addr(cpu, virt_to_phys(pm_info->secondary_startup)); scu_pwrst_prepare(cpu, power_state); /* @@ -359,6 +361,11 @@ int __init omap4_mpuss_init(void) pm_info->scu_sar_addr = sar_base + SCU_OFFSET1; pm_info->wkup_sar_addr = sar_base + CPU1_WAKEUP_NS_PA_ADDR_OFFSET; pm_info->l2x0_sar_addr = sar_base + L2X0_SAVE_OFFSET1; + if (cpu_is_omap446x()) + pm_info->secondary_startup = omap_secondary_startup_4460; + else + pm_info->secondary_startup = omap_secondary_startup; + pm_info->pwrdm = pwrdm_lookup("cpu1_pwrdm"); if (!pm_info->pwrdm) { pr_err("Lookup failed for CPU1 pwrdm\n"); diff --git a/arch/arm/mach-omap2/omap-smp.c b/arch/arm/mach-omap2/omap-smp.c index 4d05fa8a4e487e78b59fb98784e51efee8fbaf41..7d9c0e3fedc4dac9e64903f23bc48f725b79fdfc 100644 --- a/arch/arm/mach-omap2/omap-smp.c +++ b/arch/arm/mach-omap2/omap-smp.c @@ -32,6 +32,7 @@ #include "iomap.h" #include "common.h" #include "clockdomain.h" +#include "pm.h" #define CPU_MASK 0xff0ffff0 #define CPU_CORTEX_A9 0x410FC090 @@ -118,6 +119,24 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct * * 4.3.4.2 Power States of CPU0 and CPU1 */ if (booted) { + /* + * GIC distributor control register has changed between + * CortexA9 r1pX and r2pX. The Control Register secure + * banked version is now composed of 2 bits: + * bit 0 == Secure Enable + * bit 1 == Non-Secure Enable + * The Non-Secure banked register has not changed + * Because the ROM Code is based on the r1pX GIC, the CPU1 + * GIC restoration will cause a problem to CPU0 Non-Secure SW. + * The workaround must be: + * 1) Before doing the CPU1 wakeup, CPU0 must disable + * the GIC distributor + * 2) CPU1 must re-enable the GIC distributor on + * it's wakeup path. + */ + if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD)) + gic_dist_disable(); + clkdm_wakeup(cpu1_clkdm); clkdm_allow_idle(cpu1_clkdm); } else { @@ -138,7 +157,14 @@ static int __cpuinit omap4_boot_secondary(unsigned int cpu, struct task_struct * static void __init wakeup_secondary(void) { + void *startup_addr = omap_secondary_startup; void __iomem *base = omap_get_wakeupgen_base(); + + if (cpu_is_omap446x()) { + startup_addr = omap_secondary_startup_4460; + pm44xx_errata |= PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD; + } + /* * Write the address of secondary startup routine into the * AuxCoreBoot1 where ROM code will jump and start executing @@ -146,7 +172,7 @@ static void __init wakeup_secondary(void) * A barrier is added to ensure that write buffer is drained */ if (omap_secure_apis_support()) - omap_auxcoreboot_addr(virt_to_phys(omap_secondary_startup)); + omap_auxcoreboot_addr(virt_to_phys(startup_addr)); else __raw_writel(virt_to_phys(omap5_secondary_startup), base + OMAP_AUX_CORE_BOOT_1); diff --git a/arch/arm/mach-omap2/omap4-common.c b/arch/arm/mach-omap2/omap4-common.c index e1f289748c5d5d7021b8f079c878bb723466785e..72cf396a0fc2962ac219677ce3f949098c35c48c 100644 --- a/arch/arm/mach-omap2/omap4-common.c +++ b/arch/arm/mach-omap2/omap4-common.c @@ -41,6 +41,7 @@ static void __iomem *l2cache_base; #endif static void __iomem *sar_ram_base; +static void __iomem *gic_dist_base_addr; #ifdef CONFIG_OMAP4_ERRATA_I688 /* Used to implement memory barrier on DRAM path */ @@ -95,7 +96,6 @@ void __init omap_barriers_init(void) void __init gic_init_irq(void) { void __iomem *omap_irq_base; - void __iomem *gic_dist_base_addr; /* Static mapping, never released */ gic_dist_base_addr = ioremap(OMAP44XX_GIC_DIST_BASE, SZ_4K); @@ -110,6 +110,12 @@ void __init gic_init_irq(void) gic_init(0, 29, gic_dist_base_addr, omap_irq_base); } +void gic_dist_disable(void) +{ + if (gic_dist_base_addr) + __raw_writel(0x0, gic_dist_base_addr + GIC_DIST_CTRL); +} + #ifdef CONFIG_CACHE_L2X0 void __iomem *omap4_get_l2cache_base(void) diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h index f76a0d0f839f9b55f037664ab2b859912b24ef1b..fc3c96d5e013fc55b6ed40b21b404f379b90ef19 100644 --- a/arch/arm/mach-omap2/pm.h +++ b/arch/arm/mach-omap2/pm.h @@ -102,6 +102,8 @@ extern void enable_omap3630_toggle_l2_on_restore(void); static inline void enable_omap3630_toggle_l2_on_restore(void) { } #endif /* defined(CONFIG_PM) && defined(CONFIG_ARCH_OMAP3) */ +#define PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD (1 << 0) + #if defined(CONFIG_ARCH_OMAP4) extern u16 pm44xx_errata; #define IS_PM44XX_ERRATUM(id) (pm44xx_errata & (id))