2014-05-22 12:20:38 +04:00
/*
* Copyright ( C ) 2014 Linaro Ltd .
*
* Author : Linus Walleij < linus . walleij @ linaro . org >
*
* 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/mfd/syscon.h>
# include <linux/reboot.h>
# include <linux/regmap.h>
# include <linux/of.h>
2014-12-19 18:56:27 +03:00
# define INTEGRATOR_HDR_CTRL_OFFSET 0x0C
# define INTEGRATOR_HDR_LOCK_OFFSET 0x14
# define INTEGRATOR_CM_CTRL_RESET (1 << 3)
2016-01-25 11:29:53 +03:00
# define VERSATILE_SYS_LOCK_OFFSET 0x20
# define VERSATILE_SYS_RESETCTL_OFFSET 0x40
2014-05-22 12:20:38 +04:00
2014-12-19 18:56:27 +03:00
/* Magic unlocking token used on all Versatile boards */
# define VERSATILE_LOCK_VAL 0xA05F
2014-05-22 12:20:38 +04:00
/*
* We detect the different syscon types from the compatible strings .
*/
enum versatile_reboot {
2014-12-19 18:56:27 +03:00
INTEGRATOR_REBOOT_CM ,
2016-01-25 11:29:53 +03:00
VERSATILE_REBOOT_CM ,
2014-05-22 12:20:38 +04:00
REALVIEW_REBOOT_EB ,
REALVIEW_REBOOT_PB1176 ,
REALVIEW_REBOOT_PB11MP ,
REALVIEW_REBOOT_PBA8 ,
REALVIEW_REBOOT_PBX ,
} ;
/* Pointer to the system controller */
static struct regmap * syscon_regmap ;
static enum versatile_reboot versatile_reboot_type ;
static const struct of_device_id versatile_reboot_of_match [ ] = {
2014-12-19 18:56:27 +03:00
{
. compatible = " arm,core-module-integrator " ,
. data = ( void * ) INTEGRATOR_REBOOT_CM
} ,
2016-01-25 11:29:53 +03:00
{
. compatible = " arm,core-module-versatile " ,
. data = ( void * ) VERSATILE_REBOOT_CM ,
} ,
2014-05-22 12:20:38 +04:00
{
. compatible = " arm,realview-eb-syscon " ,
. data = ( void * ) REALVIEW_REBOOT_EB ,
} ,
{
. compatible = " arm,realview-pb1176-syscon " ,
. data = ( void * ) REALVIEW_REBOOT_PB1176 ,
} ,
{
. compatible = " arm,realview-pb11mp-syscon " ,
. data = ( void * ) REALVIEW_REBOOT_PB11MP ,
} ,
{
. compatible = " arm,realview-pba8-syscon " ,
. data = ( void * ) REALVIEW_REBOOT_PBA8 ,
} ,
{
. compatible = " arm,realview-pbx-syscon " ,
. data = ( void * ) REALVIEW_REBOOT_PBX ,
} ,
2014-12-19 18:56:27 +03:00
{ } ,
2014-05-22 12:20:38 +04:00
} ;
2015-01-25 23:30:40 +03:00
static int versatile_reboot ( struct notifier_block * this , unsigned long mode ,
void * cmd )
2014-05-22 12:20:38 +04:00
{
/* Unlock the reset register */
/* Then hit reset on the different machines */
switch ( versatile_reboot_type ) {
2014-12-19 18:56:27 +03:00
case INTEGRATOR_REBOOT_CM :
regmap_write ( syscon_regmap , INTEGRATOR_HDR_LOCK_OFFSET ,
VERSATILE_LOCK_VAL ) ;
regmap_update_bits ( syscon_regmap ,
INTEGRATOR_HDR_CTRL_OFFSET ,
INTEGRATOR_CM_CTRL_RESET ,
INTEGRATOR_CM_CTRL_RESET ) ;
break ;
2016-01-25 11:29:53 +03:00
case VERSATILE_REBOOT_CM :
regmap_write ( syscon_regmap , VERSATILE_SYS_LOCK_OFFSET ,
VERSATILE_LOCK_VAL ) ;
regmap_update_bits ( syscon_regmap ,
VERSATILE_SYS_RESETCTL_OFFSET ,
0x0107 ,
0x0105 ) ;
regmap_write ( syscon_regmap , VERSATILE_SYS_LOCK_OFFSET ,
0 ) ;
break ;
2014-05-22 12:20:38 +04:00
case REALVIEW_REBOOT_EB :
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_LOCK_OFFSET ,
2014-12-19 18:56:27 +03:00
VERSATILE_LOCK_VAL ) ;
2014-05-22 12:20:38 +04:00
regmap_write ( syscon_regmap ,
2016-01-25 11:29:53 +03:00
VERSATILE_SYS_RESETCTL_OFFSET , 0x0008 ) ;
2014-05-22 12:20:38 +04:00
break ;
case REALVIEW_REBOOT_PB1176 :
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_LOCK_OFFSET ,
2014-12-19 18:56:27 +03:00
VERSATILE_LOCK_VAL ) ;
2014-05-22 12:20:38 +04:00
regmap_write ( syscon_regmap ,
2016-01-25 11:29:53 +03:00
VERSATILE_SYS_RESETCTL_OFFSET , 0x0100 ) ;
2014-05-22 12:20:38 +04:00
break ;
case REALVIEW_REBOOT_PB11MP :
case REALVIEW_REBOOT_PBA8 :
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_LOCK_OFFSET ,
2014-12-19 18:56:27 +03:00
VERSATILE_LOCK_VAL ) ;
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_RESETCTL_OFFSET ,
2014-05-22 12:20:38 +04:00
0x0000 ) ;
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_RESETCTL_OFFSET ,
2014-05-22 12:20:38 +04:00
0x0004 ) ;
break ;
case REALVIEW_REBOOT_PBX :
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_LOCK_OFFSET ,
2014-12-19 18:56:27 +03:00
VERSATILE_LOCK_VAL ) ;
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_RESETCTL_OFFSET ,
2014-05-22 12:20:38 +04:00
0x00f0 ) ;
2016-01-25 11:29:53 +03:00
regmap_write ( syscon_regmap , VERSATILE_SYS_RESETCTL_OFFSET ,
2014-05-22 12:20:38 +04:00
0x00f4 ) ;
break ;
}
dsb ( ) ;
2015-01-25 23:30:40 +03:00
return NOTIFY_DONE ;
2014-05-22 12:20:38 +04:00
}
2015-01-25 23:30:40 +03:00
static struct notifier_block versatile_reboot_nb = {
. notifier_call = versatile_reboot ,
. priority = 192 ,
} ;
2014-05-22 12:20:38 +04:00
static int __init versatile_reboot_probe ( void )
{
const struct of_device_id * reboot_id ;
struct device_node * np ;
2015-01-25 23:30:40 +03:00
int err ;
2014-05-22 12:20:38 +04:00
np = of_find_matching_node_and_match ( NULL , versatile_reboot_of_match ,
& reboot_id ) ;
if ( ! np )
return - ENODEV ;
versatile_reboot_type = ( enum versatile_reboot ) reboot_id - > data ;
syscon_regmap = syscon_node_to_regmap ( np ) ;
if ( IS_ERR ( syscon_regmap ) )
return PTR_ERR ( syscon_regmap ) ;
2015-01-25 23:30:40 +03:00
err = register_restart_handler ( & versatile_reboot_nb ) ;
if ( err )
return err ;
2014-05-22 12:20:38 +04:00
pr_info ( " versatile reboot driver registered \n " ) ;
return 0 ;
}
device_initcall ( versatile_reboot_probe ) ;