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 .
*
* 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 .
*
* 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>
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 ;
2008-11-09 19:32:46 +03:00
int match , best = 0 ;
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
if ( match > best ) {
2011-04-30 13:14:08 +04:00
cl = p ;
2010-03-09 13:54:30 +03:00
if ( match ! = 3 )
best = match ;
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
}
2009-03-07 14:55:49 +03:00
struct clk * clk_get_sys ( 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 ;
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 ) ;
if ( cl & & ! __clk_get ( cl - > clk ) )
cl = NULL ;
2008-11-09 19:32:46 +03:00
mutex_unlock ( & clocks_mutex ) ;
2011-04-30 13:14:08 +04:00
return cl ? cl - > clk : ERR_PTR ( - ENOENT ) ;
2008-11-09 19:32:46 +03:00
}
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 ;
return clk_get_sys ( dev_id , con_id ) ;
}
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 ) ;
void clkdev_add ( struct clk_lookup * cl )
{
mutex_lock ( & clocks_mutex ) ;
list_add_tail ( & cl - > node , & clocks ) ;
mutex_unlock ( & clocks_mutex ) ;
}
EXPORT_SYMBOL ( clkdev_add ) ;
2010-01-12 15:28:00 +03:00
void __init clkdev_add_table ( struct clk_lookup * cl , size_t num )
{
mutex_lock ( & clocks_mutex ) ;
while ( num - - ) {
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 ] ;
} ;
2010-11-17 12:04:33 +03:00
struct clk_lookup * __init_refok
clkdev_alloc ( struct clk * clk , const char * con_id , const char * dev_fmt , . . . )
2008-11-09 19:32:46 +03:00
{
struct clk_lookup_alloc * cla ;
2010-11-17 12:04:33 +03:00
cla = __clkdev_alloc ( sizeof ( * cla ) ) ;
2008-11-09 19:32:46 +03:00
if ( ! cla )
return NULL ;
cla - > cl . clk = clk ;
if ( con_id ) {
strlcpy ( cla - > con_id , con_id , sizeof ( cla - > con_id ) ) ;
cla - > cl . con_id = cla - > con_id ;
}
if ( dev_fmt ) {
va_list ap ;
va_start ( ap , dev_fmt ) ;
vscnprintf ( cla - > dev_id , sizeof ( cla - > dev_id ) , dev_fmt , ap ) ;
cla - > cl . dev_id = cla - > dev_id ;
va_end ( ap ) ;
}
return & cla - > cl ;
}
EXPORT_SYMBOL ( clkdev_alloc ) ;
2009-06-03 20:43:14 +04:00
int clk_add_alias ( const char * alias , const char * alias_dev_name , char * id ,
struct device * dev )
{
struct clk * r = clk_get ( dev , id ) ;
struct clk_lookup * l ;
if ( IS_ERR ( r ) )
return PTR_ERR ( r ) ;
l = clkdev_alloc ( r , alias , alias_dev_name ) ;
clk_put ( r ) ;
if ( ! l )
return - ENODEV ;
clkdev_add ( l ) ;
return 0 ;
}
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 ) ;