2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-08-20 01:18:17 +03:00
/*
*
* Copyright ( C ) 2010 John Crispin < blogic @ phrozen . org >
* Copyright ( C ) 2013 - 2015 Lantiq Beteiligungs - GmbH & Co . KG
* Copyright ( C ) 2016 Martin Blumenstingl < martin . blumenstingl @ googlemail . com >
* Copyright ( C ) 2017 Hauke Mehrtens < hauke @ hauke - m . de >
*/
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/regmap.h>
# include <linux/reset-controller.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/platform_device.h>
# include <linux/property.h>
# define LANTIQ_RCU_RESET_TIMEOUT 10000
struct lantiq_rcu_reset_priv {
struct reset_controller_dev rcdev ;
struct device * dev ;
struct regmap * regmap ;
u32 reset_offset ;
u32 status_offset ;
} ;
static struct lantiq_rcu_reset_priv * to_lantiq_rcu_reset_priv (
struct reset_controller_dev * rcdev )
{
return container_of ( rcdev , struct lantiq_rcu_reset_priv , rcdev ) ;
}
static int lantiq_rcu_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct lantiq_rcu_reset_priv * priv = to_lantiq_rcu_reset_priv ( rcdev ) ;
unsigned int status = ( id > > 8 ) & 0x1f ;
u32 val ;
int ret ;
ret = regmap_read ( priv - > regmap , priv - > status_offset , & val ) ;
if ( ret )
return ret ;
return ! ! ( val & BIT ( status ) ) ;
}
static int lantiq_rcu_reset_status_timeout ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
int ret ;
int retry = LANTIQ_RCU_RESET_TIMEOUT ;
do {
ret = lantiq_rcu_reset_status ( rcdev , id ) ;
if ( ret < 0 )
return ret ;
if ( ret = = assert )
return 0 ;
usleep_range ( 20 , 40 ) ;
} while ( - - retry ) ;
return - ETIMEDOUT ;
}
static int lantiq_rcu_reset_update ( struct reset_controller_dev * rcdev ,
unsigned long id , bool assert )
{
struct lantiq_rcu_reset_priv * priv = to_lantiq_rcu_reset_priv ( rcdev ) ;
unsigned int set = id & 0x1f ;
u32 val = assert ? BIT ( set ) : 0 ;
int ret ;
ret = regmap_update_bits ( priv - > regmap , priv - > reset_offset , BIT ( set ) ,
val ) ;
if ( ret ) {
dev_err ( priv - > dev , " Failed to set reset bit %u \n " , set ) ;
return ret ;
}
ret = lantiq_rcu_reset_status_timeout ( rcdev , id , assert ) ;
if ( ret )
dev_err ( priv - > dev , " Failed to %s bit %u \n " ,
assert ? " assert " : " deassert " , set ) ;
return ret ;
}
static int lantiq_rcu_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return lantiq_rcu_reset_update ( rcdev , id , true ) ;
}
static int lantiq_rcu_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
return lantiq_rcu_reset_update ( rcdev , id , false ) ;
}
static int lantiq_rcu_reset_reset ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
int ret ;
ret = lantiq_rcu_reset_assert ( rcdev , id ) ;
if ( ret )
return ret ;
return lantiq_rcu_reset_deassert ( rcdev , id ) ;
}
static const struct reset_control_ops lantiq_rcu_reset_ops = {
. assert = lantiq_rcu_reset_assert ,
. deassert = lantiq_rcu_reset_deassert ,
. status = lantiq_rcu_reset_status ,
. reset = lantiq_rcu_reset_reset ,
} ;
static int lantiq_rcu_reset_of_parse ( struct platform_device * pdev ,
struct lantiq_rcu_reset_priv * priv )
{
struct device * dev = & pdev - > dev ;
const __be32 * offset ;
priv - > regmap = syscon_node_to_regmap ( dev - > of_node - > parent ) ;
if ( IS_ERR ( priv - > regmap ) ) {
dev_err ( & pdev - > dev , " Failed to lookup RCU regmap \n " ) ;
return PTR_ERR ( priv - > regmap ) ;
}
offset = of_get_address ( dev - > of_node , 0 , NULL , NULL ) ;
if ( ! offset ) {
dev_err ( & pdev - > dev , " Failed to get RCU reset offset \n " ) ;
return - ENOENT ;
}
priv - > reset_offset = __be32_to_cpu ( * offset ) ;
offset = of_get_address ( dev - > of_node , 1 , NULL , NULL ) ;
if ( ! offset ) {
dev_err ( & pdev - > dev , " Failed to get RCU status offset \n " ) ;
return - ENOENT ;
}
priv - > status_offset = __be32_to_cpu ( * offset ) ;
return 0 ;
}
static int lantiq_rcu_reset_xlate ( struct reset_controller_dev * rcdev ,
const struct of_phandle_args * reset_spec )
{
unsigned int status , set ;
set = reset_spec - > args [ 0 ] ;
status = reset_spec - > args [ 1 ] ;
if ( set > = rcdev - > nr_resets | | status > = rcdev - > nr_resets )
return - EINVAL ;
return ( status < < 8 ) | set ;
}
static int lantiq_rcu_reset_probe ( struct platform_device * pdev )
{
struct lantiq_rcu_reset_priv * priv ;
int err ;
priv = devm_kzalloc ( & pdev - > dev , sizeof ( * priv ) , GFP_KERNEL ) ;
if ( ! priv )
return - ENOMEM ;
priv - > dev = & pdev - > dev ;
platform_set_drvdata ( pdev , priv ) ;
err = lantiq_rcu_reset_of_parse ( pdev , priv ) ;
if ( err )
return err ;
priv - > rcdev . ops = & lantiq_rcu_reset_ops ;
priv - > rcdev . owner = THIS_MODULE ;
priv - > rcdev . of_node = pdev - > dev . of_node ;
priv - > rcdev . nr_resets = 32 ;
priv - > rcdev . of_xlate = lantiq_rcu_reset_xlate ;
priv - > rcdev . of_reset_n_cells = 2 ;
2021-05-17 06:26:48 +03:00
return devm_reset_controller_register ( & pdev - > dev , & priv - > rcdev ) ;
2017-08-20 01:18:17 +03:00
}
static const struct of_device_id lantiq_rcu_reset_dt_ids [ ] = {
{ . compatible = " lantiq,danube-reset " , } ,
{ . compatible = " lantiq,xrx200-reset " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , lantiq_rcu_reset_dt_ids ) ;
static struct platform_driver lantiq_rcu_reset_driver = {
. probe = lantiq_rcu_reset_probe ,
. driver = {
. name = " lantiq-reset " ,
. of_match_table = lantiq_rcu_reset_dt_ids ,
} ,
} ;
module_platform_driver ( lantiq_rcu_reset_driver ) ;
MODULE_AUTHOR ( " Martin Blumenstingl <martin.blumenstingl@googlemail.com> " ) ;
MODULE_DESCRIPTION ( " Lantiq XWAY RCU Reset Controller Driver " ) ;
MODULE_LICENSE ( " GPL " ) ;