2019-05-27 08:55:06 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2015-03-11 17:37:18 -07:00
/*
* Generic Syscon Poweroff Driver
*
* Copyright ( c ) 2015 , National Instruments Corp .
* Author : Moritz Fischer < moritz . fischer @ ettus . com >
*/
# include <linux/delay.h>
# include <linux/io.h>
# include <linux/notifier.h>
# include <linux/mfd/syscon.h>
2023-07-18 08:30:43 -06:00
# include <linux/of.h>
2015-03-11 17:37:18 -07:00
# include <linux/platform_device.h>
# include <linux/pm.h>
2024-02-12 10:28:31 -06:00
# include <linux/reboot.h>
2015-03-11 17:37:18 -07:00
# include <linux/regmap.h>
2024-02-12 10:28:30 -06:00
struct syscon_poweroff_data {
struct regmap * map ;
u32 offset ;
u32 value ;
u32 mask ;
} ;
2024-02-12 10:28:31 -06:00
static int syscon_poweroff ( struct sys_off_data * off_data )
2015-03-11 17:37:18 -07:00
{
2024-02-12 10:28:31 -06:00
struct syscon_poweroff_data * data = off_data - > cb_data ;
2015-03-11 17:37:18 -07:00
/* Issue the poweroff */
2024-02-12 10:28:30 -06:00
regmap_update_bits ( data - > map , data - > offset , data - > mask , data - > value ) ;
2015-03-11 17:37:18 -07:00
mdelay ( 1000 ) ;
pr_emerg ( " Unable to poweroff system \n " ) ;
2024-02-12 10:28:31 -06:00
return NOTIFY_DONE ;
2015-03-11 17:37:18 -07:00
}
static int syscon_poweroff_probe ( struct platform_device * pdev )
{
2023-09-01 14:00:56 +02:00
struct device * dev = & pdev - > dev ;
2024-02-12 10:28:31 -06:00
struct syscon_poweroff_data * data ;
2017-02-07 17:56:05 +02:00
int mask_err , value_err ;
2015-03-11 17:37:18 -07:00
2024-02-12 10:28:30 -06:00
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > map = syscon_regmap_lookup_by_phandle ( dev - > of_node , " regmap " ) ;
if ( IS_ERR ( data - > map ) ) {
data - > map = syscon_node_to_regmap ( dev - > parent - > of_node ) ;
if ( IS_ERR ( data - > map ) ) {
2023-09-01 14:00:57 +02:00
dev_err ( dev , " unable to get syscon " ) ;
2024-02-12 10:28:30 -06:00
return PTR_ERR ( data - > map ) ;
2023-09-01 14:00:57 +02:00
}
2015-03-11 17:37:18 -07:00
}
2024-02-12 10:28:30 -06:00
if ( of_property_read_u32 ( dev - > of_node , " offset " , & data - > offset ) ) {
2023-09-01 14:00:56 +02:00
dev_err ( dev , " unable to read 'offset' " ) ;
2015-03-11 17:37:18 -07:00
return - EINVAL ;
}
2024-02-12 10:28:30 -06:00
value_err = of_property_read_u32 ( dev - > of_node , " value " , & data - > value ) ;
mask_err = of_property_read_u32 ( dev - > of_node , " mask " , & data - > mask ) ;
2017-02-07 17:56:05 +02:00
if ( value_err & & mask_err ) {
2023-09-01 14:00:56 +02:00
dev_err ( dev , " unable to read 'value' and 'mask' " ) ;
2015-03-11 17:37:18 -07:00
return - EINVAL ;
}
2017-02-07 17:56:05 +02:00
if ( value_err ) {
/* support old binding */
2024-02-12 10:28:30 -06:00
data - > value = data - > mask ;
data - > mask = 0xFFFFFFFF ;
2017-02-07 17:56:05 +02:00
} else if ( mask_err ) {
/* support value without mask*/
2024-02-12 10:28:30 -06:00
data - > mask = 0xFFFFFFFF ;
2017-02-07 17:56:05 +02:00
}
2024-02-12 10:28:31 -06:00
return devm_register_sys_off_handler ( & pdev - > dev ,
SYS_OFF_MODE_POWER_OFF ,
SYS_OFF_PRIO_DEFAULT ,
syscon_poweroff , data ) ;
2015-03-11 17:37:18 -07:00
}
static const struct of_device_id syscon_poweroff_of_match [ ] = {
{ . compatible = " syscon-poweroff " } ,
{ }
} ;
static struct platform_driver syscon_poweroff_driver = {
. probe = syscon_poweroff_probe ,
. driver = {
. name = " syscon-poweroff " ,
. of_match_table = syscon_poweroff_of_match ,
} ,
} ;
2023-08-07 21:19:51 +08:00
builtin_platform_driver ( syscon_poweroff_driver ) ;