2015-08-03 20:23:52 +03:00
/*
2017-02-08 03:18:44 +03:00
* AR71xx Reset Controller Driver
* Author : Alban Bedel
*
2015-08-03 20:23:52 +03:00
* Copyright ( C ) 2015 Alban Bedel < albeu @ free . fr >
*
* 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 .
*/
2016-08-09 12:17:22 +03:00
# include <linux/io.h>
2017-02-08 03:18:44 +03:00
# include <linux/init.h>
2015-08-03 20:23:52 +03:00
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
2015-11-24 03:00:33 +03:00
# include <linux/reboot.h>
2015-08-03 20:23:52 +03:00
struct ath79_reset {
struct reset_controller_dev rcdev ;
2015-11-24 03:00:33 +03:00
struct notifier_block restart_nb ;
2015-08-03 20:23:52 +03:00
void __iomem * base ;
spinlock_t lock ;
} ;
2015-11-24 03:00:33 +03:00
# define FULL_CHIP_RESET 24
2015-08-03 20:23:52 +03:00
static int ath79_reset_update ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
struct ath79_reset * ath79_reset =
container_of ( rcdev , struct ath79_reset , rcdev ) ;
unsigned long flags ;
u32 val ;
spin_lock_irqsave ( & ath79_reset - > lock , flags ) ;
val = readl ( ath79_reset - > base ) ;
if ( assert )
val | = BIT ( id ) ;
else
val & = ~ BIT ( id ) ;
writel ( val , ath79_reset - > base ) ;
spin_unlock_irqrestore ( & ath79_reset - > lock , flags ) ;
return 0 ;
}
static int ath79_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return ath79_reset_update ( rcdev , id , true ) ;
}
static int ath79_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return ath79_reset_update ( rcdev , id , false ) ;
}
static int ath79_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct ath79_reset * ath79_reset =
container_of ( rcdev , struct ath79_reset , rcdev ) ;
u32 val ;
val = readl ( ath79_reset - > base ) ;
return ! ! ( val & BIT ( id ) ) ;
}
2016-01-17 17:12:23 +03:00
static const struct reset_control_ops ath79_reset_ops = {
2015-08-03 20:23:52 +03:00
. assert = ath79_reset_assert ,
. deassert = ath79_reset_deassert ,
. status = ath79_reset_status ,
} ;
2015-11-24 03:00:33 +03:00
static int ath79_reset_restart_handler ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct ath79_reset * ath79_reset =
container_of ( nb , struct ath79_reset , restart_nb ) ;
ath79_reset_assert ( & ath79_reset - > rcdev , FULL_CHIP_RESET ) ;
return NOTIFY_DONE ;
}
2015-08-03 20:23:52 +03:00
static int ath79_reset_probe ( struct platform_device * pdev )
{
struct ath79_reset * ath79_reset ;
struct resource * res ;
2015-11-24 03:00:33 +03:00
int err ;
2015-08-03 20:23:52 +03:00
ath79_reset = devm_kzalloc ( & pdev - > dev ,
sizeof ( * ath79_reset ) , GFP_KERNEL ) ;
if ( ! ath79_reset )
return - ENOMEM ;
platform_set_drvdata ( pdev , ath79_reset ) ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
ath79_reset - > base = devm_ioremap_resource ( & pdev - > dev , res ) ;
if ( IS_ERR ( ath79_reset - > base ) )
return PTR_ERR ( ath79_reset - > base ) ;
2015-09-01 15:15:10 +03:00
spin_lock_init ( & ath79_reset - > lock ) ;
2015-08-03 20:23:52 +03:00
ath79_reset - > rcdev . ops = & ath79_reset_ops ;
ath79_reset - > rcdev . owner = THIS_MODULE ;
ath79_reset - > rcdev . of_node = pdev - > dev . of_node ;
ath79_reset - > rcdev . of_reset_n_cells = 1 ;
ath79_reset - > rcdev . nr_resets = 32 ;
2016-05-01 13:36:58 +03:00
err = devm_reset_controller_register ( & pdev - > dev , & ath79_reset - > rcdev ) ;
2015-11-24 03:00:33 +03:00
if ( err )
return err ;
ath79_reset - > restart_nb . notifier_call = ath79_reset_restart_handler ;
ath79_reset - > restart_nb . priority = 128 ;
err = register_restart_handler ( & ath79_reset - > restart_nb ) ;
if ( err )
dev_warn ( & pdev - > dev , " Failed to register restart handler \n " ) ;
return 0 ;
2015-08-03 20:23:52 +03:00
}
static const struct of_device_id ath79_reset_dt_ids [ ] = {
{ . compatible = " qca,ar7100-reset " , } ,
{ } ,
} ;
static struct platform_driver ath79_reset_driver = {
. probe = ath79_reset_probe ,
. driver = {
2017-02-08 03:18:44 +03:00
. name = " ath79-reset " ,
. of_match_table = ath79_reset_dt_ids ,
. suppress_bind_attrs = true ,
2015-08-03 20:23:52 +03:00
} ,
} ;
2017-02-08 03:18:44 +03:00
builtin_platform_driver ( ath79_reset_driver ) ;