2010-02-22 04:46:23 +03:00
/*
* linux / arch / arm / mach - tegra / platsmp . c
*
* Copyright ( C ) 2002 ARM Ltd .
* All Rights Reserved
*
* Copyright ( C ) 2009 Palm
* 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/delay.h>
# include <linux/device.h>
# include <linux/jiffies.h>
# include <linux/smp.h>
# include <linux/io.h>
# include <asm/cacheflush.h>
2011-04-03 16:01:30 +04:00
# include <asm/hardware/gic.h>
2010-02-22 04:46:23 +03:00
# include <asm/mach-types.h>
# include <asm/smp_scu.h>
2012-02-10 03:47:50 +04:00
# include <mach/clk.h>
2010-02-22 04:46:23 +03:00
# include <mach/iomap.h>
2012-02-10 03:47:50 +04:00
# include <mach/powergate.h>
2010-02-22 04:46:23 +03:00
2012-02-10 03:47:45 +04:00
# include "fuse.h"
# include "flowctrl.h"
# include "reset.h"
2010-02-22 04:46:23 +03:00
extern void tegra_secondary_startup ( void ) ;
static void __iomem * scu_base = IO_ADDRESS ( TEGRA_ARM_PERIF_BASE ) ;
# define EVP_CPU_RESET_VECTOR \
( IO_ADDRESS ( TEGRA_EXCEPTION_VECTORS_BASE ) + 0x100 )
# define CLK_RST_CONTROLLER_CLK_CPU_CMPLX \
( IO_ADDRESS ( TEGRA_CLK_RESET_BASE ) + 0x4c )
2012-02-10 03:47:45 +04:00
# define CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET \
( IO_ADDRESS ( TEGRA_CLK_RESET_BASE ) + 0x340 )
2010-02-22 04:46:23 +03:00
# define CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR \
( IO_ADDRESS ( TEGRA_CLK_RESET_BASE ) + 0x344 )
2012-02-10 03:47:50 +04:00
# define CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR \
( IO_ADDRESS ( TEGRA_CLK_RESET_BASE ) + 0x34c )
2010-02-22 04:46:23 +03:00
2012-02-10 03:47:45 +04:00
# define CPU_CLOCK(cpu) (0x1<<(8+cpu))
# define CPU_RESET(cpu) (0x1111ul<<(cpu))
2010-02-22 04:46:23 +03:00
void __cpuinit platform_secondary_init ( unsigned int cpu )
{
/*
* if any interrupts are already enabled for the primary
* core ( e . g . timer irq ) , then they will not have been enabled
* for us : do so
*/
2010-12-04 19:01:03 +03:00
gic_secondary_init ( 0 ) ;
2010-02-22 04:46:23 +03:00
}
2012-02-10 03:47:45 +04:00
static int tegra20_power_up_cpu ( unsigned int cpu )
2010-02-22 04:46:23 +03:00
{
u32 reg ;
2012-02-10 03:47:45 +04:00
/* Enable the CPU clock. */
reg = readl ( CLK_RST_CONTROLLER_CLK_CPU_CMPLX ) ;
writel ( reg & ~ CPU_CLOCK ( cpu ) , CLK_RST_CONTROLLER_CLK_CPU_CMPLX ) ;
barrier ( ) ;
2010-02-22 04:46:23 +03:00
reg = readl ( CLK_RST_CONTROLLER_CLK_CPU_CMPLX ) ;
2012-02-10 03:47:45 +04:00
/* Clear flow controller CSR. */
flowctrl_write_cpu_csr ( cpu , 0 ) ;
2010-02-22 04:46:23 +03:00
2012-02-10 03:47:45 +04:00
return 0 ;
}
2010-02-22 04:46:23 +03:00
2012-02-10 03:47:50 +04:00
static int tegra30_power_up_cpu ( unsigned int cpu )
{
u32 reg ;
int ret , pwrgateid ;
unsigned long timeout ;
pwrgateid = tegra_cpu_powergate_id ( cpu ) ;
if ( pwrgateid < 0 )
return pwrgateid ;
/* If this is the first boot, toggle powergates directly. */
if ( ! tegra_powergate_is_powered ( pwrgateid ) ) {
ret = tegra_powergate_power_on ( pwrgateid ) ;
if ( ret )
return ret ;
/* Wait for the power to come up. */
timeout = jiffies + 10 * HZ ;
while ( tegra_powergate_is_powered ( pwrgateid ) ) {
if ( time_after ( jiffies , timeout ) )
return - ETIMEDOUT ;
udelay ( 10 ) ;
}
}
/* CPU partition is powered. Enable the CPU clock. */
writel ( CPU_CLOCK ( cpu ) , CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR ) ;
reg = readl ( CLK_RST_CONTROLLER_CLK_CPU_CMPLX_CLR ) ;
udelay ( 10 ) ;
/* Remove I/O clamps. */
ret = tegra_powergate_remove_clamping ( pwrgateid ) ;
udelay ( 10 ) ;
/* Clear flow controller CSR. */
flowctrl_write_cpu_csr ( cpu , 0 ) ;
return 0 ;
}
2012-02-10 03:47:45 +04:00
int __cpuinit boot_secondary ( unsigned int cpu , struct task_struct * idle )
{
int status ;
2010-02-22 04:46:23 +03:00
2012-02-10 03:47:50 +04:00
/*
* Force the CPU into reset . The CPU must remain in reset when the
2012-02-10 03:47:45 +04:00
* flow controller state is cleared ( which will cause the flow
* controller to stop driving reset if the CPU has been power - gated
* via the flow controller ) . This will have no effect on first boot
* of the CPU since it should already be in reset .
*/
writel ( CPU_RESET ( cpu ) , CLK_RST_CONTROLLER_RST_CPU_CMPLX_SET ) ;
dmb ( ) ;
2010-02-22 04:46:23 +03:00
/*
2012-02-10 03:47:45 +04:00
* Unhalt the CPU . If the flow controller was used to power - gate the
* CPU this will cause the flow controller to stop driving reset .
* The CPU will remain in reset because the clock and reset block
* is now driving reset .
2010-02-22 04:46:23 +03:00
*/
2012-02-10 03:47:45 +04:00
flowctrl_write_cpu_halt ( cpu , 0 ) ;
switch ( tegra_chip_id ) {
case TEGRA20 :
status = tegra20_power_up_cpu ( cpu ) ;
break ;
2012-02-10 03:47:50 +04:00
case TEGRA30 :
status = tegra30_power_up_cpu ( cpu ) ;
break ;
2012-02-10 03:47:45 +04:00
default :
status = - EINVAL ;
break ;
}
2010-02-22 04:46:23 +03:00
2012-02-10 03:47:45 +04:00
if ( status )
goto done ;
/* Take the CPU out of reset. */
writel ( CPU_RESET ( cpu ) , CLK_RST_CONTROLLER_RST_CPU_CMPLX_CLR ) ;
wmb ( ) ;
done :
return status ;
2010-02-22 04:46:23 +03:00
}
/*
* Initialise the CPU possible map early - this describes the CPUs
* which may be present or become present in the system .
*/
void __init smp_init_cpus ( void )
{
unsigned int i , ncores = scu_get_core_count ( scu_base ) ;
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 ;
2010-12-03 22:29:53 +03:00
}
2010-02-22 04:46:23 +03:00
for ( i = 0 ; i < ncores ; i + + )
2011-06-23 12:28:28 +04:00
set_cpu_possible ( i , true ) ;
2011-04-03 16:01:30 +04:00
set_smp_cross_call ( gic_raise_softirq ) ;
2010-02-22 04:46:23 +03:00
}
2010-12-03 14:09:48 +03:00
void __init platform_smp_prepare_cpus ( unsigned int max_cpus )
2010-02-22 04:46:23 +03:00
{
2012-02-10 03:47:45 +04:00
tegra_cpu_reset_handler_init ( ) ;
2010-12-03 14:09:48 +03:00
scu_enable ( scu_base ) ;
2010-02-22 04:46:23 +03:00
}