2014-05-24 00:32:39 +04:00
/*
* TI keystone reboot driver
*
* Copyright ( C ) 2014 Texas Instruments Incorporated . http : //www.ti.com/
*
* Author : Ivan Khoronzhuk < ivan . khoronzhuk @ ti . com >
*
* 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/io.h>
# include <linux/module.h>
2014-09-30 21:48:34 +04:00
# include <linux/notifier.h>
2014-05-24 00:32:39 +04:00
# include <linux/reboot.h>
# include <linux/regmap.h>
# include <linux/mfd/syscon.h>
# include <linux/of_platform.h>
# define RSTYPE_RG 0x0
# define RSCTRL_RG 0x4
# define RSCFG_RG 0x8
# define RSISO_RG 0xc
# define RSCTRL_KEY_MASK 0x0000ffff
# define RSCTRL_RESET_MASK BIT(16)
# define RSCTRL_KEY 0x5a69
# define RSMUX_OMODE_MASK 0xe
# define RSMUX_OMODE_RESET_ON 0xa
# define RSMUX_OMODE_RESET_OFF 0x0
# define RSMUX_LOCK_MASK 0x1
# define RSMUX_LOCK_SET 0x1
# define RSCFG_RSTYPE_SOFT 0x300f
# define RSCFG_RSTYPE_HARD 0x0
# define WDT_MUX_NUMBER 0x4
static int rspll_offset ;
static struct regmap * pllctrl_regs ;
/**
* rsctrl_enable_rspll_write - enable access to RSCTRL , RSCFG
* To be able to access to RSCTRL , RSCFG registers
* we have to write a key before
*/
static inline int rsctrl_enable_rspll_write ( void )
{
return regmap_update_bits ( pllctrl_regs , rspll_offset + RSCTRL_RG ,
RSCTRL_KEY_MASK , RSCTRL_KEY ) ;
}
2014-09-30 21:48:34 +04:00
static int rsctrl_restart_handler ( struct notifier_block * this ,
unsigned long mode , void * cmd )
2014-05-24 00:32:39 +04:00
{
/* enable write access to RSTCTRL */
rsctrl_enable_rspll_write ( ) ;
/* reset the SOC */
regmap_update_bits ( pllctrl_regs , rspll_offset + RSCTRL_RG ,
RSCTRL_RESET_MASK , 0 ) ;
2014-09-30 21:48:34 +04:00
return NOTIFY_DONE ;
2014-05-24 00:32:39 +04:00
}
2014-09-30 21:48:34 +04:00
static struct notifier_block rsctrl_restart_nb = {
. notifier_call = rsctrl_restart_handler ,
. priority = 128 ,
} ;
2015-03-16 22:17:12 +03:00
static const struct of_device_id rsctrl_of_match [ ] = {
2014-05-24 00:32:39 +04:00
{ . compatible = " ti,keystone-reset " , } ,
{ } ,
} ;
static int rsctrl_probe ( struct platform_device * pdev )
{
int i ;
int ret ;
u32 val ;
unsigned int rg ;
u32 rsmux_offset ;
struct regmap * devctrl_regs ;
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
if ( ! np )
return - ENODEV ;
/* get regmaps */
pllctrl_regs = syscon_regmap_lookup_by_phandle ( np , " ti,syscon-pll " ) ;
if ( IS_ERR ( pllctrl_regs ) )
return PTR_ERR ( pllctrl_regs ) ;
devctrl_regs = syscon_regmap_lookup_by_phandle ( np , " ti,syscon-dev " ) ;
if ( IS_ERR ( devctrl_regs ) )
return PTR_ERR ( devctrl_regs ) ;
ret = of_property_read_u32_index ( np , " ti,syscon-pll " , 1 , & rspll_offset ) ;
if ( ret ) {
dev_err ( dev , " couldn't read the reset pll offset! \n " ) ;
return - EINVAL ;
}
ret = of_property_read_u32_index ( np , " ti,syscon-dev " , 1 , & rsmux_offset ) ;
if ( ret ) {
dev_err ( dev , " couldn't read the rsmux offset! \n " ) ;
return - EINVAL ;
}
/* set soft/hard reset */
val = of_property_read_bool ( np , " ti,soft-reset " ) ;
val = val ? RSCFG_RSTYPE_SOFT : RSCFG_RSTYPE_HARD ;
ret = rsctrl_enable_rspll_write ( ) ;
if ( ret )
return ret ;
ret = regmap_write ( pllctrl_regs , rspll_offset + RSCFG_RG , val ) ;
if ( ret )
return ret ;
/* disable a reset isolation for all module clocks */
ret = regmap_write ( pllctrl_regs , rspll_offset + RSISO_RG , 0 ) ;
if ( ret )
return ret ;
/* enable a reset for watchdogs from wdt-list */
for ( i = 0 ; i < WDT_MUX_NUMBER ; i + + ) {
ret = of_property_read_u32_index ( np , " ti,wdt-list " , i , & val ) ;
if ( ret = = - EOVERFLOW & & ! i ) {
dev_err ( dev , " ti,wdt-list property has to contain at "
" least one entry \n " ) ;
return - EINVAL ;
} else if ( ret ) {
break ;
}
if ( val > = WDT_MUX_NUMBER ) {
2016-09-12 16:05:04 +03:00
dev_err ( dev , " ti,wdt-list property can contain "
2014-05-24 00:32:39 +04:00
" only numbers < 4 \n " ) ;
return - EINVAL ;
}
rg = rsmux_offset + val * 4 ;
ret = regmap_update_bits ( devctrl_regs , rg , RSMUX_OMODE_MASK ,
RSMUX_OMODE_RESET_ON |
RSMUX_LOCK_SET ) ;
if ( ret )
return ret ;
}
2014-09-30 21:48:34 +04:00
ret = register_restart_handler ( & rsctrl_restart_nb ) ;
if ( ret )
dev_err ( dev , " cannot register restart handler (err=%d) \n " , ret ) ;
return ret ;
2014-05-24 00:32:39 +04:00
}
static struct platform_driver rsctrl_driver = {
. probe = rsctrl_probe ,
. driver = {
. name = KBUILD_MODNAME ,
. of_match_table = rsctrl_of_match ,
} ,
} ;
module_platform_driver ( rsctrl_driver ) ;
MODULE_AUTHOR ( " Ivan Khoronzhuk <ivan.khoronzhuk@ti.com> " ) ;
MODULE_DESCRIPTION ( " Texas Instruments keystone reset driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform: " KBUILD_MODNAME ) ;