2014-07-02 19:46:58 +04:00
/*
2015-09-10 13:35:13 +03:00
* Atmel AT91 SAM9 & SAMA5 SoCs reset code
2014-07-02 19:46:58 +04: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 12:12:48 +03:00
# include <linux/clk.h>
2014-07-02 19:46:58 +04: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 23:58:21 +03:00
# include <soc/at91/at91sam9_ddrsdr.h>
# include <soc/at91/at91sam9_sdramc.h>
2014-07-02 19:46:58 +04: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 21:12:21 +03:00
RESET_TYPE_CPU_FAIL = 6 ,
RESET_TYPE_XTAL_FAIL = 7 ,
RESET_TYPE_ULP2 = 8 ,
2014-07-02 19:46:58 +04:00
} ;
2020-01-21 13:03:32 +03:00
struct at91_reset_data {
int ( * notifier_call ) ( struct notifier_block * this , unsigned long mode ,
void * cmd ) ;
2020-01-21 13:03:33 +03:00
u32 args ;
2020-01-21 13:03:32 +03:00
} ;
2020-01-21 13:03:29 +03:00
struct at91_reset {
void __iomem * rstc_base ;
2020-01-21 13:03:30 +03:00
void __iomem * ramc_base [ 2 ] ;
2020-01-21 13:03:30 +03:00
struct clk * sclk ;
2020-01-21 13:03:31 +03:00
struct notifier_block nb ;
2020-01-21 13:03:33 +03:00
u32 args ;
2020-01-21 13:03:29 +03:00
} ;
2014-07-02 19:46:58 +04: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 23:30:41 +03:00
static int at91sam9260_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
2014-07-02 19:46:58 +04:00
{
2020-01-21 13:03:32 +03:00
struct at91_reset * reset = container_of ( this , struct at91_reset , nb ) ;
2014-07-02 19:46:58 +04: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 "
:
2020-01-21 13:03:31 +03:00
: " r " ( reset - > ramc_base [ 0 ] ) ,
" r " ( reset - > rstc_base ) ,
2014-07-02 19:46:58 +04:00
" r " ( 1 ) ,
2015-03-26 17:16:22 +03:00
" r " cpu_to_le32 ( AT91_SDRAMC_LPCB_POWER_DOWN ) ,
2020-01-21 13:03:33 +03:00
" r " ( reset - > args ) ) ;
2015-01-25 23:30:41 +03:00
return NOTIFY_DONE ;
2014-07-02 19:46:58 +04:00
}
2015-01-25 23:30:41 +03:00
static int at91sam9g45_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
2014-07-02 19:46:58 +04:00
{
2020-01-21 13:03:32 +03:00
struct at91_reset * reset = container_of ( this , struct at91_reset , nb ) ;
2014-07-02 19:46:58 +04: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 */
2020-01-21 13:03:33 +03:00
" ldr r4, [%1] \n \t "
" cmp r4, #0 \n \t "
2014-07-02 19:46:58 +04:00
/* 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 22:27:09 +04:00
" str %4, [%0, # " __stringify ( AT91_DDRSDRC_LPR ) " ] \n \t "
2014-07-02 19:46:58 +04:00
/* Disable SDRAM1 accesses */
" strne %3, [%1, # " __stringify ( AT91_DDRSDRC_RTR ) " ] \n \t "
/* Power down SDRAM1 */
2014-10-20 22:27:09 +04:00
" strne %4, [%1, # " __stringify ( AT91_DDRSDRC_LPR ) " ] \n \t "
2014-07-02 19:46:58 +04:00
/* Reset CPU */
" str %5, [%2, # " __stringify ( AT91_RSTC_CR ) " ] \n \t "
" b . \n \t "
:
2020-01-21 13:03:31 +03:00
: " r " ( reset - > ramc_base [ 0 ] ) ,
" r " ( reset - > ramc_base [ 1 ] ) ,
" r " ( reset - > rstc_base ) ,
2014-07-02 19:46:58 +04:00
" r " ( 1 ) ,
2015-03-26 17:16:22 +03:00
" r " cpu_to_le32 ( AT91_DDRSDRC_LPCB_POWER_DOWN ) ,
2020-01-21 13:03:33 +03:00
" r " ( reset - > args )
2020-01-21 13:03:33 +03:00
: " r4 " ) ;
2015-01-25 23:30:41 +03:00
return NOTIFY_DONE ;
2014-07-02 19:46:58 +04:00
}
2015-07-20 12:32:05 +03:00
static int sama5d3_restart ( struct notifier_block * this , unsigned long mode ,
void * cmd )
{
2020-01-21 13:03:32 +03:00
struct at91_reset * reset = container_of ( this , struct at91_reset , nb ) ;
2020-01-21 13:03:33 +03:00
writel ( reset - > args , reset - > rstc_base ) ;
2020-01-21 13:03:29 +03:00
2017-01-18 02:07:37 +03:00
return NOTIFY_DONE ;
}
2020-01-21 13:03:32 +03:00
static void __init at91_reset_status ( struct platform_device * pdev ,
void __iomem * base )
2014-07-02 19:46:58 +04:00
{
2018-03-16 13:06:26 +03:00
const char * reason ;
2020-01-21 13:03:32 +03:00
u32 reg = readl ( base + AT91_RSTC_SR ) ;
2014-07-02 19:46:58 +04:00
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 21:12:21 +03: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 19:46:58 +04:00
default :
reason = " unknown reset " ;
break ;
}
2018-03-16 13:06:26 +03:00
dev_info ( & pdev - > dev , " Starting after %s \n " , reason ) ;
2014-07-02 19:46:58 +04:00
}
2015-03-16 22:17:12 +03:00
static const struct of_device_id at91_ramc_of_match [ ] = {
2014-07-02 19:46:58 +04:00
{ . compatible = " atmel,at91sam9260-sdramc " , } ,
{ . compatible = " atmel,at91sam9g45-ddramc " , } ,
{ /* sentinel */ }
} ;
2020-01-21 13:03:32 +03:00
static const struct at91_reset_data at91sam9260_reset_data = {
. notifier_call = at91sam9260_restart ,
2020-01-21 13:03:33 +03:00
. args = AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST ,
2020-01-21 13:03:32 +03:00
} ;
static const struct at91_reset_data at91sam9g45_reset_data = {
. notifier_call = at91sam9g45_restart ,
2020-01-21 13:03:33 +03:00
. args = AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST ,
2020-01-21 13:03:32 +03:00
} ;
static const struct at91_reset_data sama5d3_reset_data = {
. notifier_call = sama5d3_restart ,
2020-01-21 13:03:33 +03:00
. args = AT91_RSTC_KEY | AT91_RSTC_PERRST | AT91_RSTC_PROCRST ,
2020-01-21 13:03:32 +03:00
} ;
static const struct at91_reset_data samx7_reset_data = {
2020-01-21 13:03:33 +03:00
. notifier_call = sama5d3_restart ,
. args = AT91_RSTC_KEY | AT91_RSTC_PROCRST ,
2020-01-21 13:03:32 +03:00
} ;
2015-03-16 22:17:12 +03:00
static const struct of_device_id at91_reset_of_match [ ] = {
2020-01-21 13:03:32 +03:00
{
. compatible = " atmel,at91sam9260-rstc " ,
. data = & at91sam9260_reset_data
} ,
{
. compatible = " atmel,at91sam9g45-rstc " ,
. data = & at91sam9g45_reset_data
} ,
{
. compatible = " atmel,sama5d3-rstc " ,
. data = & sama5d3_reset_data
} ,
{
. compatible = " atmel,samx7-rstc " ,
. data = & samx7_reset_data
} ,
{
. compatible = " microchip,sam9x60-rstc " ,
. data = & samx7_reset_data
} ,
2014-07-02 19:46:58 +04:00
{ /* sentinel */ }
} ;
2016-10-17 21:36:12 +03:00
MODULE_DEVICE_TABLE ( of , at91_reset_of_match ) ;
2014-07-02 19:46:58 +04:00
2015-08-11 12:12:47 +03:00
static int __init at91_reset_probe ( struct platform_device * pdev )
2014-07-02 19:46:58 +04:00
{
2020-01-21 13:03:32 +03:00
const struct at91_reset_data * reset_data ;
2014-07-02 19:46:58 +04:00
const struct of_device_id * match ;
2020-01-21 13:03:32 +03:00
struct at91_reset * reset ;
2014-07-02 19:46:58 +04:00
struct device_node * np ;
2015-08-11 12:12:46 +03:00
int ret , idx = 0 ;
2014-07-02 19:46:58 +04:00
2020-01-21 13:03:31 +03:00
reset = devm_kzalloc ( & pdev - > dev , sizeof ( * reset ) , GFP_KERNEL ) ;
if ( ! reset )
return - ENOMEM ;
reset - > rstc_base = of_iomap ( pdev - > dev . of_node , 0 ) ;
if ( ! reset - > rstc_base ) {
2014-07-02 19:46:58 +04:00
dev_err ( & pdev - > dev , " Could not map reset controller address \n " ) ;
return - ENODEV ;
}
2015-07-20 12:32:05 +03: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 ) {
2020-01-21 13:03:31 +03:00
reset - > ramc_base [ idx ] = of_iomap ( np , 0 ) ;
if ( ! reset - > ramc_base [ idx ] ) {
2015-07-20 12:32:05 +03:00
dev_err ( & pdev - > dev , " Could not map ram controller address \n " ) ;
2015-11-19 01:04:14 +03:00
of_node_put ( np ) ;
2015-07-20 12:32:05 +03:00
return - ENODEV ;
}
idx + + ;
2014-07-02 19:46:58 +04:00
}
}
match = of_match_node ( at91_reset_of_match , pdev - > dev . of_node ) ;
2020-01-21 13:03:32 +03:00
reset_data = match - > data ;
reset - > nb . notifier_call = reset_data - > notifier_call ;
2020-01-21 13:03:31 +03:00
reset - > nb . priority = 192 ;
2020-01-21 13:03:33 +03:00
reset - > args = reset_data - > args ;
2014-07-02 19:46:58 +04:00
2020-01-21 13:03:31 +03:00
reset - > sclk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( reset - > sclk ) )
return PTR_ERR ( reset - > sclk ) ;
2015-08-11 12:12:48 +03:00
2020-01-21 13:03:31 +03:00
ret = clk_prepare_enable ( reset - > sclk ) ;
2015-08-11 12:12:48 +03:00
if ( ret ) {
dev_err ( & pdev - > dev , " Could not enable slow clock \n " ) ;
return ret ;
}
2020-01-21 13:03:32 +03:00
platform_set_drvdata ( pdev , reset ) ;
2020-01-21 13:03:31 +03:00
ret = register_restart_handler ( & reset - > nb ) ;
2015-08-11 12:12:48 +03:00
if ( ret ) {
2020-01-21 13:03:31 +03:00
clk_disable_unprepare ( reset - > sclk ) ;
2014-07-02 19:46:58 +04:00
return ret ;
2015-08-11 12:12:48 +03:00
}
2014-07-02 19:46:58 +04:00
2020-01-21 13:03:32 +03:00
at91_reset_status ( pdev , reset - > rstc_base ) ;
2014-07-02 19:46:58 +04:00
return 0 ;
}
2015-08-11 12:12:47 +03:00
static int __exit at91_reset_remove ( struct platform_device * pdev )
{
2020-01-21 13:03:32 +03:00
struct at91_reset * reset = platform_get_drvdata ( pdev ) ;
2020-01-21 13:03:31 +03:00
unregister_restart_handler ( & reset - > nb ) ;
clk_disable_unprepare ( reset - > sclk ) ;
2015-08-11 12:12:47 +03:00
return 0 ;
}
2014-07-02 19:46:58 +04:00
static struct platform_driver at91_reset_driver = {
2015-08-11 12:12:47 +03:00
. remove = __exit_p ( at91_reset_remove ) ,
2014-07-02 19:46:58 +04:00
. driver = {
. name = " at91-reset " ,
. of_match_table = at91_reset_of_match ,
} ,
} ;
2015-08-11 12:12:47 +03: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 " ) ;