2012-02-10 03:47:45 +04:00
/*
* arch / arm / mach - tegra / reset . c
*
* Copyright ( C ) 2011 , 2012 NVIDIA Corporation .
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
*/
# include <linux/init.h>
# include <linux/io.h>
# include <linux/cpumask.h>
# include <linux/bitops.h>
# include <asm/cacheflush.h>
# include <asm/hardware/cache-l2x0.h>
2013-11-24 10:30:51 +04:00
# include <asm/firmware.h>
2012-02-10 03:47:45 +04:00
2012-10-05 00:24:09 +04:00
# include "iomap.h"
2012-10-05 00:16:59 +04:00
# include "irammap.h"
2012-02-10 03:47:45 +04:00
# include "reset.h"
2012-10-31 13:41:16 +04:00
# include "sleep.h"
2012-02-10 03:47:45 +04:00
# include "fuse.h"
# define TEGRA_IRAM_RESET_BASE (TEGRA_IRAM_BASE + \
TEGRA_IRAM_RESET_HANDLER_OFFSET )
static bool is_enabled ;
2013-11-24 10:30:50 +04:00
static void __init tegra_cpu_reset_handler_set ( const u32 reset_address )
2012-02-10 03:47:45 +04:00
{
void __iomem * evp_cpu_reset =
IO_ADDRESS ( TEGRA_EXCEPTION_VECTORS_BASE + 0x100 ) ;
void __iomem * sb_ctrl = IO_ADDRESS ( TEGRA_SB_BASE ) ;
u32 reg ;
/*
* NOTE : This must be the one and only write to the EVP CPU reset
* vector in the entire system .
*/
2013-11-24 10:30:50 +04:00
writel ( reset_address , evp_cpu_reset ) ;
2012-02-10 03:47:45 +04:00
wmb ( ) ;
reg = readl ( evp_cpu_reset ) ;
/*
* Prevent further modifications to the physical reset vector .
* NOTE : Has no effect on chips prior to Tegra30 .
*/
if ( tegra_chip_id ! = TEGRA20 ) {
reg = readl ( sb_ctrl ) ;
reg | = 2 ;
writel ( reg , sb_ctrl ) ;
wmb ( ) ;
}
2013-11-24 10:30:50 +04:00
}
static void __init tegra_cpu_reset_handler_enable ( void )
{
void __iomem * iram_base = IO_ADDRESS ( TEGRA_IRAM_RESET_BASE ) ;
const u32 reset_address = TEGRA_IRAM_RESET_BASE +
tegra_cpu_reset_handler_offset ;
2013-11-24 10:30:51 +04:00
int err ;
2013-11-24 10:30:50 +04:00
BUG_ON ( is_enabled ) ;
BUG_ON ( tegra_cpu_reset_handler_size > TEGRA_IRAM_RESET_HANDLER_SIZE ) ;
memcpy ( iram_base , ( void * ) __tegra_cpu_reset_handler_start ,
tegra_cpu_reset_handler_size ) ;
2013-11-24 10:30:51 +04:00
err = call_firmware_op ( set_cpu_boot_addr , 0 , reset_address ) ;
switch ( err ) {
case - ENOSYS :
tegra_cpu_reset_handler_set ( reset_address ) ;
/* pass-through */
case 0 :
is_enabled = true ;
break ;
default :
pr_crit ( " Cannot set CPU reset handler: %d \n " , err ) ;
BUG ( ) ;
}
2012-02-10 03:47:45 +04:00
}
void __init tegra_cpu_reset_handler_init ( void )
{
# ifdef CONFIG_SMP
__tegra_cpu_reset_handler_data [ TEGRA_RESET_MASK_PRESENT ] =
2013-01-04 13:32:22 +04:00
* ( ( u32 * ) cpu_possible_mask ) ;
2012-02-10 03:47:45 +04:00
__tegra_cpu_reset_handler_data [ TEGRA_RESET_STARTUP_SECONDARY ] =
virt_to_phys ( ( void * ) tegra_secondary_startup ) ;
# endif
2012-10-31 13:41:16 +04:00
# ifdef CONFIG_PM_SLEEP
2013-08-12 13:40:00 +04:00
__tegra_cpu_reset_handler_data [ TEGRA_RESET_STARTUP_LP1 ] =
2013-08-21 02:19:15 +04:00
TEGRA_IRAM_LPx_RESUME_AREA ;
2012-10-31 13:41:16 +04:00
__tegra_cpu_reset_handler_data [ TEGRA_RESET_STARTUP_LP2 ] =
virt_to_phys ( ( void * ) tegra_resume ) ;
# endif
2012-02-10 03:47:45 +04:00
tegra_cpu_reset_handler_enable ( ) ;
}