2013-11-03 13:30:13 +04:00
/*
* SMP support for Allwinner SoCs
*
* Copyright ( C ) 2013 Maxime Ripard
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*
* Based on code
* Copyright ( C ) 2012 - 2013 Allwinner Ltd .
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/memory.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/smp.h>
# define CPUCFG_CPU_PWR_CLAMP_STATUS_REG(cpu) ((cpu) * 0x40 + 0x64)
# define CPUCFG_CPU_RST_CTRL_REG(cpu) (((cpu) + 1) * 0x40)
# define CPUCFG_CPU_CTRL_REG(cpu) (((cpu) + 1) * 0x40 + 0x04)
# define CPUCFG_CPU_STATUS_REG(cpu) (((cpu) + 1) * 0x40 + 0x08)
# define CPUCFG_GEN_CTRL_REG 0x184
# define CPUCFG_PRIVATE0_REG 0x1a4
# define CPUCFG_PRIVATE1_REG 0x1a8
# define CPUCFG_DBG_CTL0_REG 0x1e0
# define CPUCFG_DBG_CTL1_REG 0x1e4
# define PRCM_CPU_PWROFF_REG 0x100
# define PRCM_CPU_PWR_CLAMP_REG(cpu) (((cpu) * 4) + 0x140)
static void __iomem * cpucfg_membase ;
static void __iomem * prcm_membase ;
static DEFINE_SPINLOCK ( cpu_lock ) ;
static void __init sun6i_smp_prepare_cpus ( unsigned int max_cpus )
{
struct device_node * node ;
node = of_find_compatible_node ( NULL , NULL , " allwinner,sun6i-a31-prcm " ) ;
if ( ! node ) {
pr_err ( " Missing A31 PRCM node in the device tree \n " ) ;
return ;
}
prcm_membase = of_iomap ( node , 0 ) ;
2019-03-05 14:34:01 +03:00
of_node_put ( node ) ;
2013-11-03 13:30:13 +04:00
if ( ! prcm_membase ) {
pr_err ( " Couldn't map A31 PRCM registers \n " ) ;
return ;
}
node = of_find_compatible_node ( NULL , NULL ,
" allwinner,sun6i-a31-cpuconfig " ) ;
if ( ! node ) {
pr_err ( " Missing A31 CPU config node in the device tree \n " ) ;
return ;
}
cpucfg_membase = of_iomap ( node , 0 ) ;
2019-03-05 14:34:01 +03:00
of_node_put ( node ) ;
2013-11-03 13:30:13 +04:00
if ( ! cpucfg_membase )
pr_err ( " Couldn't map A31 CPU config registers \n " ) ;
}
static int sun6i_smp_boot_secondary ( unsigned int cpu ,
struct task_struct * idle )
{
u32 reg ;
int i ;
if ( ! ( prcm_membase & & cpucfg_membase ) )
return - EFAULT ;
spin_lock ( & cpu_lock ) ;
/* Set CPU boot address */
2017-01-15 05:59:29 +03:00
writel ( __pa_symbol ( secondary_startup ) ,
2013-11-03 13:30:13 +04:00
cpucfg_membase + CPUCFG_PRIVATE0_REG ) ;
/* Assert the CPU core in reset */
writel ( 0 , cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG ( cpu ) ) ;
/* Assert the L1 cache in reset */
reg = readl ( cpucfg_membase + CPUCFG_GEN_CTRL_REG ) ;
writel ( reg & ~ BIT ( cpu ) , cpucfg_membase + CPUCFG_GEN_CTRL_REG ) ;
/* Disable external debug access */
reg = readl ( cpucfg_membase + CPUCFG_DBG_CTL1_REG ) ;
writel ( reg & ~ BIT ( cpu ) , cpucfg_membase + CPUCFG_DBG_CTL1_REG ) ;
/* Power up the CPU */
for ( i = 0 ; i < = 8 ; i + + )
writel ( 0xff > > i , prcm_membase + PRCM_CPU_PWR_CLAMP_REG ( cpu ) ) ;
mdelay ( 10 ) ;
/* Clear CPU power-off gating */
reg = readl ( prcm_membase + PRCM_CPU_PWROFF_REG ) ;
writel ( reg & ~ BIT ( cpu ) , prcm_membase + PRCM_CPU_PWROFF_REG ) ;
mdelay ( 1 ) ;
/* Deassert the CPU core reset */
writel ( 3 , cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG ( cpu ) ) ;
/* Enable back the external debug accesses */
reg = readl ( cpucfg_membase + CPUCFG_DBG_CTL1_REG ) ;
writel ( reg | BIT ( cpu ) , cpucfg_membase + CPUCFG_DBG_CTL1_REG ) ;
spin_unlock ( & cpu_lock ) ;
return 0 ;
}
2015-11-15 04:39:53 +03:00
static const struct smp_operations sun6i_smp_ops __initconst = {
2013-11-03 13:30:13 +04:00
. smp_prepare_cpus = sun6i_smp_prepare_cpus ,
. smp_boot_secondary = sun6i_smp_boot_secondary ,
} ;
2014-12-17 20:18:14 +03:00
CPU_METHOD_OF_DECLARE ( sun6i_a31_smp , " allwinner,sun6i-a31 " , & sun6i_smp_ops ) ;
2015-03-18 06:24:01 +03:00
static void __init sun8i_smp_prepare_cpus ( unsigned int max_cpus )
{
struct device_node * node ;
node = of_find_compatible_node ( NULL , NULL , " allwinner,sun8i-a23-prcm " ) ;
if ( ! node ) {
pr_err ( " Missing A23 PRCM node in the device tree \n " ) ;
return ;
}
prcm_membase = of_iomap ( node , 0 ) ;
2019-03-05 14:32:57 +03:00
of_node_put ( node ) ;
2015-03-18 06:24:01 +03:00
if ( ! prcm_membase ) {
pr_err ( " Couldn't map A23 PRCM registers \n " ) ;
return ;
}
node = of_find_compatible_node ( NULL , NULL ,
" allwinner,sun8i-a23-cpuconfig " ) ;
if ( ! node ) {
pr_err ( " Missing A23 CPU config node in the device tree \n " ) ;
return ;
}
cpucfg_membase = of_iomap ( node , 0 ) ;
2019-03-05 14:32:57 +03:00
of_node_put ( node ) ;
2015-03-18 06:24:01 +03:00
if ( ! cpucfg_membase )
pr_err ( " Couldn't map A23 CPU config registers \n " ) ;
}
static int sun8i_smp_boot_secondary ( unsigned int cpu ,
struct task_struct * idle )
{
u32 reg ;
if ( ! ( prcm_membase & & cpucfg_membase ) )
return - EFAULT ;
spin_lock ( & cpu_lock ) ;
/* Set CPU boot address */
2017-01-15 05:59:29 +03:00
writel ( __pa_symbol ( secondary_startup ) ,
2015-03-18 06:24:01 +03:00
cpucfg_membase + CPUCFG_PRIVATE0_REG ) ;
/* Assert the CPU core in reset */
writel ( 0 , cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG ( cpu ) ) ;
/* Assert the L1 cache in reset */
reg = readl ( cpucfg_membase + CPUCFG_GEN_CTRL_REG ) ;
writel ( reg & ~ BIT ( cpu ) , cpucfg_membase + CPUCFG_GEN_CTRL_REG ) ;
/* Clear CPU power-off gating */
reg = readl ( prcm_membase + PRCM_CPU_PWROFF_REG ) ;
writel ( reg & ~ BIT ( cpu ) , prcm_membase + PRCM_CPU_PWROFF_REG ) ;
mdelay ( 1 ) ;
/* Deassert the CPU core reset */
writel ( 3 , cpucfg_membase + CPUCFG_CPU_RST_CTRL_REG ( cpu ) ) ;
spin_unlock ( & cpu_lock ) ;
return 0 ;
}
2015-11-15 04:39:53 +03:00
static const struct smp_operations sun8i_smp_ops __initconst = {
2015-03-18 06:24:01 +03:00
. smp_prepare_cpus = sun8i_smp_prepare_cpus ,
. smp_boot_secondary = sun8i_smp_boot_secondary ,
} ;
CPU_METHOD_OF_DECLARE ( sun8i_a23_smp , " allwinner,sun8i-a23 " , & sun8i_smp_ops ) ;