2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-08-11 13:58:43 +03:00
/*
* Simple Reset Controller Driver
*
* Copyright ( C ) 2017 Pengutronix , Philipp Zabel < kernel @ pengutronix . de >
*
* Based on Allwinner SoCs Reset Controller driver
*
* Copyright 2013 Maxime Ripard
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*/
2020-05-27 18:47:32 +03:00
# include <linux/delay.h>
2017-08-11 13:58:43 +03:00
# include <linux/device.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
2020-05-27 18:47:31 +03:00
# include <linux/reset/reset-simple.h>
2017-08-11 13:58:43 +03:00
# include <linux/spinlock.h>
static inline struct reset_simple_data *
to_reset_simple_data ( struct reset_controller_dev * rcdev )
{
return container_of ( rcdev , struct reset_simple_data , rcdev ) ;
}
static int reset_simple_update ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
struct reset_simple_data * data = to_reset_simple_data ( rcdev ) ;
int reg_width = sizeof ( u32 ) ;
int bank = id / ( reg_width * BITS_PER_BYTE ) ;
int offset = id % ( reg_width * BITS_PER_BYTE ) ;
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & data - > lock , flags ) ;
reg = readl ( data - > membase + ( bank * reg_width ) ) ;
if ( assert ^ data - > active_low )
reg | = BIT ( offset ) ;
else
reg & = ~ BIT ( offset ) ;
writel ( reg , data - > membase + ( bank * reg_width ) ) ;
spin_unlock_irqrestore ( & data - > lock , flags ) ;
return 0 ;
}
static int reset_simple_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return reset_simple_update ( rcdev , id , true ) ;
}
static int reset_simple_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return reset_simple_update ( rcdev , id , false ) ;
}
2020-05-27 18:47:32 +03:00
static int reset_simple_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct reset_simple_data * data = to_reset_simple_data ( rcdev ) ;
int ret ;
if ( ! data - > reset_us )
return - ENOTSUPP ;
ret = reset_simple_assert ( rcdev , id ) ;
if ( ret )
return ret ;
usleep_range ( data - > reset_us , data - > reset_us * 2 ) ;
return reset_simple_deassert ( rcdev , id ) ;
}
2017-08-11 14:02:19 +03:00
static int reset_simple_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct reset_simple_data * data = to_reset_simple_data ( rcdev ) ;
int reg_width = sizeof ( u32 ) ;
int bank = id / ( reg_width * BITS_PER_BYTE ) ;
int offset = id % ( reg_width * BITS_PER_BYTE ) ;
u32 reg ;
reg = readl ( data - > membase + ( bank * reg_width ) ) ;
return ! ( reg & BIT ( offset ) ) ^ ! data - > status_active_low ;
}
2017-08-11 13:58:43 +03:00
const struct reset_control_ops reset_simple_ops = {
. assert = reset_simple_assert ,
. deassert = reset_simple_deassert ,
2020-05-27 18:47:32 +03:00
. reset = reset_simple_reset ,
2017-08-11 14:02:19 +03:00
. status = reset_simple_status ,
2017-08-11 13:58:43 +03:00
} ;
2018-07-04 13:13:56 +03:00
EXPORT_SYMBOL_GPL ( reset_simple_ops ) ;
2017-08-11 13:58:43 +03:00
/**
* struct reset_simple_devdata - simple reset controller properties
2017-08-11 14:02:19 +03:00
* @ reg_offset : offset between base address and first reset register .
* @ nr_resets : number of resets . If not set , default to resource size in bits .
2017-08-11 13:58:43 +03:00
* @ active_low : if true , bits are cleared to assert the reset . Otherwise , bits
* are set to assert the reset .
2017-08-11 14:02:19 +03:00
* @ status_active_low : if true , bits read back as cleared while the reset is
* asserted . Otherwise , bits read back as set while the
* reset is asserted .
2017-08-11 13:58:43 +03:00
*/
struct reset_simple_devdata {
2017-08-11 14:02:19 +03:00
u32 reg_offset ;
u32 nr_resets ;
2017-08-11 13:58:43 +03:00
bool active_low ;
2017-08-11 14:02:19 +03:00
bool status_active_low ;
} ;
# define SOCFPGA_NR_BANKS 8
static const struct reset_simple_devdata reset_simple_socfpga = {
2018-11-13 21:50:48 +03:00
. reg_offset = 0x20 ,
2017-08-11 14:02:19 +03:00
. nr_resets = SOCFPGA_NR_BANKS * 32 ,
. status_active_low = true ,
2017-08-11 13:58:43 +03:00
} ;
static const struct reset_simple_devdata reset_simple_active_low = {
. active_low = true ,
2017-08-11 14:02:19 +03:00
. status_active_low = true ,
2017-08-11 13:58:43 +03:00
} ;
static const struct of_device_id reset_simple_dt_ids [ ] = {
2018-11-13 21:50:48 +03:00
{ . compatible = " altr,stratix10-rst-mgr " ,
. data = & reset_simple_socfpga } ,
2017-08-11 14:02:47 +03:00
{ . compatible = " st,stm32-rcc " , } ,
2017-08-11 13:58:43 +03:00
{ . compatible = " allwinner,sun6i-a31-clock-reset " ,
. data = & reset_simple_active_low } ,
2017-08-11 14:03:05 +03:00
{ . compatible = " zte,zx296718-reset " ,
. data = & reset_simple_active_low } ,
2018-02-20 04:43:29 +03:00
{ . compatible = " aspeed,ast2400-lpc-reset " } ,
{ . compatible = " aspeed,ast2500-lpc-reset " } ,
2022-02-21 10:26:50 +03:00
{ . compatible = " aspeed,ast2600-lpc-reset " } ,
2019-05-10 21:45:24 +03:00
{ . compatible = " bitmain,bm1880-reset " ,
. data = & reset_simple_active_low } ,
2020-11-27 14:14:42 +03:00
{ . compatible = " brcm,bcm4908-misc-pcie-reset " ,
. data = & reset_simple_active_low } ,
2019-07-23 18:17:28 +03:00
{ . compatible = " snps,dw-high-reset " } ,
{ . compatible = " snps,dw-low-reset " ,
. data = & reset_simple_active_low } ,
2017-08-11 13:58:43 +03:00
{ /* sentinel */ } ,
} ;
static int reset_simple_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
const struct reset_simple_devdata * devdata ;
struct reset_simple_data * data ;
void __iomem * membase ;
struct resource * res ;
2017-08-11 14:02:19 +03:00
u32 reg_offset = 0 ;
2017-08-11 13:58:43 +03:00
devdata = of_device_get_match_data ( dev ) ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
membase = devm_ioremap_resource ( dev , res ) ;
if ( IS_ERR ( membase ) )
return PTR_ERR ( membase ) ;
spin_lock_init ( & data - > lock ) ;
data - > membase = membase ;
data - > rcdev . owner = THIS_MODULE ;
data - > rcdev . nr_resets = resource_size ( res ) * BITS_PER_BYTE ;
data - > rcdev . ops = & reset_simple_ops ;
data - > rcdev . of_node = dev - > of_node ;
2017-08-11 14:02:19 +03:00
if ( devdata ) {
reg_offset = devdata - > reg_offset ;
if ( devdata - > nr_resets )
data - > rcdev . nr_resets = devdata - > nr_resets ;
2017-08-11 13:58:43 +03:00
data - > active_low = devdata - > active_low ;
2017-08-11 14:02:19 +03:00
data - > status_active_low = devdata - > status_active_low ;
}
data - > membase + = reg_offset ;
2017-08-11 13:58:43 +03:00
return devm_reset_controller_register ( dev , & data - > rcdev ) ;
}
static struct platform_driver reset_simple_driver = {
. probe = reset_simple_probe ,
. driver = {
. name = " simple-reset " ,
. of_match_table = reset_simple_dt_ids ,
} ,
} ;
builtin_platform_driver ( reset_simple_driver ) ;