2020-06-17 13:50:35 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* BCM6345 Reset Controller Driver
*
* Copyright ( C ) 2020 Á lvaro Fernández Rojas < noltari @ gmail . com >
*/
# include <linux/delay.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/mod_devicetable.h>
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
# define BCM6345_RESET_NUM 32
# define BCM6345_RESET_SLEEP_MIN_US 10000
# define BCM6345_RESET_SLEEP_MAX_US 20000
struct bcm6345_reset {
struct reset_controller_dev rcdev ;
void __iomem * base ;
spinlock_t lock ;
} ;
static inline struct bcm6345_reset *
to_bcm6345_reset ( struct reset_controller_dev * rcdev )
{
return container_of ( rcdev , struct bcm6345_reset , rcdev ) ;
}
static int bcm6345_reset_update ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
struct bcm6345_reset * bcm6345_reset = to_bcm6345_reset ( rcdev ) ;
unsigned long flags ;
uint32_t val ;
spin_lock_irqsave ( & bcm6345_reset - > lock , flags ) ;
val = __raw_readl ( bcm6345_reset - > base ) ;
if ( assert )
val & = ~ BIT ( id ) ;
else
val | = BIT ( id ) ;
__raw_writel ( val , bcm6345_reset - > base ) ;
spin_unlock_irqrestore ( & bcm6345_reset - > lock , flags ) ;
return 0 ;
}
static int bcm6345_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return bcm6345_reset_update ( rcdev , id , true ) ;
}
static int bcm6345_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return bcm6345_reset_update ( rcdev , id , false ) ;
}
static int bcm6345_reset_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
bcm6345_reset_update ( rcdev , id , true ) ;
usleep_range ( BCM6345_RESET_SLEEP_MIN_US ,
BCM6345_RESET_SLEEP_MAX_US ) ;
bcm6345_reset_update ( rcdev , id , false ) ;
/*
* Ensure component is taken out reset state by sleeping also after
* deasserting the reset . Otherwise , the component may not be ready
* for operation .
*/
usleep_range ( BCM6345_RESET_SLEEP_MIN_US ,
BCM6345_RESET_SLEEP_MAX_US ) ;
return 0 ;
}
static int bcm6345_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct bcm6345_reset * bcm6345_reset = to_bcm6345_reset ( rcdev ) ;
return ! ( __raw_readl ( bcm6345_reset - > base ) & BIT ( id ) ) ;
}
2021-01-14 15:54:34 +03:00
static const struct reset_control_ops bcm6345_reset_ops = {
2020-06-17 13:50:35 +03:00
. assert = bcm6345_reset_assert ,
. deassert = bcm6345_reset_deassert ,
. reset = bcm6345_reset_reset ,
. status = bcm6345_reset_status ,
} ;
static int bcm6345_reset_probe ( struct platform_device * pdev )
{
struct bcm6345_reset * bcm6345_reset ;
bcm6345_reset = devm_kzalloc ( & pdev - > dev ,
sizeof ( * bcm6345_reset ) , GFP_KERNEL ) ;
if ( ! bcm6345_reset )
return - ENOMEM ;
platform_set_drvdata ( pdev , bcm6345_reset ) ;
bcm6345_reset - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( bcm6345_reset - > base ) )
return PTR_ERR ( bcm6345_reset - > base ) ;
spin_lock_init ( & bcm6345_reset - > lock ) ;
bcm6345_reset - > rcdev . ops = & bcm6345_reset_ops ;
bcm6345_reset - > rcdev . owner = THIS_MODULE ;
bcm6345_reset - > rcdev . of_node = pdev - > dev . of_node ;
bcm6345_reset - > rcdev . of_reset_n_cells = 1 ;
bcm6345_reset - > rcdev . nr_resets = BCM6345_RESET_NUM ;
return devm_reset_controller_register ( & pdev - > dev ,
& bcm6345_reset - > rcdev ) ;
}
static const struct of_device_id bcm6345_reset_of_match [ ] = {
{ . compatible = " brcm,bcm6345-reset " } ,
{ /* sentinel */ } ,
} ;
static struct platform_driver bcm6345_reset_driver = {
. probe = bcm6345_reset_probe ,
. driver = {
. name = " bcm6345-reset " ,
. of_match_table = bcm6345_reset_of_match ,
. suppress_bind_attrs = true ,
} ,
} ;
builtin_platform_driver ( bcm6345_reset_driver ) ;