2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2008-11-09 19:32:46 +03:00
/*
2010-11-17 12:04:33 +03:00
* drivers / clk / clkdev . c
2008-11-09 19:32:46 +03:00
*
* Copyright ( C ) 2008 Russell King .
*
* Helper for the clk API to assist looking up a struct clk .
*/
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/string.h>
# include <linux/mutex.h>
2009-08-05 02:38:06 +04:00
# include <linux/clk.h>
2010-11-17 12:04:33 +03:00
# include <linux/clkdev.h>
2015-01-23 14:03:30 +03:00
# include <linux/clk-provider.h>
2012-04-09 23:50:06 +04:00
# include <linux/of.h>
2008-11-09 19:32:46 +03:00
2013-08-23 19:03:44 +04:00
# include "clk.h"
2008-11-09 19:32:46 +03:00
static LIST_HEAD ( clocks ) ;
static DEFINE_MUTEX ( clocks_mutex ) ;
2009-01-24 13:14:37 +03:00
/*
* Find the correct struct clk for the device and connection ID .
* We do slightly fuzzy matching here :
* An entry with a NULL ID is assumed to be a wildcard .
* If an entry has a device ID , it must match
* If an entry has a connection ID , it must match
* Then we take the most specific entry - with the following
2010-01-18 18:02:48 +03:00
* order of precedence : dev + con > dev only > con only .
2009-01-24 13:14:37 +03:00
*/
2011-04-30 13:14:08 +04:00
static struct clk_lookup * clk_find ( const char * dev_id , const char * con_id )
2008-11-09 19:32:46 +03:00
{
2011-04-30 13:14:08 +04:00
struct clk_lookup * p , * cl = NULL ;
2012-04-19 07:23:25 +04:00
int match , best_found = 0 , best_possible = 0 ;
if ( dev_id )
best_possible + = 2 ;
if ( con_id )
best_possible + = 1 ;
2008-11-09 19:32:46 +03:00
2019-04-12 21:31:42 +03:00
lockdep_assert_held ( & clocks_mutex ) ;
2008-11-09 19:32:46 +03:00
list_for_each_entry ( p , & clocks , node ) {
match = 0 ;
2009-01-24 13:14:37 +03:00
if ( p - > dev_id ) {
if ( ! dev_id | | strcmp ( p - > dev_id , dev_id ) )
continue ;
match + = 2 ;
}
if ( p - > con_id ) {
if ( ! con_id | | strcmp ( p - > con_id , con_id ) )
continue ;
match + = 1 ;
}
2008-11-09 19:32:46 +03:00
2012-04-19 07:23:25 +04:00
if ( match > best_found ) {
2011-04-30 13:14:08 +04:00
cl = p ;
2012-04-19 07:23:25 +04:00
if ( match ! = best_possible )
best_found = match ;
2010-03-09 13:54:30 +03:00
else
break ;
2008-11-09 19:32:46 +03:00
}
}
2011-04-30 13:14:08 +04:00
return cl ;
2008-11-09 19:32:46 +03:00
}
2019-04-12 21:31:48 +03:00
struct clk_hw * clk_find_hw ( const char * dev_id , const char * con_id )
2008-11-09 19:32:46 +03:00
{
2011-04-30 13:14:08 +04:00
struct clk_lookup * cl ;
2019-04-12 21:31:43 +03:00
struct clk_hw * hw = ERR_PTR ( - ENOENT ) ;
2008-11-09 19:32:46 +03:00
mutex_lock ( & clocks_mutex ) ;
2011-04-30 13:14:08 +04:00
cl = clk_find ( dev_id , con_id ) ;
2019-04-12 21:31:43 +03:00
if ( cl )
hw = cl - > clk_hw ;
2008-11-09 19:32:46 +03:00
mutex_unlock ( & clocks_mutex ) ;
2019-04-12 21:31:43 +03:00
return hw ;
}
static struct clk * __clk_get_sys ( struct device * dev , const char * dev_id ,
const char * con_id )
{
struct clk_hw * hw = clk_find_hw ( dev_id , con_id ) ;
return clk_hw_create_clk ( dev , hw , dev_id , con_id ) ;
2008-11-09 19:32:46 +03:00
}
2018-12-11 19:34:16 +03:00
struct clk * clk_get_sys ( const char * dev_id , const char * con_id )
{
return __clk_get_sys ( NULL , dev_id , con_id ) ;
}
2009-03-07 14:55:49 +03:00
EXPORT_SYMBOL ( clk_get_sys ) ;
struct clk * clk_get ( struct device * dev , const char * con_id )
{
const char * dev_id = dev ? dev_name ( dev ) : NULL ;
2018-12-19 21:59:55 +03:00
struct clk_hw * hw ;
2012-04-09 23:50:06 +04:00
2018-06-28 17:42:20 +03:00
if ( dev & & dev - > of_node ) {
2018-12-19 21:59:55 +03:00
hw = of_clk_get_hw ( dev - > of_node , 0 , con_id ) ;
if ( ! IS_ERR ( hw ) | | PTR_ERR ( hw ) = = - EPROBE_DEFER )
2018-12-11 19:34:16 +03:00
return clk_hw_create_clk ( dev , hw , dev_id , con_id ) ;
2012-04-09 23:50:06 +04:00
}
2009-03-07 14:55:49 +03:00
2018-12-11 19:34:16 +03:00
return __clk_get_sys ( dev , dev_id , con_id ) ;
2009-03-07 14:55:49 +03:00
}
2008-11-09 19:32:46 +03:00
EXPORT_SYMBOL ( clk_get ) ;
void clk_put ( struct clk * clk )
{
__clk_put ( clk ) ;
}
EXPORT_SYMBOL ( clk_put ) ;
2015-03-02 18:45:41 +03:00
static void __clkdev_add ( struct clk_lookup * cl )
2008-11-09 19:32:46 +03:00
{
mutex_lock ( & clocks_mutex ) ;
list_add_tail ( & cl - > node , & clocks ) ;
mutex_unlock ( & clocks_mutex ) ;
}
2015-03-02 18:45:41 +03:00
void clkdev_add ( struct clk_lookup * cl )
{
if ( ! cl - > clk_hw )
cl - > clk_hw = __clk_get_hw ( cl - > clk ) ;
__clkdev_add ( cl ) ;
}
2008-11-09 19:32:46 +03:00
EXPORT_SYMBOL ( clkdev_add ) ;
2015-03-10 17:34:00 +03:00
void clkdev_add_table ( struct clk_lookup * cl , size_t num )
2010-01-12 15:28:00 +03:00
{
mutex_lock ( & clocks_mutex ) ;
while ( num - - ) {
2015-03-02 18:45:41 +03:00
cl - > clk_hw = __clk_get_hw ( cl - > clk ) ;
2010-01-12 15:28:00 +03:00
list_add_tail ( & cl - > node , & clocks ) ;
cl + + ;
}
mutex_unlock ( & clocks_mutex ) ;
}
2008-11-09 19:32:46 +03:00
# define MAX_DEV_ID 20
# define MAX_CON_ID 16
struct clk_lookup_alloc {
struct clk_lookup cl ;
char dev_id [ MAX_DEV_ID ] ;
char con_id [ MAX_CON_ID ] ;
} ;
2016-08-03 00:03:33 +03:00
static struct clk_lookup * __ref
2015-03-02 18:45:41 +03:00
vclkdev_alloc ( struct clk_hw * hw , const char * con_id , const char * dev_fmt ,
2012-05-02 12:30:32 +04:00
va_list ap )
2008-11-09 19:32:46 +03:00
{
struct clk_lookup_alloc * cla ;
2018-01-03 02:47:07 +03:00
cla = kzalloc ( sizeof ( * cla ) , GFP_KERNEL ) ;
2008-11-09 19:32:46 +03:00
if ( ! cla )
return NULL ;
2015-03-02 18:45:41 +03:00
cla - > cl . clk_hw = hw ;
2008-11-09 19:32:46 +03:00
if ( con_id ) {
2022-08-19 00:00:00 +03:00
strscpy ( cla - > con_id , con_id , sizeof ( cla - > con_id ) ) ;
2008-11-09 19:32:46 +03:00
cla - > cl . con_id = cla - > con_id ;
}
if ( dev_fmt ) {
vscnprintf ( cla - > dev_id , sizeof ( cla - > dev_id ) , dev_fmt , ap ) ;
cla - > cl . dev_id = cla - > dev_id ;
}
return & cla - > cl ;
}
2012-05-02 12:30:32 +04:00
2015-03-02 18:40:29 +03:00
static struct clk_lookup *
vclkdev_create ( struct clk_hw * hw , const char * con_id , const char * dev_fmt ,
va_list ap )
{
struct clk_lookup * cl ;
cl = vclkdev_alloc ( hw , con_id , dev_fmt , ap ) ;
if ( cl )
__clkdev_add ( cl ) ;
return cl ;
}
/**
* clkdev_create - allocate and add a clkdev lookup structure
* @ clk : struct clk to associate with all clk_lookups
* @ con_id : connection ID string on device
* @ dev_fmt : format string describing device name
*
* Returns a clk_lookup structure , which can be later unregistered and
* freed .
*/
struct clk_lookup * clkdev_create ( struct clk * clk , const char * con_id ,
const char * dev_fmt , . . . )
{
struct clk_lookup * cl ;
va_list ap ;
va_start ( ap , dev_fmt ) ;
cl = vclkdev_create ( __clk_get_hw ( clk ) , con_id , dev_fmt , ap ) ;
va_end ( ap ) ;
return cl ;
}
EXPORT_SYMBOL_GPL ( clkdev_create ) ;
2016-02-09 01:59:49 +03:00
/**
* clkdev_hw_create - allocate and add a clkdev lookup structure
* @ hw : struct clk_hw to associate with all clk_lookups
* @ con_id : connection ID string on device
* @ dev_fmt : format string describing device name
*
* Returns a clk_lookup structure , which can be later unregistered and
* freed .
*/
struct clk_lookup * clkdev_hw_create ( struct clk_hw * hw , const char * con_id ,
const char * dev_fmt , . . . )
{
struct clk_lookup * cl ;
va_list ap ;
va_start ( ap , dev_fmt ) ;
cl = vclkdev_create ( hw , con_id , dev_fmt , ap ) ;
va_end ( ap ) ;
return cl ;
}
EXPORT_SYMBOL_GPL ( clkdev_hw_create ) ;
2015-03-09 13:43:04 +03:00
int clk_add_alias ( const char * alias , const char * alias_dev_name ,
const char * con_id , struct device * dev )
2009-06-03 20:43:14 +04:00
{
2015-03-09 13:43:04 +03:00
struct clk * r = clk_get ( dev , con_id ) ;
2009-06-03 20:43:14 +04:00
struct clk_lookup * l ;
if ( IS_ERR ( r ) )
return PTR_ERR ( r ) ;
2015-10-20 13:49:44 +03:00
l = clkdev_create ( r , alias , alias_dev_name ? " %s " : NULL ,
alias_dev_name ) ;
2009-06-03 20:43:14 +04:00
clk_put ( r ) ;
2015-03-02 18:40:29 +03:00
return l ? 0 : - ENODEV ;
2009-06-03 20:43:14 +04:00
}
EXPORT_SYMBOL ( clk_add_alias ) ;
2008-11-09 19:32:46 +03:00
/*
* clkdev_drop - remove a clock dynamically allocated
*/
void clkdev_drop ( struct clk_lookup * cl )
{
mutex_lock ( & clocks_mutex ) ;
list_del ( & cl - > node ) ;
mutex_unlock ( & clocks_mutex ) ;
kfree ( cl ) ;
}
EXPORT_SYMBOL ( clkdev_drop ) ;
2012-05-02 12:30:32 +04:00
2016-01-26 03:21:26 +03:00
static struct clk_lookup * __clk_register_clkdev ( struct clk_hw * hw ,
const char * con_id ,
const char * dev_id , . . . )
{
struct clk_lookup * cl ;
va_list ap ;
va_start ( ap , dev_id ) ;
cl = vclkdev_create ( hw , con_id , dev_id , ap ) ;
va_end ( ap ) ;
return cl ;
}
2018-12-07 14:09:39 +03:00
static int do_clk_register_clkdev ( struct clk_hw * hw ,
struct clk_lookup * * cl , const char * con_id , const char * dev_id )
{
if ( IS_ERR ( hw ) )
return PTR_ERR ( hw ) ;
/*
* Since dev_id can be NULL , and NULL is handled specially , we must
* pass it as either a NULL format string , or with " %s " .
*/
if ( dev_id )
* cl = __clk_register_clkdev ( hw , con_id , " %s " , dev_id ) ;
else
* cl = __clk_register_clkdev ( hw , con_id , NULL ) ;
return * cl ? 0 : - ENOMEM ;
}
2012-05-02 12:30:32 +04:00
/**
* clk_register_clkdev - register one clock lookup for a struct clk
* @ clk : struct clk to associate with all clk_lookups
* @ con_id : connection ID string on device
2016-01-26 03:21:26 +03:00
* @ dev_id : string describing device name
2012-05-02 12:30:32 +04:00
*
* con_id or dev_id may be NULL as a wildcard , just as in the rest of
* clkdev .
*
* To make things easier for mass registration , we detect error clks
* from a previous clk_register ( ) call , and return the error code for
* those . This is to permit this function to be called immediately
* after clk_register ( ) .
*/
int clk_register_clkdev ( struct clk * clk , const char * con_id ,
2016-01-26 03:21:26 +03:00
const char * dev_id )
2012-05-02 12:30:32 +04:00
{
struct clk_lookup * cl ;
if ( IS_ERR ( clk ) )
return PTR_ERR ( clk ) ;
2018-12-07 14:09:39 +03:00
return do_clk_register_clkdev ( __clk_get_hw ( clk ) , & cl , con_id ,
dev_id ) ;
2012-05-02 12:30:32 +04:00
}
2015-01-23 14:03:32 +03:00
EXPORT_SYMBOL ( clk_register_clkdev ) ;
2016-02-09 01:59:49 +03:00
/**
* clk_hw_register_clkdev - register one clock lookup for a struct clk_hw
* @ hw : struct clk_hw to associate with all clk_lookups
* @ con_id : connection ID string on device
* @ dev_id : format string describing device name
*
* con_id or dev_id may be NULL as a wildcard , just as in the rest of
* clkdev .
2016-11-22 14:33:11 +03:00
*
* To make things easier for mass registration , we detect error clk_hws
* from a previous clk_hw_register_ * ( ) call , and return the error code for
* those . This is to permit this function to be called immediately
* after clk_hw_register_ * ( ) .
2016-02-09 01:59:49 +03:00
*/
int clk_hw_register_clkdev ( struct clk_hw * hw , const char * con_id ,
const char * dev_id )
{
struct clk_lookup * cl ;
2018-12-07 14:09:39 +03:00
return do_clk_register_clkdev ( hw , & cl , con_id , dev_id ) ;
}
EXPORT_SYMBOL ( clk_hw_register_clkdev ) ;
2016-11-22 14:33:11 +03:00
2022-06-23 14:57:19 +03:00
static void devm_clkdev_release ( void * res )
2018-12-07 14:09:39 +03:00
{
2022-06-23 14:57:19 +03:00
clkdev_drop ( res ) ;
2018-12-07 14:09:39 +03:00
}
2016-02-09 01:59:49 +03:00
2018-12-07 14:09:39 +03:00
/**
* devm_clk_hw_register_clkdev - managed clk lookup registration for clk_hw
* @ dev : device this lookup is bound
* @ hw : struct clk_hw to associate with all clk_lookups
* @ con_id : connection ID string on device
* @ dev_id : format string describing device name
*
* con_id or dev_id may be NULL as a wildcard , just as in the rest of
* clkdev .
*
* To make things easier for mass registration , we detect error clk_hws
* from a previous clk_hw_register_ * ( ) call , and return the error code for
* those . This is to permit this function to be called immediately
* after clk_hw_register_ * ( ) .
*/
int devm_clk_hw_register_clkdev ( struct device * dev , struct clk_hw * hw ,
const char * con_id , const char * dev_id )
{
2022-06-23 14:57:19 +03:00
struct clk_lookup * cl ;
int rval ;
rval = do_clk_register_clkdev ( hw , & cl , con_id , dev_id ) ;
if ( rval )
return rval ;
return devm_add_action_or_reset ( dev , devm_clkdev_release , cl ) ;
2018-12-07 14:09:39 +03:00
}
EXPORT_SYMBOL ( devm_clk_hw_register_clkdev ) ;