2022-05-16 09:05:48 +02:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) STMicroelectronics 2022 - All Rights Reserved
* Author : Gabriel Fernandez < gabriel . fernandez @ foss . st . com > for STMicroelectronics .
*/
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/reset-controller.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include "clk-stm32-core.h"
# define STM32_RESET_ID_MASK GENMASK(15, 0)
struct stm32_reset_data {
/* reset lock */
spinlock_t lock ;
struct reset_controller_dev rcdev ;
void __iomem * membase ;
u32 clear_offset ;
} ;
static inline struct stm32_reset_data *
to_stm32_reset_data ( struct reset_controller_dev * rcdev )
{
return container_of ( rcdev , struct stm32_reset_data , rcdev ) ;
}
static int stm32_reset_update ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
struct stm32_reset_data * data = to_stm32_reset_data ( rcdev ) ;
int reg_width = sizeof ( u32 ) ;
int bank = id / ( reg_width * BITS_PER_BYTE ) ;
int offset = id % ( reg_width * BITS_PER_BYTE ) ;
if ( data - > clear_offset ) {
void __iomem * addr ;
addr = data - > membase + ( bank * reg_width ) ;
if ( ! assert )
addr + = data - > clear_offset ;
writel ( BIT ( offset ) , addr ) ;
} else {
unsigned long flags ;
u32 reg ;
spin_lock_irqsave ( & data - > lock , flags ) ;
reg = readl ( data - > membase + ( bank * reg_width ) ) ;
if ( assert )
reg | = BIT ( offset ) ;
else
reg & = ~ BIT ( offset ) ;
writel ( reg , data - > membase + ( bank * reg_width ) ) ;
spin_unlock_irqrestore ( & data - > lock , flags ) ;
}
return 0 ;
}
static int stm32_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return stm32_reset_update ( rcdev , id , true ) ;
}
static int stm32_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return stm32_reset_update ( rcdev , id , false ) ;
}
static int stm32_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct stm32_reset_data * data = to_stm32_reset_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 ) ) ;
}
static const struct reset_control_ops stm32_reset_ops = {
. assert = stm32_reset_assert ,
. deassert = stm32_reset_deassert ,
. status = stm32_reset_status ,
} ;
int stm32_rcc_reset_init ( struct device * dev , const struct of_device_id * match ,
void __iomem * base )
{
const struct stm32_rcc_match_data * data = match - > data ;
struct stm32_reset_data * reset_data = NULL ;
data = match - > data ;
reset_data = kzalloc ( sizeof ( * reset_data ) , GFP_KERNEL ) ;
if ( ! reset_data )
return - ENOMEM ;
2022-06-08 02:11:54 +00:00
spin_lock_init ( & reset_data - > lock ) ;
2022-05-16 09:05:48 +02:00
reset_data - > membase = base ;
reset_data - > rcdev . owner = THIS_MODULE ;
reset_data - > rcdev . ops = & stm32_reset_ops ;
reset_data - > rcdev . of_node = dev_of_node ( dev ) ;
reset_data - > rcdev . nr_resets = STM32_RESET_ID_MASK ;
reset_data - > clear_offset = data - > clear_offset ;
return reset_controller_register ( & reset_data - > rcdev ) ;
}