2005-11-08 00:05:42 +03:00
/*
* linux / arch / arm / mach - realview / platsmp . c
*
* Copyright ( C ) 2002 ARM Ltd .
* All Rights Reserved
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/errno.h>
# include <linux/smp.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2005-11-08 00:05:42 +03:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2008-02-04 19:39:00 +03:00
# include <asm/mach-types.h>
2011-01-19 13:24:56 +03:00
# include <asm/smp_scu.h>
2005-11-08 00:05:42 +03:00
2008-08-05 19:14:15 +04:00
# include <mach/board-eb.h>
# include <mach/board-pb11mp.h>
2009-05-30 16:56:12 +04:00
# include <mach/board-pbx.h>
2008-04-19 01:43:08 +04:00
2011-09-08 16:15:22 +04:00
# include <plat/platsmp.h>
2008-12-01 17:54:58 +03:00
2011-09-08 16:15:22 +04:00
# include "core.h"
ARM: Fix subtle race in CPU pen_release hotplug code
There is a subtle race in the CPU hotplug code, where a CPU which has
been offlined can online itself before being requested, which results
in things going astray on the next online/offline cycle.
What happens in the normal online/offline/online cycle is:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads -1
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
However, as the write of -1 of pen_release is not fully flushed back to
memory, and the checking of pen_release is done with caches disabled,
this allows CPU3 the opportunity to read the old value of pen_release:
CPU0 CPU3
requests boot of CPU3
pen_release = 3
flush cache line
checks pen_release, reads 3
starts boot
pen_release = -1
... requests CPU3 offline ...
... dies ...
checks pen_release, reads 3
starts boot
pen_release = -1
requests boot of CPU3
pen_release = 3
flush cache line
Fix this by grouping the write of pen_release along with its cache line
flushing code to ensure that any update to pen_release is always pushed
out to physical memory.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
2010-12-18 13:53:12 +03:00
2008-12-01 17:54:58 +03:00
static void __iomem * scu_base_addr ( void )
{
if ( machine_is_realview_eb_mp ( ) )
return __io_address ( REALVIEW_EB11MP_SCU_BASE ) ;
else if ( machine_is_realview_pb11mp ( ) )
return __io_address ( REALVIEW_TC11MP_SCU_BASE ) ;
2009-05-30 16:56:12 +04:00
else if ( machine_is_realview_pbx ( ) & &
( core_tile_pbx11mp ( ) | | core_tile_pbxa9mp ( ) ) )
return __io_address ( REALVIEW_PBX_TILE_SCU_BASE ) ;
2008-12-01 17:54:58 +03:00
else
return ( void __iomem * ) 0 ;
}
2006-02-16 14:08:09 +03:00
/*
* Initialise the CPU possible map early - this describes the CPUs
* which may be present or become present in the system .
*/
2011-09-08 16:15:22 +04:00
static void __init realview_smp_init_cpus ( void )
2006-02-16 14:08:09 +03:00
{
2010-12-02 21:09:37 +03:00
void __iomem * scu_base = scu_base_addr ( ) ;
unsigned int i , ncores ;
2006-02-16 14:08:09 +03:00
2010-12-02 21:09:37 +03:00
ncores = scu_base ? scu_get_core_count ( scu_base ) : 1 ;
2005-11-08 00:05:42 +03:00
/* sanity check */
2011-10-21 01:04:18 +04:00
if ( ncores > nr_cpu_ids ) {
pr_warn ( " SMP: %u cores greater than maximum (%u), clipping \n " ,
ncores , nr_cpu_ids ) ;
ncores = nr_cpu_ids ;
2005-11-08 00:05:42 +03:00
}
2010-12-03 13:42:58 +03:00
for ( i = 0 ; i < ncores ; i + + )
set_cpu_possible ( i , true ) ;
}
2005-11-08 00:05:42 +03:00
2011-09-08 16:15:22 +04:00
static void __init realview_smp_prepare_cpus ( unsigned int max_cpus )
2010-12-03 13:42:58 +03:00
{
2005-11-08 00:05:42 +03:00
2010-12-03 14:09:48 +03:00
scu_enable ( scu_base_addr ( ) ) ;
2005-11-08 00:05:42 +03:00
/*
2010-12-03 14:09:48 +03:00
* Write the address of secondary startup into the
* system - wide flags register . The BootMonitor waits
* until it receives a soft interrupt , and then the
* secondary CPU branches to this address .
2005-11-08 00:05:42 +03:00
*/
2011-12-15 18:02:33 +04:00
__raw_writel ( virt_to_phys ( versatile_secondary_startup ) ,
2010-12-03 14:09:48 +03:00
__io_address ( REALVIEW_SYS_FLAGSSET ) ) ;
2005-11-08 00:05:42 +03:00
}
2011-09-08 16:15:22 +04:00
struct smp_operations realview_smp_ops __initdata = {
. smp_init_cpus = realview_smp_init_cpus ,
. smp_prepare_cpus = realview_smp_prepare_cpus ,
. smp_secondary_init = versatile_secondary_init ,
. smp_boot_secondary = versatile_boot_secondary ,
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = realview_cpu_die ,
# endif
} ;