2020-01-03 13:00:18 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2019 Intel Corporation .
* Lei Chuanhua < Chuanhua . lei @ intel . com >
*/
# include <linux/bitfield.h>
# include <linux/init.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/reboot.h>
# include <linux/regmap.h>
# include <linux/reset-controller.h>
# define RCU_RST_STAT 0x0024
# define RCU_RST_REQ 0x0048
2020-06-26 16:00:41 +03:00
# define REG_OFFSET_MASK GENMASK(31, 16)
# define BIT_OFFSET_MASK GENMASK(15, 8)
# define STAT_BIT_OFFSET_MASK GENMASK(7, 0)
2020-01-03 13:00:18 +03:00
# define to_reset_data(x) container_of(x, struct intel_reset_data, rcdev)
struct intel_reset_soc {
bool legacy ;
u32 reset_cell_count ;
} ;
struct intel_reset_data {
struct reset_controller_dev rcdev ;
struct notifier_block restart_nb ;
const struct intel_reset_soc * soc_data ;
struct regmap * regmap ;
struct device * dev ;
u32 reboot_id ;
} ;
static const struct regmap_config intel_rcu_regmap_config = {
. name = " intel-reset " ,
. reg_bits = 32 ,
. reg_stride = 4 ,
. val_bits = 32 ,
. fast_io = true ,
} ;
/*
* Reset status register offset relative to
* the reset control register ( X ) is X + 4
*/
static u32 id_to_reg_and_bit_offsets ( struct intel_reset_data * data ,
unsigned long id , u32 * rst_req ,
u32 * req_bit , u32 * stat_bit )
{
2020-06-26 16:00:41 +03:00
* rst_req = FIELD_GET ( REG_OFFSET_MASK , id ) ;
* req_bit = FIELD_GET ( BIT_OFFSET_MASK , id ) ;
2020-01-03 13:00:18 +03:00
if ( data - > soc_data - > legacy )
2020-06-26 16:00:41 +03:00
* stat_bit = FIELD_GET ( STAT_BIT_OFFSET_MASK , id ) ;
2020-01-03 13:00:18 +03:00
else
* stat_bit = * req_bit ;
if ( data - > soc_data - > legacy & & * rst_req = = RCU_RST_REQ )
return RCU_RST_STAT ;
else
return * rst_req + 0x4 ;
}
static int intel_set_clr_bits ( struct intel_reset_data * data , unsigned long id ,
bool set )
{
u32 rst_req , req_bit , rst_stat , stat_bit , val ;
int ret ;
rst_stat = id_to_reg_and_bit_offsets ( data , id , & rst_req ,
& req_bit , & stat_bit ) ;
val = set ? BIT ( req_bit ) : 0 ;
ret = regmap_update_bits ( data - > regmap , rst_req , BIT ( req_bit ) , val ) ;
if ( ret )
return ret ;
return regmap_read_poll_timeout ( data - > regmap , rst_stat , val ,
set = = ! ! ( val & BIT ( stat_bit ) ) , 20 ,
200 ) ;
}
static int intel_assert_device ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct intel_reset_data * data = to_reset_data ( rcdev ) ;
int ret ;
ret = intel_set_clr_bits ( data , id , true ) ;
if ( ret )
dev_err ( data - > dev , " Reset assert failed %d \n " , ret ) ;
return ret ;
}
static int intel_deassert_device ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct intel_reset_data * data = to_reset_data ( rcdev ) ;
int ret ;
ret = intel_set_clr_bits ( data , id , false ) ;
if ( ret )
dev_err ( data - > dev , " Reset deassert failed %d \n " , ret ) ;
return ret ;
}
static int intel_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct intel_reset_data * data = to_reset_data ( rcdev ) ;
u32 rst_req , req_bit , rst_stat , stat_bit , val ;
int ret ;
rst_stat = id_to_reg_and_bit_offsets ( data , id , & rst_req ,
& req_bit , & stat_bit ) ;
ret = regmap_read ( data - > regmap , rst_stat , & val ) ;
if ( ret )
return ret ;
return ! ! ( val & BIT ( stat_bit ) ) ;
}
static const struct reset_control_ops intel_reset_ops = {
. assert = intel_assert_device ,
. deassert = intel_deassert_device ,
. status = intel_reset_status ,
} ;
static int intel_reset_xlate ( struct reset_controller_dev * rcdev ,
const struct of_phandle_args * spec )
{
struct intel_reset_data * data = to_reset_data ( rcdev ) ;
u32 id ;
if ( spec - > args [ 1 ] > 31 )
return - EINVAL ;
2020-06-26 16:00:41 +03:00
id = FIELD_PREP ( REG_OFFSET_MASK , spec - > args [ 0 ] ) ;
id | = FIELD_PREP ( BIT_OFFSET_MASK , spec - > args [ 1 ] ) ;
2020-01-03 13:00:18 +03:00
if ( data - > soc_data - > legacy ) {
if ( spec - > args [ 2 ] > 31 )
return - EINVAL ;
2020-06-26 16:00:41 +03:00
id | = FIELD_PREP ( STAT_BIT_OFFSET_MASK , spec - > args [ 2 ] ) ;
2020-01-03 13:00:18 +03:00
}
return id ;
}
static int intel_reset_restart_handler ( struct notifier_block * nb ,
unsigned long action , void * data )
{
struct intel_reset_data * reset_data ;
reset_data = container_of ( nb , struct intel_reset_data , restart_nb ) ;
intel_assert_device ( & reset_data - > rcdev , reset_data - > reboot_id ) ;
return NOTIFY_DONE ;
}
static int intel_reset_probe ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct device * dev = & pdev - > dev ;
struct intel_reset_data * data ;
void __iomem * base ;
u32 rb_id [ 3 ] ;
int ret ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > soc_data = of_device_get_match_data ( dev ) ;
if ( ! data - > soc_data )
return - ENODEV ;
base = devm_platform_ioremap_resource ( pdev , 0 ) ;
if ( IS_ERR ( base ) )
return PTR_ERR ( base ) ;
data - > regmap = devm_regmap_init_mmio ( dev , base ,
& intel_rcu_regmap_config ) ;
if ( IS_ERR ( data - > regmap ) ) {
dev_err ( dev , " regmap initialization failed \n " ) ;
return PTR_ERR ( data - > regmap ) ;
}
ret = device_property_read_u32_array ( dev , " intel,global-reset " , rb_id ,
data - > soc_data - > reset_cell_count ) ;
if ( ret ) {
dev_err ( dev , " Failed to get global reset offset! \n " ) ;
return ret ;
}
data - > dev = dev ;
data - > rcdev . of_node = np ;
data - > rcdev . owner = dev - > driver - > owner ;
data - > rcdev . ops = & intel_reset_ops ;
data - > rcdev . of_xlate = intel_reset_xlate ;
data - > rcdev . of_reset_n_cells = data - > soc_data - > reset_cell_count ;
ret = devm_reset_controller_register ( & pdev - > dev , & data - > rcdev ) ;
if ( ret )
return ret ;
2020-06-26 16:00:41 +03:00
data - > reboot_id = FIELD_PREP ( REG_OFFSET_MASK , rb_id [ 0 ] ) ;
data - > reboot_id | = FIELD_PREP ( BIT_OFFSET_MASK , rb_id [ 1 ] ) ;
2020-01-03 13:00:18 +03:00
if ( data - > soc_data - > legacy )
2020-06-26 16:00:41 +03:00
data - > reboot_id | = FIELD_PREP ( STAT_BIT_OFFSET_MASK , rb_id [ 2 ] ) ;
2020-01-03 13:00:18 +03:00
data - > restart_nb . notifier_call = intel_reset_restart_handler ;
data - > restart_nb . priority = 128 ;
register_restart_handler ( & data - > restart_nb ) ;
return 0 ;
}
static const struct intel_reset_soc xrx200_data = {
. legacy = true ,
. reset_cell_count = 3 ,
} ;
static const struct intel_reset_soc lgm_data = {
. legacy = false ,
. reset_cell_count = 2 ,
} ;
static const struct of_device_id intel_reset_match [ ] = {
{ . compatible = " intel,rcu-lgm " , . data = & lgm_data } ,
{ . compatible = " intel,rcu-xrx200 " , . data = & xrx200_data } ,
{ }
} ;
static struct platform_driver intel_reset_driver = {
. probe = intel_reset_probe ,
. driver = {
. name = " intel-reset " ,
. of_match_table = intel_reset_match ,
} ,
} ;
static int __init intel_reset_init ( void )
{
return platform_driver_register ( & intel_reset_driver ) ;
}
/*
* RCU is system core entity which is in Always On Domain whose clocks
* or resource initialization happens in system core initialization .
* Also , it is required for most of the platform or architecture
* specific devices to perform reset operation as part of initialization .
* So perform RCU as post core initialization .
*/
postcore_initcall ( intel_reset_init ) ;