2012-11-19 20:23:13 +04:00
/*
* Reset Controller framework
*
* Copyright 2013 Philipp Zabel , Pengutronix
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/export.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/reset.h>
# include <linux/reset-controller.h>
# include <linux/slab.h>
static DEFINE_MUTEX ( reset_controller_list_mutex ) ;
static LIST_HEAD ( reset_controller_list ) ;
/**
* struct reset_control - a reset control
* @ rcdev : a pointer to the reset controller device
* this reset control belongs to
* @ id : ID of the reset controller in the reset
* controller device
*/
struct reset_control {
struct reset_controller_dev * rcdev ;
struct device * dev ;
unsigned int id ;
} ;
/**
* of_reset_simple_xlate - translate reset_spec to the reset line number
* @ rcdev : a pointer to the reset controller device
* @ reset_spec : reset line specifier as found in the device tree
* @ flags : a flags pointer to fill in ( optional )
*
* This simple translation function should be used for reset controllers
* with 1 : 1 mapping , where reset lines can be indexed by number without gaps .
*/
int of_reset_simple_xlate ( struct reset_controller_dev * rcdev ,
const struct of_phandle_args * reset_spec )
{
if ( WARN_ON ( reset_spec - > args_count ! = rcdev - > of_reset_n_cells ) )
return - EINVAL ;
if ( reset_spec - > args [ 0 ] > = rcdev - > nr_resets )
return - EINVAL ;
return reset_spec - > args [ 0 ] ;
}
EXPORT_SYMBOL_GPL ( of_reset_simple_xlate ) ;
/**
* reset_controller_register - register a reset controller device
* @ rcdev : a pointer to the initialized reset controller device
*/
int reset_controller_register ( struct reset_controller_dev * rcdev )
{
if ( ! rcdev - > of_xlate ) {
rcdev - > of_reset_n_cells = 1 ;
rcdev - > of_xlate = of_reset_simple_xlate ;
}
mutex_lock ( & reset_controller_list_mutex ) ;
list_add ( & rcdev - > list , & reset_controller_list ) ;
mutex_unlock ( & reset_controller_list_mutex ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( reset_controller_register ) ;
/**
* reset_controller_unregister - unregister a reset controller device
* @ rcdev : a pointer to the reset controller device
*/
void reset_controller_unregister ( struct reset_controller_dev * rcdev )
{
mutex_lock ( & reset_controller_list_mutex ) ;
list_del ( & rcdev - > list ) ;
mutex_unlock ( & reset_controller_list_mutex ) ;
}
EXPORT_SYMBOL_GPL ( reset_controller_unregister ) ;
/**
* reset_control_reset - reset the controlled device
* @ rstc : reset controller
*/
int reset_control_reset ( struct reset_control * rstc )
{
if ( rstc - > rcdev - > ops - > reset )
return rstc - > rcdev - > ops - > reset ( rstc - > rcdev , rstc - > id ) ;
return - ENOSYS ;
}
EXPORT_SYMBOL_GPL ( reset_control_reset ) ;
/**
* reset_control_assert - asserts the reset line
* @ rstc : reset controller
*/
int reset_control_assert ( struct reset_control * rstc )
{
if ( rstc - > rcdev - > ops - > assert )
return rstc - > rcdev - > ops - > assert ( rstc - > rcdev , rstc - > id ) ;
return - ENOSYS ;
}
EXPORT_SYMBOL_GPL ( reset_control_assert ) ;
/**
* reset_control_deassert - deasserts the reset line
* @ rstc : reset controller
*/
int reset_control_deassert ( struct reset_control * rstc )
{
if ( rstc - > rcdev - > ops - > deassert )
return rstc - > rcdev - > ops - > deassert ( rstc - > rcdev , rstc - > id ) ;
return - ENOSYS ;
}
EXPORT_SYMBOL_GPL ( reset_control_deassert ) ;
/**
* reset_control_get - Lookup and obtain a reference to a reset controller .
* @ dev : device to be reset by the controller
* @ id : reset line name
*
* Returns a struct reset_control or IS_ERR ( ) condition containing errno .
*
* Use of id names is optional .
*/
struct reset_control * reset_control_get ( struct device * dev , const char * id )
{
struct reset_control * rstc = ERR_PTR ( - EPROBE_DEFER ) ;
struct reset_controller_dev * r , * rcdev ;
struct of_phandle_args args ;
int index = 0 ;
int rstc_id ;
int ret ;
if ( ! dev )
return ERR_PTR ( - EINVAL ) ;
if ( id )
index = of_property_match_string ( dev - > of_node ,
" reset-names " , id ) ;
ret = of_parse_phandle_with_args ( dev - > of_node , " resets " , " #reset-cells " ,
index , & args ) ;
if ( ret )
return ERR_PTR ( ret ) ;
mutex_lock ( & reset_controller_list_mutex ) ;
rcdev = NULL ;
list_for_each_entry ( r , & reset_controller_list , list ) {
if ( args . np = = r - > of_node ) {
rcdev = r ;
break ;
}
}
of_node_put ( args . np ) ;
if ( ! rcdev ) {
mutex_unlock ( & reset_controller_list_mutex ) ;
return ERR_PTR ( - ENODEV ) ;
}
rstc_id = rcdev - > of_xlate ( rcdev , & args ) ;
if ( rstc_id < 0 ) {
mutex_unlock ( & reset_controller_list_mutex ) ;
return ERR_PTR ( rstc_id ) ;
}
try_module_get ( rcdev - > owner ) ;
mutex_unlock ( & reset_controller_list_mutex ) ;
rstc = kzalloc ( sizeof ( * rstc ) , GFP_KERNEL ) ;
if ( ! rstc ) {
2013-04-03 09:02:53 +04:00
module_put ( rcdev - > owner ) ;
2012-11-19 20:23:13 +04:00
return ERR_PTR ( - ENOMEM ) ;
}
rstc - > dev = dev ;
rstc - > rcdev = rcdev ;
rstc - > id = rstc_id ;
return rstc ;
}
EXPORT_SYMBOL_GPL ( reset_control_get ) ;
/**
* reset_control_put - free the reset controller
* @ rstc : reset controller
*/
void reset_control_put ( struct reset_control * rstc )
{
if ( IS_ERR ( rstc ) )
return ;
module_put ( rstc - > rcdev - > owner ) ;
kfree ( rstc ) ;
}
EXPORT_SYMBOL_GPL ( reset_control_put ) ;
static void devm_reset_control_release ( struct device * dev , void * res )
{
reset_control_put ( * ( struct reset_control * * ) res ) ;
}
/**
* devm_reset_control_get - resource managed reset_control_get ( )
* @ dev : device to be reset by the controller
* @ id : reset line name
*
* Managed reset_control_get ( ) . For reset controllers returned from this
* function , reset_control_put ( ) is called automatically on driver detach .
* See reset_control_get ( ) for more information .
*/
struct reset_control * devm_reset_control_get ( struct device * dev , const char * id )
{
struct reset_control * * ptr , * rstc ;
ptr = devres_alloc ( devm_reset_control_release , sizeof ( * ptr ) ,
GFP_KERNEL ) ;
if ( ! ptr )
return ERR_PTR ( - ENOMEM ) ;
rstc = reset_control_get ( dev , id ) ;
if ( ! IS_ERR ( rstc ) ) {
* ptr = rstc ;
devres_add ( dev , ptr ) ;
} else {
devres_free ( ptr ) ;
}
return rstc ;
}
EXPORT_SYMBOL_GPL ( devm_reset_control_get ) ;
static int devm_reset_control_match ( struct device * dev , void * res , void * data )
{
struct reset_control * * rstc = res ;
if ( WARN_ON ( ! rstc | | ! * rstc ) )
return 0 ;
return * rstc = = data ;
}
/**
* devm_reset_control_put - resource managed reset_control_put ( )
* @ rstc : reset controller to free
*
* Deallocate a reset control allocated withd devm_reset_control_get ( ) .
* This function will not need to be called normally , as devres will take
* care of freeing the resource .
*/
void devm_reset_control_put ( struct reset_control * rstc )
{
int ret ;
ret = devres_release ( rstc - > dev , devm_reset_control_release ,
devm_reset_control_match , rstc ) ;
if ( ret )
WARN_ON ( ret ) ;
}
EXPORT_SYMBOL_GPL ( devm_reset_control_put ) ;
/**
* device_reset - find reset controller associated with the device
* and perform reset
* @ dev : device to be reset by the controller
*
* Convenience wrapper for reset_control_get ( ) and reset_control_reset ( ) .
* This is useful for the common case of devices with single , dedicated reset
* lines .
*/
int device_reset ( struct device * dev )
{
struct reset_control * rstc ;
int ret ;
rstc = reset_control_get ( dev , NULL ) ;
if ( IS_ERR ( rstc ) )
return PTR_ERR ( rstc ) ;
ret = reset_control_reset ( rstc ) ;
reset_control_put ( rstc ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( device_reset ) ;