2022-09-09 15:31:14 +03:00
// SPDX-License-Identifier: GPL-2.0-only
/*
* PolarFire SoC ( MPFS ) Peripheral Clock Reset Controller
*
* Author : Conor Dooley < conor . dooley @ microchip . com >
* Copyright ( c ) 2022 Microchip Technology Inc . and its subsidiaries .
*
*/
# include <linux/auxiliary_bus.h>
# include <linux/delay.h>
# include <linux/module.h>
2023-07-14 20:49:37 +03:00
# include <linux/of.h>
2022-09-09 15:31:14 +03:00
# include <linux/platform_device.h>
# include <linux/reset-controller.h>
# include <dt-bindings/clock/microchip,mpfs-clock.h>
# include <soc/microchip/mpfs.h>
/*
* The ENVM reset is the lowest bit in the register & I am using the CLK_FOO
* defines in the dt to make things easier to configure - so this is accounting
* for the offset of 3 there .
*/
# define MPFS_PERIPH_OFFSET CLK_ENVM
# define MPFS_NUM_RESETS 30u
# define MPFS_SLEEP_MIN_US 100
# define MPFS_SLEEP_MAX_US 200
/* block concurrent access to the soft reset register */
static DEFINE_SPINLOCK ( mpfs_reset_lock ) ;
/*
* Peripheral clock resets
*/
static int mpfs_assert ( struct reset_controller_dev * rcdev , unsigned long id )
{
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & mpfs_reset_lock , flags ) ;
reg = mpfs_reset_read ( rcdev - > dev ) ;
reg | = BIT ( id ) ;
mpfs_reset_write ( rcdev - > dev , reg ) ;
spin_unlock_irqrestore ( & mpfs_reset_lock , flags ) ;
return 0 ;
}
static int mpfs_deassert ( struct reset_controller_dev * rcdev , unsigned long id )
{
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & mpfs_reset_lock , flags ) ;
reg = mpfs_reset_read ( rcdev - > dev ) ;
reg & = ~ BIT ( id ) ;
mpfs_reset_write ( rcdev - > dev , reg ) ;
spin_unlock_irqrestore ( & mpfs_reset_lock , flags ) ;
return 0 ;
}
static int mpfs_status ( struct reset_controller_dev * rcdev , unsigned long id )
{
u32 reg = mpfs_reset_read ( rcdev - > dev ) ;
/*
* It is safe to return here as MPFS_NUM_RESETS makes sure the sign bit
* is never hit .
*/
return ( reg & BIT ( id ) ) ;
}
static int mpfs_reset ( struct reset_controller_dev * rcdev , unsigned long id )
{
mpfs_assert ( rcdev , id ) ;
usleep_range ( MPFS_SLEEP_MIN_US , MPFS_SLEEP_MAX_US ) ;
mpfs_deassert ( rcdev , id ) ;
return 0 ;
}
static const struct reset_control_ops mpfs_reset_ops = {
. reset = mpfs_reset ,
. assert = mpfs_assert ,
. deassert = mpfs_deassert ,
. status = mpfs_status ,
} ;
static int mpfs_reset_xlate ( struct reset_controller_dev * rcdev ,
const struct of_phandle_args * reset_spec )
{
unsigned int index = reset_spec - > args [ 0 ] ;
/*
* CLK_RESERVED does not map to a clock , but it does map to a reset ,
* so it has to be accounted for here . It is the reset for the fabric ,
* so if this reset gets called - do not reset it .
*/
if ( index = = CLK_RESERVED ) {
dev_err ( rcdev - > dev , " Resetting the fabric is not supported \n " ) ;
return - EINVAL ;
}
if ( index < MPFS_PERIPH_OFFSET | | index > = ( MPFS_PERIPH_OFFSET + rcdev - > nr_resets ) ) {
dev_err ( rcdev - > dev , " Invalid reset index %u \n " , index ) ;
return - EINVAL ;
}
return index - MPFS_PERIPH_OFFSET ;
}
static int mpfs_reset_probe ( struct auxiliary_device * adev ,
const struct auxiliary_device_id * id )
{
struct device * dev = & adev - > dev ;
struct reset_controller_dev * rcdev ;
rcdev = devm_kzalloc ( dev , sizeof ( * rcdev ) , GFP_KERNEL ) ;
if ( ! rcdev )
return - ENOMEM ;
rcdev - > dev = dev ;
rcdev - > dev - > parent = dev - > parent ;
rcdev - > ops = & mpfs_reset_ops ;
rcdev - > of_node = dev - > parent - > of_node ;
rcdev - > of_reset_n_cells = 1 ;
rcdev - > of_xlate = mpfs_reset_xlate ;
rcdev - > nr_resets = MPFS_NUM_RESETS ;
return devm_reset_controller_register ( dev , rcdev ) ;
}
static const struct auxiliary_device_id mpfs_reset_ids [ ] = {
{
. name = " clk_mpfs.reset-mpfs " ,
} ,
{ }
} ;
MODULE_DEVICE_TABLE ( auxiliary , mpfs_reset_ids ) ;
static struct auxiliary_driver mpfs_reset_driver = {
. probe = mpfs_reset_probe ,
. id_table = mpfs_reset_ids ,
} ;
module_auxiliary_driver ( mpfs_reset_driver ) ;
MODULE_DESCRIPTION ( " Microchip PolarFire SoC Reset Driver " ) ;
MODULE_AUTHOR ( " Conor Dooley <conor.dooley@microchip.com> " ) ;
MODULE_IMPORT_NS ( MCHP_CLK_MPFS ) ;