2014-09-30 16:25:03 -07:00
/*
* Generic Syscon Reboot Driver
*
* Copyright ( c ) 2013 , Applied Micro Circuits Corporation
* Author : Feng Kan < fkan @ apm . com >
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation ; either version 2 of
* the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
2014-10-02 11:24:15 -07:00
# include <linux/delay.h>
2014-09-30 16:25:03 -07:00
# include <linux/io.h>
# include <linux/notifier.h>
# include <linux/mfd/syscon.h>
2014-10-02 11:24:15 -07:00
# include <linux/of_address.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
2014-09-30 16:25:03 -07:00
# include <linux/reboot.h>
2014-10-02 11:24:15 -07:00
# include <linux/regmap.h>
2014-09-30 16:25:03 -07:00
struct syscon_reboot_context {
struct regmap * map ;
u32 offset ;
2019-03-08 08:49:30 +01:00
u32 value ;
2014-09-30 16:25:03 -07:00
u32 mask ;
struct notifier_block restart_handler ;
} ;
static int syscon_restart_handle ( struct notifier_block * this ,
unsigned long mode , void * cmd )
{
2014-10-02 11:24:15 -07:00
struct syscon_reboot_context * ctx =
container_of ( this , struct syscon_reboot_context ,
restart_handler ) ;
2014-09-30 16:25:03 -07:00
/* Issue the reboot */
2019-03-08 08:49:30 +01:00
regmap_update_bits ( ctx - > map , ctx - > offset , ctx - > mask , ctx - > value ) ;
2014-09-30 16:25:03 -07:00
2014-10-02 11:24:15 -07:00
mdelay ( 1000 ) ;
2014-09-30 16:25:03 -07:00
pr_emerg ( " Unable to restart system \n " ) ;
return NOTIFY_DONE ;
}
static int syscon_reboot_probe ( struct platform_device * pdev )
{
struct syscon_reboot_context * ctx ;
struct device * dev = & pdev - > dev ;
2019-03-08 08:49:30 +01:00
int mask_err , value_err ;
2014-09-30 16:25:03 -07:00
int err ;
ctx = devm_kzalloc ( & pdev - > dev , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
ctx - > map = syscon_regmap_lookup_by_phandle ( dev - > of_node , " regmap " ) ;
if ( IS_ERR ( ctx - > map ) )
return PTR_ERR ( ctx - > map ) ;
if ( of_property_read_u32 ( pdev - > dev . of_node , " offset " , & ctx - > offset ) )
return - EINVAL ;
2019-03-08 08:49:30 +01:00
value_err = of_property_read_u32 ( pdev - > dev . of_node , " value " , & ctx - > value ) ;
mask_err = of_property_read_u32 ( pdev - > dev . of_node , " mask " , & ctx - > mask ) ;
if ( value_err & & mask_err ) {
dev_err ( dev , " unable to read 'value' and 'mask' " ) ;
2014-09-30 16:25:03 -07:00
return - EINVAL ;
2019-03-08 08:49:30 +01:00
}
if ( value_err ) {
/* support old binding */
ctx - > value = ctx - > mask ;
ctx - > mask = 0xFFFFFFFF ;
} else if ( mask_err ) {
/* support value without mask*/
ctx - > mask = 0xFFFFFFFF ;
}
2014-09-30 16:25:03 -07:00
ctx - > restart_handler . notifier_call = syscon_restart_handle ;
2014-12-02 18:11:58 +01:00
ctx - > restart_handler . priority = 192 ;
2014-09-30 16:25:03 -07:00
err = register_restart_handler ( & ctx - > restart_handler ) ;
if ( err )
dev_err ( dev , " can't register restart notifier (err=%d) \n " , err ) ;
2014-10-02 11:24:15 -07:00
return err ;
2014-09-30 16:25:03 -07:00
}
2015-03-16 20:17:12 +01:00
static const struct of_device_id syscon_reboot_of_match [ ] = {
2014-09-30 16:25:03 -07:00
{ . compatible = " syscon-reboot " } ,
{ }
} ;
static struct platform_driver syscon_reboot_driver = {
. probe = syscon_reboot_probe ,
. driver = {
. name = " syscon-reboot " ,
. of_match_table = syscon_reboot_of_match ,
} ,
} ;
2015-05-01 20:10:58 -04:00
builtin_platform_driver ( syscon_reboot_driver ) ;