2018-12-11 09:43:03 -08:00
// SPDX-License-Identifier: GPL-2.0
2012-09-11 19:56:23 +01:00
# include <linux/clk.h>
# include <linux/device.h>
# include <linux/export.h>
# include <linux/gfp.h>
2022-05-20 09:57:35 +02:00
struct devm_clk_state {
struct clk * clk ;
void ( * exit ) ( struct clk * clk ) ;
} ;
2012-09-11 19:56:23 +01:00
static void devm_clk_release ( struct device * dev , void * res )
{
2022-06-20 19:18:15 +02:00
struct devm_clk_state * state = res ;
2022-05-20 09:57:35 +02:00
if ( state - > exit )
state - > exit ( state - > clk ) ;
clk_put ( state - > clk ) ;
2012-09-11 19:56:23 +01:00
}
2022-05-20 09:57:35 +02: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 19:56:23 +01:00
{
2022-05-20 09:57:35 +02:00
struct devm_clk_state * state ;
struct clk * clk ;
int ret ;
2012-09-11 19:56:23 +01:00
2022-05-20 09:57:35 +02:00
state = devres_alloc ( devm_clk_release , sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
2012-09-11 19:56:23 +01:00
return ERR_PTR ( - ENOMEM ) ;
2022-05-20 09:57:35 +02: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 19:56:23 +01:00
}
2022-05-20 09:57:35 +02:00
state - > clk = clk ;
state - > exit = exit ;
devres_add ( dev , state ) ;
2012-09-11 19:56:23 +01:00
return clk ;
2022-05-20 09:57:35 +02: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 19:56:23 +01:00
}
EXPORT_SYMBOL ( devm_clk_get ) ;
2022-05-20 09:57:36 +02: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 11:13:09 +00:00
struct clk * devm_clk_get_optional ( struct device * dev , const char * id )
{
2022-05-20 09:57:35 +02:00
return __devm_clk_get ( dev , id , clk_get_optional , NULL , NULL ) ;
2018-12-03 11:13:09 +00:00
}
EXPORT_SYMBOL ( devm_clk_get_optional ) ;
2022-05-20 09:57:36 +02: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 21:49:05 +08: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 11:39:26 +02:00
static int __devm_clk_bulk_get ( struct device * dev , int num_clks ,
struct clk_bulk_data * clks , bool optional )
2017-05-19 21:49:05 +08: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 11:39:26 +02:00
if ( optional )
ret = clk_bulk_get_optional ( dev , num_clks , clks ) ;
else
ret = clk_bulk_get ( dev , num_clks , clks ) ;
2017-05-19 21:49:05 +08:00
if ( ! ret ) {
devres - > clks = clks ;
devres - > num_clks = num_clks ;
devres_add ( dev , devres ) ;
} else {
devres_free ( devres ) ;
}
return ret ;
}
2019-06-19 11:39:26 +02: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 21:49:05 +08:00
EXPORT_SYMBOL_GPL ( devm_clk_bulk_get ) ;
2019-06-19 11:39:26 +02: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-30 19:59:50 -07: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 12:45:55 +08: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-30 19:59:50 -07:00
devres = devres_alloc ( devm_clk_bulk_release_all ,
2018-08-31 12:45:55 +08: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 19:56:23 +01: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 12:43:21 +01:00
ret = devres_release ( dev , devm_clk_release , devm_clk_match , clk ) ;
2012-09-11 19:56:23 +01:00
WARN_ON ( ret ) ;
}
EXPORT_SYMBOL ( devm_clk_put ) ;
2016-12-05 05:23:20 +00:00
struct clk * devm_get_clk_from_child ( struct device * dev ,
struct device_node * np , const char * con_id )
{
2023-08-05 11:48:47 +03:00
struct devm_clk_state * state ;
struct clk * clk ;
2016-12-05 05:23:20 +00:00
2023-08-05 11:48:47 +03:00
state = devres_alloc ( devm_clk_release , sizeof ( * state ) , GFP_KERNEL ) ;
if ( ! state )
2016-12-05 05:23:20 +00:00
return ERR_PTR ( - ENOMEM ) ;
clk = of_clk_get_by_name ( np , con_id ) ;
if ( ! IS_ERR ( clk ) ) {
2023-08-05 11:48:47 +03:00
state - > clk = clk ;
devres_add ( dev , state ) ;
2016-12-05 05:23:20 +00:00
} else {
2023-08-05 11:48:47 +03:00
devres_free ( state ) ;
2016-12-05 05:23:20 +00:00
}
return clk ;
}
EXPORT_SYMBOL ( devm_get_clk_from_child ) ;