2016-04-23 10:40:28 +03:00
/*
* Hisilicon Reset Controller Driver
*
* Copyright ( c ) 2015 - 2016 HiSilicon Technologies Co . , Ltd .
*
* 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 .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/io.h>
# include <linux/of_address.h>
2016-06-15 09:26:34 +03:00
# include <linux/platform_device.h>
2016-04-23 10:40:28 +03:00
# include <linux/reset-controller.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include "reset.h"
# define HISI_RESET_BIT_MASK 0x1f
# define HISI_RESET_OFFSET_SHIFT 8
# define HISI_RESET_OFFSET_MASK 0xffff00
struct hisi_reset_controller {
spinlock_t lock ;
void __iomem * membase ;
struct reset_controller_dev rcdev ;
} ;
# define to_hisi_reset_controller(rcdev) \
container_of ( rcdev , struct hisi_reset_controller , rcdev )
static int hisi_reset_of_xlate ( struct reset_controller_dev * rcdev ,
const struct of_phandle_args * reset_spec )
{
u32 offset ;
u8 bit ;
offset = ( reset_spec - > args [ 0 ] < < HISI_RESET_OFFSET_SHIFT )
& HISI_RESET_OFFSET_MASK ;
bit = reset_spec - > args [ 1 ] & HISI_RESET_BIT_MASK ;
return ( offset | bit ) ;
}
static int hisi_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct hisi_reset_controller * rstc = to_hisi_reset_controller ( rcdev ) ;
unsigned long flags ;
u32 offset , reg ;
u8 bit ;
offset = ( id & HISI_RESET_OFFSET_MASK ) > > HISI_RESET_OFFSET_SHIFT ;
bit = id & HISI_RESET_BIT_MASK ;
spin_lock_irqsave ( & rstc - > lock , flags ) ;
reg = readl ( rstc - > membase + offset ) ;
writel ( reg | BIT ( bit ) , rstc - > membase + offset ) ;
spin_unlock_irqrestore ( & rstc - > lock , flags ) ;
return 0 ;
}
static int hisi_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct hisi_reset_controller * rstc = to_hisi_reset_controller ( rcdev ) ;
unsigned long flags ;
u32 offset , reg ;
u8 bit ;
offset = ( id & HISI_RESET_OFFSET_MASK ) > > HISI_RESET_OFFSET_SHIFT ;
bit = id & HISI_RESET_BIT_MASK ;
spin_lock_irqsave ( & rstc - > lock , flags ) ;
reg = readl ( rstc - > membase + offset ) ;
writel ( reg & ~ BIT ( bit ) , rstc - > membase + offset ) ;
spin_unlock_irqrestore ( & rstc - > lock , flags ) ;
return 0 ;
}
static const struct reset_control_ops hisi_reset_ops = {
. assert = hisi_reset_assert ,
. deassert = hisi_reset_deassert ,
} ;
2016-06-15 09:26:34 +03:00
struct hisi_reset_controller * hisi_reset_init ( struct platform_device * pdev )
2016-04-23 10:40:28 +03:00
{
struct hisi_reset_controller * rstc ;
2016-06-15 09:26:34 +03:00
struct resource * res ;
2016-04-23 10:40:28 +03:00
2016-06-15 09:26:34 +03:00
rstc = devm_kmalloc ( & pdev - > dev , sizeof ( * rstc ) , GFP_KERNEL ) ;
2016-04-23 10:40:28 +03:00
if ( ! rstc )
return NULL ;
2016-06-15 09:26:34 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
rstc - > membase = devm_ioremap ( & pdev - > dev ,
res - > start , resource_size ( res ) ) ;
if ( ! rstc - > membase )
2016-04-23 10:40:28 +03:00
return NULL ;
spin_lock_init ( & rstc - > lock ) ;
rstc - > rcdev . owner = THIS_MODULE ;
rstc - > rcdev . ops = & hisi_reset_ops ;
2016-06-15 09:26:34 +03:00
rstc - > rcdev . of_node = pdev - > dev . of_node ;
2016-04-23 10:40:28 +03:00
rstc - > rcdev . of_reset_n_cells = 2 ;
rstc - > rcdev . of_xlate = hisi_reset_of_xlate ;
reset_controller_register ( & rstc - > rcdev ) ;
return rstc ;
}
EXPORT_SYMBOL_GPL ( hisi_reset_init ) ;
void hisi_reset_exit ( struct hisi_reset_controller * rstc )
{
reset_controller_unregister ( & rstc - > rcdev ) ;
}
EXPORT_SYMBOL_GPL ( hisi_reset_exit ) ;