2014-07-02 17:46:58 +02:00
/*
2015-09-10 12:35:13 +02:00
* Atmel AT91 SAM9 & SAMA5 SoCs reset code
2014-07-02 17:46:58 +02:00
*
* Copyright ( C ) 2007 Atmel Corporation .
* Copyright ( C ) BitBox Ltd 2010
* Copyright ( C ) 2011 Jean - Christophe PLAGNIOL - VILLARD < plagnioj @ jcosoft . com >
* Copyright ( C ) 2014 Free Electrons
*
* 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 .
*/
2015-08-11 11:12:48 +02:00
# include <linux/clk.h>
2014-07-02 17:46:58 +02:00
# include <linux/io.h>
# include <linux/module.h>
# include <linux/of_address.h>
# include <linux/platform_device.h>
# include <linux/reboot.h>
2014-11-07 21:58:21 +01:00
# include <soc/at91/at91sam9_ddrsdr.h>
# include <soc/at91/at91sam9_sdramc.h>
2014-07-02 17:46:58 +02:00
# define AT91_RSTC_CR 0x00 /* Reset Controller Control Register */
# define AT91_RSTC_PROCRST BIT(0) /* Processor Reset */
# define AT91_RSTC_PERRST BIT(2) /* Peripheral Reset */
# define AT91_RSTC_EXTRST BIT(3) /* External Reset */
# define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */
# define AT91_RSTC_SR 0x04 /* Reset Controller Status Register */
# define AT91_RSTC_URSTS BIT(0) /* User Reset Status */
# define AT91_RSTC_RSTTYP GENMASK(10, 8) /* Reset Type */
# define AT91_RSTC_NRSTL BIT(16) /* NRST Pin Level */
# define AT91_RSTC_SRCMP BIT(17) /* Software Reset Command in Progress */
# define AT91_RSTC_MR 0x08 /* Reset Controller Mode Register */
# define AT91_RSTC_URSTEN BIT(0) /* User Reset Enable */
# define AT91_RSTC_URSTIEN BIT(4) /* User Reset Interrupt Enable */
# define AT91_RSTC_ERSTL GENMASK(11, 8) /* External Reset Length */
enum reset_type {
RESET_TYPE_GENERAL = 0 ,
RESET_TYPE_WAKEUP = 1 ,
RESET_TYPE_WATCHDOG = 2 ,
RESET_TYPE_SOFTWARE = 3 ,
RESET_TYPE_USER = 4 ,
2019-02-06 19:12:21 +01:00
RESET_TYPE_CPU_FAIL = 6 ,
RESET_TYPE_XTAL_FAIL = 7 ,
RESET_TYPE_ULP2 = 8 ,
2014-07-02 17:46:58 +02:00
} ;
static void __iomem * at91_ramc_base [ 2 ] , * at91_rstc_base ;
2015-08-11 11:12:48 +02:00
static struct clk * sclk ;
2014-07-02 17:46:58 +02:00
/*
* unless the SDRAM is cleanly shutdown before we hit the
* reset register it can be left driving the data bus and
* killing the chance of a subsequent boot from NAND
*/
2015-01-25 12:30:41 -08:00
static int at91sam9260_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
2014-07-02 17:46:58 +02:00
{
asm volatile (
/* Align to cache lines */
" .balign 32 \n \t "
/* Disable SDRAM accesses */
" str %2, [%0, # " __stringify ( AT91_SDRAMC_TR ) " ] \n \t "
/* Power down SDRAM */
" str %3, [%0, # " __stringify ( AT91_SDRAMC_LPR ) " ] \n \t "
/* Reset CPU */
" str %4, [%1, # " __stringify ( AT91_RSTC_CR ) " ] \n \t "
" b . \n \t "
:
: " r " ( at91_ramc_base [ 0 ] ) ,
" r " ( at91_rstc_base ) ,
" r " ( 1 ) ,
2015-03-26 14:16:22 +00:00
" r " cpu_to_le32 ( AT91_SDRAMC_LPCB_POWER_DOWN ) ,
" r " cpu_to_le32 ( AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST ) ) ;
2015-01-25 12:30:41 -08:00
return NOTIFY_DONE ;
2014-07-02 17:46:58 +02:00
}
2015-01-25 12:30:41 -08:00
static int at91sam9g45_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
2014-07-02 17:46:58 +02:00
{
asm volatile (
/*
* Test wether we have a second RAM controller to care
* about .
*
* First , test that we can dereference the virtual address .
*/
" cmp %1, #0 \n \t "
" beq 1f \n \t "
/* Then, test that the RAM controller is enabled */
" ldr r0, [%1] \n \t "
" cmp r0, #0 \n \t "
/* Align to cache lines */
" .balign 32 \n \t "
/* Disable SDRAM0 accesses */
" 1: str %3, [%0, # " __stringify ( AT91_DDRSDRC_RTR ) " ] \n \t "
/* Power down SDRAM0 */
2014-10-20 20:27:09 +02:00
" str %4, [%0, # " __stringify ( AT91_DDRSDRC_LPR ) " ] \n \t "
2014-07-02 17:46:58 +02:00
/* Disable SDRAM1 accesses */
" strne %3, [%1, # " __stringify ( AT91_DDRSDRC_RTR ) " ] \n \t "
/* Power down SDRAM1 */
2014-10-20 20:27:09 +02:00
" strne %4, [%1, # " __stringify ( AT91_DDRSDRC_LPR ) " ] \n \t "
2014-07-02 17:46:58 +02:00
/* Reset CPU */
" str %5, [%2, # " __stringify ( AT91_RSTC_CR ) " ] \n \t "
" b . \n \t "
:
: " r " ( at91_ramc_base [ 0 ] ) ,
" r " ( at91_ramc_base [ 1 ] ) ,
" r " ( at91_rstc_base ) ,
" r " ( 1 ) ,
2015-03-26 14:16:22 +00:00
" r " cpu_to_le32 ( AT91_DDRSDRC_LPCB_POWER_DOWN ) ,
" r " cpu_to_le32 ( AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST )
2014-07-02 17:46:58 +02:00
: " r0 " ) ;
2015-01-25 12:30:41 -08:00
return NOTIFY_DONE ;
2014-07-02 17:46:58 +02:00
}
2015-07-20 17:32:05 +08:00
static int sama5d3_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
{
writel ( cpu_to_le32 ( AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST ) ,
at91_rstc_base ) ;
return NOTIFY_DONE ;
}
2017-01-18 00:07:37 +01:00
static int samx7_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
{
writel ( cpu_to_le32 ( AT91_RSTC_KEY | AT91_RSTC_PROCRST ) ,
at91_rstc_base ) ;
return NOTIFY_DONE ;
}
2014-07-02 17:46:58 +02:00
static void __init at91_reset_status ( struct platform_device * pdev )
{
2018-03-16 11:06:26 +01:00
const char * reason ;
2014-07-02 17:46:58 +02:00
u32 reg = readl ( at91_rstc_base + AT91_RSTC_SR ) ;
switch ( ( reg & AT91_RSTC_RSTTYP ) > > 8 ) {
case RESET_TYPE_GENERAL :
reason = " general reset " ;
break ;
case RESET_TYPE_WAKEUP :
reason = " wakeup " ;
break ;
case RESET_TYPE_WATCHDOG :
reason = " watchdog reset " ;
break ;
case RESET_TYPE_SOFTWARE :
reason = " software reset " ;
break ;
case RESET_TYPE_USER :
reason = " user reset " ;
break ;
2019-02-06 19:12:21 +01:00
case RESET_TYPE_CPU_FAIL :
reason = " CPU clock failure detection " ;
break ;
case RESET_TYPE_XTAL_FAIL :
reason = " 32.768 kHz crystal failure detection " ;
break ;
case RESET_TYPE_ULP2 :
reason = " ULP2 reset " ;
break ;
2014-07-02 17:46:58 +02:00
default :
reason = " unknown reset " ;
break ;
}
2018-03-16 11:06:26 +01:00
dev_info ( & pdev - > dev , " Starting after %s \n " , reason ) ;
2014-07-02 17:46:58 +02:00
}
2015-03-16 20:17:12 +01:00
static const struct of_device_id at91_ramc_of_match [ ] = {
2014-07-02 17:46:58 +02:00
{ . compatible = " atmel,at91sam9260-sdramc " , } ,
{ . compatible = " atmel,at91sam9g45-ddramc " , } ,
{ /* sentinel */ }
} ;
2015-03-16 20:17:12 +01:00
static const struct of_device_id at91_reset_of_match [ ] = {
2014-07-02 17:46:58 +02:00
{ . compatible = " atmel,at91sam9260-rstc " , . data = at91sam9260_restart } ,
{ . compatible = " atmel,at91sam9g45-rstc " , . data = at91sam9g45_restart } ,
2015-07-20 17:32:05 +08:00
{ . compatible = " atmel,sama5d3-rstc " , . data = sama5d3_restart } ,
2017-01-18 00:07:37 +01:00
{ . compatible = " atmel,samx7-rstc " , . data = samx7_restart } ,
2019-02-06 19:12:21 +01:00
{ . compatible = " microchip,sam9x60-rstc " , . data = samx7_restart } ,
2014-07-02 17:46:58 +02:00
{ /* sentinel */ }
} ;
2016-10-17 15:36:12 -03:00
MODULE_DEVICE_TABLE ( of , at91_reset_of_match ) ;
2014-07-02 17:46:58 +02:00
2015-01-25 12:30:41 -08:00
static struct notifier_block at91_restart_nb = {
. priority = 192 ,
} ;
2015-08-11 11:12:47 +02:00
static int __init at91_reset_probe ( struct platform_device * pdev )
2014-07-02 17:46:58 +02:00
{
const struct of_device_id * match ;
struct device_node * np ;
2015-08-11 11:12:46 +02:00
int ret , idx = 0 ;
2014-07-02 17:46:58 +02:00
at91_rstc_base = of_iomap ( pdev - > dev . of_node , 0 ) ;
if ( ! at91_rstc_base ) {
dev_err ( & pdev - > dev , " Could not map reset controller address \n " ) ;
return - ENODEV ;
}
2015-07-20 17:32:05 +08:00
if ( ! of_device_is_compatible ( pdev - > dev . of_node , " atmel,sama5d3-rstc " ) ) {
/* we need to shutdown the ddr controller, so get ramc base */
for_each_matching_node ( np , at91_ramc_of_match ) {
at91_ramc_base [ idx ] = of_iomap ( np , 0 ) ;
if ( ! at91_ramc_base [ idx ] ) {
dev_err ( & pdev - > dev , " Could not map ram controller address \n " ) ;
2015-11-18 23:04:14 +01:00
of_node_put ( np ) ;
2015-07-20 17:32:05 +08:00
return - ENODEV ;
}
idx + + ;
2014-07-02 17:46:58 +02:00
}
}
match = of_match_node ( at91_reset_of_match , pdev - > dev . of_node ) ;
2015-01-25 12:30:41 -08:00
at91_restart_nb . notifier_call = match - > data ;
2014-07-02 17:46:58 +02:00
2015-08-11 11:12:48 +02:00
sclk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( sclk ) )
return PTR_ERR ( sclk ) ;
ret = clk_prepare_enable ( sclk ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " Could not enable slow clock \n " ) ;
return ret ;
}
2015-08-11 11:12:46 +02:00
ret = register_restart_handler ( & at91_restart_nb ) ;
2015-08-11 11:12:48 +02:00
if ( ret ) {
clk_disable_unprepare ( sclk ) ;
2014-07-02 17:46:58 +02:00
return ret ;
2015-08-11 11:12:48 +02:00
}
2014-07-02 17:46:58 +02:00
at91_reset_status ( pdev ) ;
return 0 ;
}
2015-08-11 11:12:47 +02:00
static int __exit at91_reset_remove ( struct platform_device * pdev )
{
unregister_restart_handler ( & at91_restart_nb ) ;
2015-08-11 11:12:48 +02:00
clk_disable_unprepare ( sclk ) ;
2015-08-11 11:12:47 +02:00
return 0 ;
}
2014-07-02 17:46:58 +02:00
static struct platform_driver at91_reset_driver = {
2015-08-11 11:12:47 +02:00
. remove = __exit_p ( at91_reset_remove ) ,
2014-07-02 17:46:58 +02:00
. driver = {
. name = " at91-reset " ,
. of_match_table = at91_reset_of_match ,
} ,
} ;
2015-08-11 11:12:47 +02:00
module_platform_driver_probe ( at91_reset_driver , at91_reset_probe ) ;
MODULE_AUTHOR ( " Atmel Corporation " ) ;
MODULE_DESCRIPTION ( " Reset driver for Atmel SoCs " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;