2014-06-04 20:03:42 +04:00
/*
* Copyright ( C ) 2014 Marvell Technology Group Ltd .
*
* Antoine Ténart < antoine . tenart @ free - electrons . com >
*
* 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/io.h>
# include <linux/delay.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <asm/cacheflush.h>
2015-09-14 09:47:45 +03:00
# include <asm/cp15.h>
2017-01-29 19:31:32 +03:00
# include <asm/memory.h>
2014-06-04 20:03:42 +04:00
# include <asm/smp_plat.h>
# include <asm/smp_scu.h>
2015-09-14 09:47:44 +03:00
/*
* There are two reset registers , one with self - clearing ( SC )
* reset and one with non - self - clearing reset ( NON_SC ) .
*/
# define CPU_RESET_SC 0x00
# define CPU_RESET_NON_SC 0x20
2014-06-04 20:03:42 +04:00
# define RESET_VECT 0x00
# define SW_RESET_ADDR 0x94
extern u32 boot_inst ;
static void __iomem * cpu_ctrl ;
static inline void berlin_perform_reset_cpu ( unsigned int cpu )
{
u32 val ;
2015-09-14 09:47:44 +03:00
val = readl ( cpu_ctrl + CPU_RESET_NON_SC ) ;
val & = ~ BIT ( cpu_logical_map ( cpu ) ) ;
writel ( val , cpu_ctrl + CPU_RESET_NON_SC ) ;
2014-06-04 20:03:42 +04:00
val | = BIT ( cpu_logical_map ( cpu ) ) ;
2015-09-14 09:47:44 +03:00
writel ( val , cpu_ctrl + CPU_RESET_NON_SC ) ;
2014-06-04 20:03:42 +04:00
}
static int berlin_boot_secondary ( unsigned int cpu , struct task_struct * idle )
{
if ( ! cpu_ctrl )
return - EFAULT ;
/*
* Reset the CPU , making it to execute the instruction in the reset
* exception vector .
*/
berlin_perform_reset_cpu ( cpu ) ;
return 0 ;
}
static void __init berlin_smp_prepare_cpus ( unsigned int max_cpus )
{
struct device_node * np ;
void __iomem * scu_base ;
void __iomem * vectors_base ;
np = of_find_compatible_node ( NULL , NULL , " arm,cortex-a9-scu " ) ;
scu_base = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! scu_base )
return ;
np = of_find_compatible_node ( NULL , NULL , " marvell,berlin-cpu-ctrl " ) ;
cpu_ctrl = of_iomap ( np , 0 ) ;
of_node_put ( np ) ;
if ( ! cpu_ctrl )
goto unmap_scu ;
2017-01-29 19:31:32 +03:00
vectors_base = ioremap ( VECTORS_BASE , SZ_32K ) ;
2014-06-04 20:03:42 +04:00
if ( ! vectors_base )
goto unmap_scu ;
scu_enable ( scu_base ) ;
flush_cache_all ( ) ;
/*
* Write the first instruction the CPU will execute after being reset
* in the reset exception vector .
*/
writel ( boot_inst , vectors_base + RESET_VECT ) ;
/*
* Write the secondary startup address into the SW reset address
* vector . This is used by boot_inst .
*/
2017-01-15 05:59:29 +03:00
writel ( __pa_symbol ( secondary_startup ) , vectors_base + SW_RESET_ADDR ) ;
2014-06-04 20:03:42 +04:00
iounmap ( vectors_base ) ;
unmap_scu :
iounmap ( scu_base ) ;
}
2015-09-14 09:47:45 +03:00
# ifdef CONFIG_HOTPLUG_CPU
static void berlin_cpu_die ( unsigned int cpu )
{
v7_exit_coherency_flush ( louis ) ;
while ( 1 )
cpu_do_idle ( ) ;
}
static int berlin_cpu_kill ( unsigned int cpu )
{
u32 val ;
val = readl ( cpu_ctrl + CPU_RESET_NON_SC ) ;
val & = ~ BIT ( cpu_logical_map ( cpu ) ) ;
writel ( val , cpu_ctrl + CPU_RESET_NON_SC ) ;
return 1 ;
}
# endif
2015-11-15 04:39:53 +03:00
static const struct smp_operations berlin_smp_ops __initconst = {
2014-06-04 20:03:42 +04:00
. smp_prepare_cpus = berlin_smp_prepare_cpus ,
. smp_boot_secondary = berlin_boot_secondary ,
2015-09-14 09:47:45 +03:00
# ifdef CONFIG_HOTPLUG_CPU
. cpu_die = berlin_cpu_die ,
. cpu_kill = berlin_cpu_kill ,
# endif
2014-06-04 20:03:42 +04:00
} ;
CPU_METHOD_OF_DECLARE ( berlin_smp , " marvell,berlin-smp " , & berlin_smp_ops ) ;