2016-06-27 20:12:17 +03:00
/*
* TI SYSCON regmap reset driver
*
* Copyright ( C ) 2015 - 2016 Texas Instruments Incorporated - http : //www.ti.com/
* Andrew F . Davis < afd @ ti . com >
* Suman Anna < afd @ ti . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed " as is " WITHOUT ANY WARRANTY of any
* kind , whether express or implied ; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*/
# include <linux/mfd/syscon.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/reset-controller.h>
# include <dt-bindings/reset/ti-syscon.h>
/**
* struct ti_syscon_reset_control - reset control structure
* @ assert_offset : reset assert control register offset from syscon base
* @ assert_bit : reset assert bit in the reset assert control register
* @ deassert_offset : reset deassert control register offset from syscon base
* @ deassert_bit : reset deassert bit in the reset deassert control register
* @ status_offset : reset status register offset from syscon base
* @ status_bit : reset status bit in the reset status register
* @ flags : reset flag indicating how the ( de ) assert and status are handled
*/
struct ti_syscon_reset_control {
unsigned int assert_offset ;
unsigned int assert_bit ;
unsigned int deassert_offset ;
unsigned int deassert_bit ;
unsigned int status_offset ;
unsigned int status_bit ;
u32 flags ;
} ;
/**
* struct ti_syscon_reset_data - reset controller information structure
* @ rcdev : reset controller entity
* @ regmap : regmap handle containing the memory - mapped reset registers
* @ controls : array of reset controls
* @ nr_controls : number of controls in control array
*/
struct ti_syscon_reset_data {
struct reset_controller_dev rcdev ;
struct regmap * regmap ;
struct ti_syscon_reset_control * controls ;
unsigned int nr_controls ;
} ;
# define to_ti_syscon_reset_data(rcdev) \
container_of ( rcdev , struct ti_syscon_reset_data , rcdev )
/**
* ti_syscon_reset_assert ( ) - assert device reset
* @ rcdev : reset controller entity
* @ id : ID of the reset to be asserted
*
* This function implements the reset driver op to assert a device ' s reset .
* This asserts the reset in a manner prescribed by the reset flags .
*
* Return : 0 for successful request , else a corresponding error value
*/
static int ti_syscon_reset_assert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct ti_syscon_reset_data * data = to_ti_syscon_reset_data ( rcdev ) ;
struct ti_syscon_reset_control * control ;
unsigned int mask , value ;
if ( id > = data - > nr_controls )
return - EINVAL ;
control = & data - > controls [ id ] ;
if ( control - > flags & ASSERT_NONE )
return - ENOTSUPP ; /* assert not supported for this reset */
mask = BIT ( control - > assert_bit ) ;
value = ( control - > flags & ASSERT_SET ) ? mask : 0x0 ;
return regmap_update_bits ( data - > regmap , control - > assert_offset , mask , value ) ;
}
/**
* ti_syscon_reset_deassert ( ) - deassert device reset
* @ rcdev : reset controller entity
* @ id : ID of reset to be deasserted
*
* This function implements the reset driver op to deassert a device ' s reset .
* This deasserts the reset in a manner prescribed by the reset flags .
*
* Return : 0 for successful request , else a corresponding error value
*/
static int ti_syscon_reset_deassert ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct ti_syscon_reset_data * data = to_ti_syscon_reset_data ( rcdev ) ;
struct ti_syscon_reset_control * control ;
unsigned int mask , value ;
if ( id > = data - > nr_controls )
return - EINVAL ;
control = & data - > controls [ id ] ;
if ( control - > flags & DEASSERT_NONE )
return - ENOTSUPP ; /* deassert not supported for this reset */
mask = BIT ( control - > deassert_bit ) ;
value = ( control - > flags & DEASSERT_SET ) ? mask : 0x0 ;
return regmap_update_bits ( data - > regmap , control - > deassert_offset , mask , value ) ;
}
/**
* ti_syscon_reset_status ( ) - check device reset status
* @ rcdev : reset controller entity
* @ id : ID of the reset for which the status is being requested
*
* This function implements the reset driver op to return the status of a
* device ' s reset .
*
* Return : 0 if reset is deasserted , true if reset is asserted , else a
* corresponding error value
*/
static int ti_syscon_reset_status ( struct reset_controller_dev * rcdev ,
unsigned long id )
{
struct ti_syscon_reset_data * data = to_ti_syscon_reset_data ( rcdev ) ;
struct ti_syscon_reset_control * control ;
unsigned int reset_state ;
int ret ;
if ( id > = data - > nr_controls )
return - EINVAL ;
control = & data - > controls [ id ] ;
if ( control - > flags & STATUS_NONE )
return - ENOTSUPP ; /* status not supported for this reset */
ret = regmap_read ( data - > regmap , control - > status_offset , & reset_state ) ;
if ( ret )
return ret ;
2016-11-30 04:03:32 +03:00
return ! ( reset_state & BIT ( control - > status_bit ) ) = =
! ( control - > flags & STATUS_SET ) ;
2016-06-27 20:12:17 +03:00
}
2017-01-09 19:53:55 +03:00
static const struct reset_control_ops ti_syscon_reset_ops = {
2016-06-27 20:12:17 +03:00
. assert = ti_syscon_reset_assert ,
. deassert = ti_syscon_reset_deassert ,
. status = ti_syscon_reset_status ,
} ;
static int ti_syscon_reset_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct device_node * np = dev - > of_node ;
struct ti_syscon_reset_data * data ;
struct regmap * regmap ;
const __be32 * list ;
struct ti_syscon_reset_control * controls ;
int size , nr_controls , i ;
data = devm_kzalloc ( dev , sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
regmap = syscon_node_to_regmap ( np - > parent ) ;
if ( IS_ERR ( regmap ) )
return PTR_ERR ( regmap ) ;
list = of_get_property ( np , " ti,reset-bits " , & size ) ;
if ( ! list | | ( size / sizeof ( * list ) ) % 7 ! = 0 ) {
dev_err ( dev , " invalid DT reset description \n " ) ;
return - EINVAL ;
}
nr_controls = ( size / sizeof ( * list ) ) / 7 ;
controls = devm_kzalloc ( dev , nr_controls * sizeof ( * controls ) , GFP_KERNEL ) ;
if ( ! controls )
return - ENOMEM ;
for ( i = 0 ; i < nr_controls ; i + + ) {
controls [ i ] . assert_offset = be32_to_cpup ( list + + ) ;
controls [ i ] . assert_bit = be32_to_cpup ( list + + ) ;
controls [ i ] . deassert_offset = be32_to_cpup ( list + + ) ;
controls [ i ] . deassert_bit = be32_to_cpup ( list + + ) ;
controls [ i ] . status_offset = be32_to_cpup ( list + + ) ;
controls [ i ] . status_bit = be32_to_cpup ( list + + ) ;
controls [ i ] . flags = be32_to_cpup ( list + + ) ;
}
data - > rcdev . ops = & ti_syscon_reset_ops ;
data - > rcdev . owner = THIS_MODULE ;
data - > rcdev . of_node = np ;
data - > rcdev . nr_resets = nr_controls ;
data - > regmap = regmap ;
data - > controls = controls ;
data - > nr_controls = nr_controls ;
platform_set_drvdata ( pdev , data ) ;
return devm_reset_controller_register ( dev , & data - > rcdev ) ;
}
static const struct of_device_id ti_syscon_reset_of_match [ ] = {
{ . compatible = " ti,syscon-reset " , } ,
{ /* sentinel */ } ,
} ;
MODULE_DEVICE_TABLE ( of , ti_syscon_reset_of_match ) ;
static struct platform_driver ti_syscon_reset_driver = {
. probe = ti_syscon_reset_probe ,
. driver = {
. name = " ti-syscon-reset " ,
. of_match_table = ti_syscon_reset_of_match ,
} ,
} ;
module_platform_driver ( ti_syscon_reset_driver ) ;
MODULE_AUTHOR ( " Andrew F. Davis <afd@ti.com> " ) ;
MODULE_AUTHOR ( " Suman Anna <s-anna@ti.com> " ) ;
MODULE_DESCRIPTION ( " TI SYSCON Regmap Reset Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;