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 */
2020-01-21 10:03:39 +00:00
# define AT91_RSTC_URSTASYNC BIT(2) /* User Reset Asynchronous Control */
2014-07-02 17:46:58 +02:00
# 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
} ;
2020-01-21 10:03:29 +00:00
struct at91_reset {
void __iomem * rstc_base ;
2020-01-21 10:03:30 +00:00
void __iomem * ramc_base [ 2 ] ;
2020-01-21 10:03:30 +00:00
struct clk * sclk ;
2020-01-21 10:03:31 +00:00
struct notifier_block nb ;
2020-01-21 10:03:33 +00:00
u32 args ;
2020-01-21 10:03:34 +00:00
u32 ramc_lpr ;
2020-01-21 10:03:29 +00:00
} ;
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
*/
2020-01-21 10:03:38 +00:00
static int at91_reset ( struct notifier_block * this , unsigned long mode ,
void * cmd )
2014-07-02 17:46:58 +02:00
{
2020-01-21 10:03:32 +00:00
struct at91_reset * reset = container_of ( this , struct at91_reset , nb ) ;
2014-07-02 17:46:58 +02:00
asm volatile (
/* Align to cache lines */
" .balign 32 \n \t "
/* Disable SDRAM0 accesses */
2020-01-21 10:03:37 +00:00
" tst %0, #0 \n \t "
" beq 1f \n \t "
" str %3, [%0, # " __stringify ( AT91_DDRSDRC_RTR ) " ] \n \t "
2014-07-02 17:46:58 +02:00
/* Power down SDRAM0 */
2020-01-21 10:03:34 +00:00
" str %4, [%0, %6] \n \t "
2014-07-02 17:46:58 +02:00
/* Disable SDRAM1 accesses */
2020-01-21 10:03:37 +00:00
" 1: tst %1, #0 \n \t "
" beq 2f \n \t "
2014-07-02 17:46:58 +02:00
" strne %3, [%1, # " __stringify ( AT91_DDRSDRC_RTR ) " ] \n \t "
/* Power down SDRAM1 */
2020-01-21 10:03:34 +00:00
" strne %4, [%1, %6] \n \t "
2014-07-02 17:46:58 +02:00
/* Reset CPU */
2020-01-21 10:03:37 +00:00
" 2: str %5, [%2, # " __stringify ( AT91_RSTC_CR ) " ] \n \t "
2014-07-02 17:46:58 +02:00
" b . \n \t "
:
2020-01-21 10:03:31 +00:00
: " r " ( reset - > ramc_base [ 0 ] ) ,
" r " ( reset - > ramc_base [ 1 ] ) ,
" r " ( reset - > rstc_base ) ,
2014-07-02 17:46:58 +02:00
" r " ( 1 ) ,
2015-03-26 14:16:22 +00:00
" r " cpu_to_le32 ( AT91_DDRSDRC_LPCB_POWER_DOWN ) ,
2020-01-21 10:03:34 +00:00
" r " ( reset - > args ) ,
" r " ( reset - > ramc_lpr )
2020-01-21 10:03:33 +00:00
: " r4 " ) ;
2015-01-25 12:30:41 -08:00
return NOTIFY_DONE ;
2014-07-02 17:46:58 +02:00
}
2020-01-21 10:03:32 +00:00
static void __init at91_reset_status ( struct platform_device * pdev ,
void __iomem * base )
2014-07-02 17:46:58 +02:00
{
2018-03-16 11:06:26 +01:00
const char * reason ;
2020-01-21 10:03:32 +00:00
u32 reg = readl ( base + AT91_RSTC_SR ) ;
2014-07-02 17:46:58 +02: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 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 [ ] = {
2020-01-21 10:03:34 +00:00
{
. compatible = " atmel,at91sam9260-sdramc " ,
. data = ( void * ) AT91_SDRAMC_LPR ,
} ,
{
. compatible = " atmel,at91sam9g45-ddramc " ,
. data = ( void * ) AT91_DDRSDRC_LPR ,
} ,
2014-07-02 17:46:58 +02:00
{ /* sentinel */ }
} ;
2015-03-16 20:17:12 +01:00
static const struct of_device_id at91_reset_of_match [ ] = {
2020-01-21 10:03:32 +00:00
{
. compatible = " atmel,at91sam9260-rstc " ,
2020-01-21 10:03:39 +00:00
. data = ( void * ) ( AT91_RSTC_KEY | AT91_RSTC_PERRST |
AT91_RSTC_PROCRST ) ,
2020-01-21 10:03:32 +00:00
} ,
{
. compatible = " atmel,at91sam9g45-rstc " ,
2020-01-21 10:03:39 +00:00
. data = ( void * ) ( AT91_RSTC_KEY | AT91_RSTC_PERRST |
AT91_RSTC_PROCRST )
2020-01-21 10:03:32 +00:00
} ,
{
. compatible = " atmel,sama5d3-rstc " ,
2020-01-21 10:03:39 +00:00
. data = ( void * ) ( AT91_RSTC_KEY | AT91_RSTC_PERRST |
AT91_RSTC_PROCRST )
2020-01-21 10:03:32 +00:00
} ,
{
. compatible = " atmel,samx7-rstc " ,
2020-01-21 10:03:39 +00:00
. data = ( void * ) ( AT91_RSTC_KEY | AT91_RSTC_PROCRST )
2020-01-21 10:03:32 +00:00
} ,
{
. compatible = " microchip,sam9x60-rstc " ,
2020-01-21 10:03:39 +00:00
. data = ( void * ) ( AT91_RSTC_KEY | AT91_RSTC_PROCRST )
2020-01-21 10:03:32 +00:00
} ,
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-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 ;
2020-01-21 10:03:32 +00:00
struct at91_reset * reset ;
2014-07-02 17:46:58 +02:00
struct device_node * np ;
2015-08-11 11:12:46 +02:00
int ret , idx = 0 ;
2014-07-02 17:46:58 +02:00
2020-01-21 10:03:31 +00:00
reset = devm_kzalloc ( & pdev - > dev , sizeof ( * reset ) , GFP_KERNEL ) ;
if ( ! reset )
return - ENOMEM ;
2021-04-02 13:50:18 +03:00
reset - > rstc_base = devm_of_iomap ( & pdev - > dev , pdev - > dev . of_node , 0 , NULL ) ;
2021-09-30 13:09:28 +03:00
if ( IS_ERR ( reset - > rstc_base ) ) {
2014-07-02 17:46:58 +02:00
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 */
2020-01-21 10:03:34 +00:00
for_each_matching_node_and_match ( np , at91_ramc_of_match , & match ) {
reset - > ramc_lpr = ( u32 ) match - > data ;
2021-04-02 13:50:18 +03:00
reset - > ramc_base [ idx ] = devm_of_iomap ( & pdev - > dev , np , 0 , NULL ) ;
2021-09-30 13:09:28 +03:00
if ( IS_ERR ( reset - > ramc_base [ idx ] ) ) {
2015-07-20 17:32:05 +08:00
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 ) ;
2020-01-21 10:03:38 +00:00
reset - > nb . notifier_call = at91_reset ;
2020-01-21 10:03:31 +00:00
reset - > nb . priority = 192 ;
2020-01-21 10:03:39 +00:00
reset - > args = ( u32 ) match - > data ;
2014-07-02 17:46:58 +02:00
2020-01-21 10:03:31 +00:00
reset - > sclk = devm_clk_get ( & pdev - > dev , NULL ) ;
if ( IS_ERR ( reset - > sclk ) )
return PTR_ERR ( reset - > sclk ) ;
2015-08-11 11:12:48 +02:00
2020-01-21 10:03:31 +00:00
ret = clk_prepare_enable ( reset - > sclk ) ;
2015-08-11 11:12:48 +02:00
if ( ret ) {
dev_err ( & pdev - > dev , " Could not enable slow clock \n " ) ;
return ret ;
}
2020-01-21 10:03:32 +00:00
platform_set_drvdata ( pdev , reset ) ;
2020-01-21 10:03:39 +00:00
if ( of_device_is_compatible ( pdev - > dev . of_node , " microchip,sam9x60-rstc " ) ) {
u32 val = readl ( reset - > rstc_base + AT91_RSTC_MR ) ;
writel ( AT91_RSTC_KEY | AT91_RSTC_URSTASYNC | val ,
reset - > rstc_base + AT91_RSTC_MR ) ;
}
2020-01-21 10:03:31 +00:00
ret = register_restart_handler ( & reset - > nb ) ;
2015-08-11 11:12:48 +02:00
if ( ret ) {
2020-01-21 10:03:31 +00:00
clk_disable_unprepare ( reset - > 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
2020-01-21 10:03:32 +00:00
at91_reset_status ( pdev , reset - > rstc_base ) ;
2014-07-02 17:46:58 +02:00
return 0 ;
}
2015-08-11 11:12:47 +02:00
static int __exit at91_reset_remove ( struct platform_device * pdev )
{
2020-01-21 10:03:32 +00:00
struct at91_reset * reset = platform_get_drvdata ( pdev ) ;
2020-01-21 10:03:31 +00:00
unregister_restart_handler ( & reset - > nb ) ;
clk_disable_unprepare ( reset - > 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 " ) ;