2018-12-11 20:43:03 +03:00
// SPDX-License-Identifier: GPL-2.0
2012-09-11 22:56:23 +04:00
# include <linux/clk.h>
# include <linux/device.h>
# include <linux/export.h>
# include <linux/gfp.h>
2022-05-20 10:57:35 +03:00
struct devm_clk_state {
struct clk * clk ;
void ( * exit ) ( struct clk * clk ) ;
} ;
2012-09-11 22:56:23 +04:00
static void devm_clk_release ( struct device * dev , void * res )
{
2022-06-20 20:18:15 +03:00
struct devm_clk_state * state = res ;
2022-05-20 10:57:35 +03:00
if ( state - > exit )
state - > exit ( state - > clk ) ;
clk_put ( state - > clk ) ;
2012-09-11 22:56:23 +04:00
}
2022-05-20 10:57:35 +03:00
static struct clk * __devm_clk_get ( struct device * dev , const char * id ,
struct clk * ( * get ) ( struct device * dev , const char * id ) ,
int ( * init ) ( struct clk * clk ) ,
void ( * exit ) ( struct clk * clk ) )
2012-09-11 22:56:23 +04:00
{
2022-05-20 10:57:35 +03:00
struct devm_clk_state * state ;
struct clk * clk ;
int ret ;
2012-09-11 22:56:23 +04:00
2022-05-20 10:57:35 +03:00
state = devres_alloc ( devm_clk_release , sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
2012-09-11 22:56:23 +04:00
return ERR_PTR ( - ENOMEM ) ;
2022-05-20 10:57:35 +03:00
clk = get ( dev , id ) ;
if ( IS_ERR ( clk ) ) {
ret = PTR_ERR ( clk ) ;
goto err_clk_get ;
}
if ( init ) {
ret = init ( clk ) ;
if ( ret )
goto err_clk_init ;
2012-09-11 22:56:23 +04:00
}
2022-05-20 10:57:35 +03:00
state - > clk = clk ;
state - > exit = exit ;
devres_add ( dev , state ) ;
2012-09-11 22:56:23 +04:00
return clk ;
2022-05-20 10:57:35 +03:00
err_clk_init :
clk_put ( clk ) ;
err_clk_get :
devres_free ( state ) ;
return ERR_PTR ( ret ) ;
}
struct clk * devm_clk_get ( struct device * dev , const char * id )
{
return __devm_clk_get ( dev , id , clk_get , NULL , NULL ) ;
2012-09-11 22:56:23 +04:00
}
EXPORT_SYMBOL ( devm_clk_get ) ;
2022-05-20 10:57:36 +03:00
struct clk * devm_clk_get_prepared ( struct device * dev , const char * id )
{
return __devm_clk_get ( dev , id , clk_get , clk_prepare , clk_unprepare ) ;
}
EXPORT_SYMBOL_GPL ( devm_clk_get_prepared ) ;
struct clk * devm_clk_get_enabled ( struct device * dev , const char * id )
{
return __devm_clk_get ( dev , id , clk_get ,
clk_prepare_enable , clk_disable_unprepare ) ;
}
EXPORT_SYMBOL_GPL ( devm_clk_get_enabled ) ;
2018-12-03 14:13:09 +03:00
struct clk * devm_clk_get_optional ( struct device * dev , const char * id )
{
2022-05-20 10:57:35 +03:00
return __devm_clk_get ( dev , id , clk_get_optional , NULL , NULL ) ;
2018-12-03 14:13:09 +03:00
}
EXPORT_SYMBOL ( devm_clk_get_optional ) ;
2022-05-20 10:57:36 +03:00
struct clk * devm_clk_get_optional_prepared ( struct device * dev , const char * id )
{
return __devm_clk_get ( dev , id , clk_get_optional ,
clk_prepare , clk_unprepare ) ;
}
EXPORT_SYMBOL_GPL ( devm_clk_get_optional_prepared ) ;
struct clk * devm_clk_get_optional_enabled ( struct device * dev , const char * id )
{
return __devm_clk_get ( dev , id , clk_get_optional ,
clk_prepare_enable , clk_disable_unprepare ) ;
}
EXPORT_SYMBOL_GPL ( devm_clk_get_optional_enabled ) ;
2017-05-19 16:49:05 +03:00
struct clk_bulk_devres {
struct clk_bulk_data * clks ;
int num_clks ;
} ;
static void devm_clk_bulk_release ( struct device * dev , void * res )
{
struct clk_bulk_devres * devres = res ;
clk_bulk_put ( devres - > num_clks , devres - > clks ) ;
}
2019-06-19 12:39:26 +03:00
static int __devm_clk_bulk_get ( struct device * dev , int num_clks ,
struct clk_bulk_data * clks , bool optional )
2017-05-19 16:49:05 +03:00
{
struct clk_bulk_devres * devres ;
int ret ;
devres = devres_alloc ( devm_clk_bulk_release ,
sizeof ( * devres ) , GFP_KERNEL ) ;
if ( ! devres )
return - ENOMEM ;
2019-06-19 12:39:26 +03:00
if ( optional )
ret = clk_bulk_get_optional ( dev , num_clks , clks ) ;
else
ret = clk_bulk_get ( dev , num_clks , clks ) ;
2017-05-19 16:49:05 +03:00
if ( ! ret ) {
devres - > clks = clks ;
devres - > num_clks = num_clks ;
devres_add ( dev , devres ) ;
} else {
devres_free ( devres ) ;
}
return ret ;
}
2019-06-19 12:39:26 +03:00
int __must_check devm_clk_bulk_get ( struct device * dev , int num_clks ,
struct clk_bulk_data * clks )
{
return __devm_clk_bulk_get ( dev , num_clks , clks , false ) ;
}
2017-05-19 16:49:05 +03:00
EXPORT_SYMBOL_GPL ( devm_clk_bulk_get ) ;
2019-06-19 12:39:26 +03:00
int __must_check devm_clk_bulk_get_optional ( struct device * dev , int num_clks ,
struct clk_bulk_data * clks )
{
return __devm_clk_bulk_get ( dev , num_clks , clks , true ) ;
}
EXPORT_SYMBOL_GPL ( devm_clk_bulk_get_optional ) ;
2021-07-31 05:59:50 +03:00
static void devm_clk_bulk_release_all ( struct device * dev , void * res )
{
struct clk_bulk_devres * devres = res ;
clk_bulk_put_all ( devres - > num_clks , devres - > clks ) ;
}
2018-08-31 07:45:55 +03:00
int __must_check devm_clk_bulk_get_all ( struct device * dev ,
struct clk_bulk_data * * clks )
{
struct clk_bulk_devres * devres ;
int ret ;
2021-07-31 05:59:50 +03:00
devres = devres_alloc ( devm_clk_bulk_release_all ,
2018-08-31 07:45:55 +03:00
sizeof ( * devres ) , GFP_KERNEL ) ;
if ( ! devres )
return - ENOMEM ;
ret = clk_bulk_get_all ( dev , & devres - > clks ) ;
if ( ret > 0 ) {
* clks = devres - > clks ;
devres - > num_clks = ret ;
devres_add ( dev , devres ) ;
} else {
devres_free ( devres ) ;
}
return ret ;
}
EXPORT_SYMBOL_GPL ( devm_clk_bulk_get_all ) ;
2012-09-11 22:56:23 +04:00
static int devm_clk_match ( struct device * dev , void * res , void * data )
{
struct clk * * c = res ;
if ( ! c | | ! * c ) {
WARN_ON ( ! c | | ! * c ) ;
return 0 ;
}
return * c = = data ;
}
void devm_clk_put ( struct device * dev , struct clk * clk )
{
int ret ;
2012-09-19 15:43:21 +04:00
ret = devres_release ( dev , devm_clk_release , devm_clk_match , clk ) ;
2012-09-11 22:56:23 +04:00
WARN_ON ( ret ) ;
}
EXPORT_SYMBOL ( devm_clk_put ) ;
2016-12-05 08:23:20 +03:00
struct clk * devm_get_clk_from_child ( struct device * dev ,
struct device_node * np , const char * con_id )
{
struct clk * * ptr , * clk ;
ptr = devres_alloc ( devm_clk_release , sizeof ( * ptr ) , GFP_KERNEL ) ;
if ( ! ptr )
return ERR_PTR ( - ENOMEM ) ;
clk = of_clk_get_by_name ( np , con_id ) ;
if ( ! IS_ERR ( clk ) ) {
* ptr = clk ;
devres_add ( dev , ptr ) ;
} else {
devres_free ( ptr ) ;
}
return clk ;
}
EXPORT_SYMBOL ( devm_get_clk_from_child ) ;