2005-04-17 02:20:36 +04:00
/*
2005-07-10 22:58:14 +04:00
* linux / arch / arm / plat - omap / clock . c
2005-04-17 02:20:36 +04:00
*
2008-07-03 13:24:41 +04:00
* Copyright ( C ) 2004 - 2008 Nokia corporation
2005-04-17 02:20:36 +04:00
* Written by Tuukka Tikkanen < tuukka . tikkanen @ elektrobit . com >
*
2005-11-10 17:26:50 +03:00
* Modified for omap shared clock framework by Tony Lindgren < tony @ atomide . com >
*
2005-04-17 02:20:36 +04:00
* 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 .
*/
# include <linux/kernel.h>
2005-11-10 17:26:50 +03:00
# include <linux/init.h>
2005-04-17 02:20:36 +04:00
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
2005-10-31 02:03:48 +03:00
# include <linux/string.h>
2006-01-07 19:15:52 +03:00
# include <linux/clk.h>
2006-01-12 21:42:23 +03:00
# include <linux/mutex.h>
2008-05-22 19:38:50 +04:00
# include <linux/cpufreq.h>
2008-07-03 13:24:41 +04:00
# include <linux/debugfs.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
2009-10-20 20:40:47 +04:00
# include <plat/clock.h>
2005-04-17 02:20:36 +04:00
2006-06-27 03:16:22 +04:00
static LIST_HEAD ( clocks ) ;
2006-01-12 21:42:23 +03:00
static DEFINE_MUTEX ( clocks_mutex ) ;
2006-06-27 03:16:22 +04:00
static DEFINE_SPINLOCK ( clockfw_lock ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
static struct clk_functions * arch_clock ;
2005-04-17 02:20:36 +04:00
2010-05-19 04:40:26 +04:00
/*
2006-01-18 02:27:09 +03:00
* Standard clock functions defined in include / linux / clk . h
2010-05-19 04:40:26 +04:00
*/
2005-04-17 02:20:36 +04:00
int clk_enable ( struct clk * clk )
{
unsigned long flags ;
2005-11-10 17:26:50 +03:00
int ret = 0 ;
2005-04-17 02:20:36 +04:00
2006-04-02 20:46:20 +04:00
if ( clk = = NULL | | IS_ERR ( clk ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & clockfw_lock , flags ) ;
2006-01-18 02:27:09 +03:00
if ( arch_clock - > clk_enable )
2005-11-10 17:26:50 +03:00
ret = arch_clock - > clk_enable ( clk ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
2005-11-10 17:26:50 +03:00
2005-04-17 02:20:36 +04:00
return ret ;
}
EXPORT_SYMBOL ( clk_enable ) ;
void clk_disable ( struct clk * clk )
{
unsigned long flags ;
2006-04-02 20:46:20 +04:00
if ( clk = = NULL | | IS_ERR ( clk ) )
return ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & clockfw_lock , flags ) ;
2007-08-07 16:20:00 +04:00
if ( clk - > usecount = = 0 ) {
printk ( KERN_ERR " Trying disable clock %s with 0 usecount \n " ,
clk - > name ) ;
WARN_ON ( 1 ) ;
goto out ;
}
2006-01-18 02:27:09 +03:00
if ( arch_clock - > clk_disable )
2005-11-10 17:26:50 +03:00
arch_clock - > clk_disable ( clk ) ;
2007-08-07 16:20:00 +04:00
out :
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
}
EXPORT_SYMBOL ( clk_disable ) ;
2005-11-10 17:26:50 +03:00
unsigned long clk_get_rate ( struct clk * clk )
2005-04-17 02:20:36 +04:00
{
2005-11-10 17:26:50 +03:00
unsigned long flags ;
unsigned long ret = 0 ;
2005-04-17 02:20:36 +04:00
2006-04-02 20:46:20 +04:00
if ( clk = = NULL | | IS_ERR ( clk ) )
return 0 ;
2005-11-10 17:26:50 +03:00
spin_lock_irqsave ( & clockfw_lock , flags ) ;
ret = clk - > rate ;
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 17:26:50 +03:00
EXPORT_SYMBOL ( clk_get_rate ) ;
2005-04-17 02:20:36 +04:00
2010-05-19 04:40:26 +04:00
/*
2006-01-18 02:27:09 +03:00
* Optional clock functions defined in include / linux / clk . h
2010-05-19 04:40:26 +04:00
*/
2005-07-10 22:58:18 +04:00
2005-04-17 02:20:36 +04:00
long clk_round_rate ( struct clk * clk , unsigned long rate )
{
2005-11-10 17:26:50 +03:00
unsigned long flags ;
long ret = 0 ;
2005-04-17 02:20:36 +04:00
2006-04-02 20:46:20 +04:00
if ( clk = = NULL | | IS_ERR ( clk ) )
return ret ;
2005-11-10 17:26:50 +03:00
spin_lock_irqsave ( & clockfw_lock , flags ) ;
if ( arch_clock - > clk_round_rate )
ret = arch_clock - > clk_round_rate ( clk , rate ) ;
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( clk_round_rate ) ;
2005-11-10 17:26:50 +03:00
int clk_set_rate ( struct clk * clk , unsigned long rate )
2005-04-17 02:20:36 +04:00
{
2005-11-10 17:26:50 +03:00
unsigned long flags ;
2006-04-02 20:46:20 +04:00
int ret = - EINVAL ;
if ( clk = = NULL | | IS_ERR ( clk ) )
return ret ;
2005-07-10 22:58:18 +04:00
2005-11-10 17:26:50 +03:00
spin_lock_irqsave ( & clockfw_lock , flags ) ;
if ( arch_clock - > clk_set_rate )
ret = arch_clock - > clk_set_rate ( clk , rate ) ;
2009-01-29 22:33:19 +03:00
if ( ret = = 0 ) {
if ( clk - > recalc )
2009-02-12 13:12:59 +03:00
clk - > rate = clk - > recalc ( clk ) ;
2009-01-31 13:05:51 +03:00
propagate_rate ( clk ) ;
2009-01-29 22:33:19 +03:00
}
2005-11-10 17:26:50 +03:00
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 17:26:50 +03:00
EXPORT_SYMBOL ( clk_set_rate ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
int clk_set_parent ( struct clk * clk , struct clk * parent )
2005-04-17 02:20:36 +04:00
{
2005-11-10 17:26:50 +03:00
unsigned long flags ;
2006-04-02 20:46:20 +04:00
int ret = - EINVAL ;
if ( clk = = NULL | | IS_ERR ( clk ) | | parent = = NULL | | IS_ERR ( parent ) )
return ret ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
spin_lock_irqsave ( & clockfw_lock , flags ) ;
2009-02-24 15:46:31 +03:00
if ( clk - > usecount = = 0 ) {
if ( arch_clock - > clk_set_parent )
ret = arch_clock - > clk_set_parent ( clk , parent ) ;
if ( ret = = 0 ) {
if ( clk - > recalc )
clk - > rate = clk - > recalc ( clk ) ;
propagate_rate ( clk ) ;
}
} else
ret = - EBUSY ;
2005-11-10 17:26:50 +03:00
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
return ret ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 17:26:50 +03:00
EXPORT_SYMBOL ( clk_set_parent ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
struct clk * clk_get_parent ( struct clk * clk )
2005-04-17 02:20:36 +04:00
{
2009-02-08 20:49:22 +03:00
return clk - > parent ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 17:26:50 +03:00
EXPORT_SYMBOL ( clk_get_parent ) ;
2005-04-17 02:20:36 +04:00
2010-05-19 04:40:26 +04:00
/*
2005-11-10 17:26:50 +03:00
* OMAP specific clock functions shared between omap1 and omap2
2010-05-19 04:40:26 +04:00
*/
2005-04-17 02:20:36 +04:00
2010-01-27 06:13:11 +03:00
int __initdata mpurate ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
/*
* By default we use the rate set by the bootloader .
* You can override this with mpurate = cmdline option .
*/
static int __init omap_clk_setup ( char * str )
2005-04-17 02:20:36 +04:00
{
2005-11-10 17:26:50 +03:00
get_option ( & str , & mpurate ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
if ( ! mpurate )
return 1 ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
if ( mpurate < 1000 )
mpurate * = 1000000 ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
return 1 ;
2005-04-17 02:20:36 +04:00
}
2005-11-10 17:26:50 +03:00
__setup ( " mpurate= " , omap_clk_setup ) ;
2005-04-17 02:20:36 +04:00
2005-11-10 17:26:50 +03:00
/* Used for clocks that always have same value as the parent clock */
2009-02-12 13:12:59 +03:00
unsigned long followparent_recalc ( struct clk * clk )
2005-04-17 02:20:36 +04:00
{
2009-02-12 13:12:59 +03:00
return clk - > parent - > rate ;
2005-04-17 02:20:36 +04:00
}
2010-01-27 06:12:57 +03:00
/*
* Used for clocks that have the same value as the parent clock ,
* divided by some factor
*/
unsigned long omap_fixed_divisor_recalc ( struct clk * clk )
{
WARN_ON ( ! clk - > fixed_div ) ;
return clk - > parent - > rate / clk - > fixed_div ;
}
2009-01-31 13:05:51 +03:00
void clk_reparent ( struct clk * child , struct clk * parent )
{
list_del_init ( & child - > sibling ) ;
if ( parent )
list_add ( & child - > sibling , & parent - > children ) ;
child - > parent = parent ;
/* now do the debugfs renaming to reattach the child
to the proper parent */
}
2005-11-10 17:26:50 +03:00
/* Propagate rate to children */
2010-05-19 04:40:26 +04:00
void propagate_rate ( struct clk * tclk )
2005-04-17 02:20:36 +04:00
{
2005-11-10 17:26:50 +03:00
struct clk * clkp ;
2005-04-17 02:20:36 +04:00
2009-01-31 13:05:51 +03:00
list_for_each_entry ( clkp , & tclk - > children , sibling ) {
2008-11-13 16:44:15 +03:00
if ( clkp - > recalc )
2009-02-12 13:12:59 +03:00
clkp - > rate = clkp - > recalc ( clkp ) ;
2009-01-31 13:05:51 +03:00
propagate_rate ( clkp ) ;
2005-11-10 17:26:50 +03:00
}
2005-04-17 02:20:36 +04:00
}
2009-01-31 13:05:51 +03:00
static LIST_HEAD ( root_clks ) ;
2008-03-18 11:35:15 +03:00
/**
* recalculate_root_clocks - recalculate and propagate all root clocks
*
* Recalculates all root clocks ( clocks with no parent ) , which if the
* clock ' s . recalc is set correctly , should also propagate their rates .
* Called at init .
*/
void recalculate_root_clocks ( void )
{
struct clk * clkp ;
2009-01-31 13:05:51 +03:00
list_for_each_entry ( clkp , & root_clks , sibling ) {
if ( clkp - > recalc )
2009-02-12 13:12:59 +03:00
clkp - > rate = clkp - > recalc ( clkp ) ;
2009-01-31 13:05:51 +03:00
propagate_rate ( clkp ) ;
2008-03-18 11:35:15 +03:00
}
}
2009-04-23 05:48:53 +04:00
/**
2009-05-13 03:50:30 +04:00
* clk_preinit - initialize any fields in the struct clk before clk init
2009-04-23 05:48:53 +04:00
* @ clk : struct clk * to initialize
*
* Initialize any struct clk fields needed before normal clk initialization
* can run . No return value .
*/
2009-05-13 03:50:30 +04:00
void clk_preinit ( struct clk * clk )
2009-01-31 13:05:51 +03:00
{
INIT_LIST_HEAD ( & clk - > children ) ;
}
2005-04-17 02:20:36 +04:00
int clk_register ( struct clk * clk )
{
2006-04-02 20:46:20 +04:00
if ( clk = = NULL | | IS_ERR ( clk ) )
return - EINVAL ;
2009-01-22 19:08:04 +03:00
/*
* trap out already registered clocks
*/
if ( clk - > node . next | | clk - > node . prev )
return 0 ;
2006-01-12 21:42:23 +03:00
mutex_lock ( & clocks_mutex ) ;
2009-01-31 13:05:51 +03:00
if ( clk - > parent )
list_add ( & clk - > sibling , & clk - > parent - > children ) ;
else
list_add ( & clk - > sibling , & root_clks ) ;
2005-04-17 02:20:36 +04:00
list_add ( & clk - > node , & clocks ) ;
if ( clk - > init )
clk - > init ( clk ) ;
2006-01-12 21:42:23 +03:00
mutex_unlock ( & clocks_mutex ) ;
2005-11-10 17:26:50 +03:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
EXPORT_SYMBOL ( clk_register ) ;
void clk_unregister ( struct clk * clk )
{
2006-04-02 20:46:20 +04:00
if ( clk = = NULL | | IS_ERR ( clk ) )
return ;
2006-01-12 21:42:23 +03:00
mutex_lock ( & clocks_mutex ) ;
2009-01-31 13:05:51 +03:00
list_del ( & clk - > sibling ) ;
2005-04-17 02:20:36 +04:00
list_del ( & clk - > node ) ;
2006-01-12 21:42:23 +03:00
mutex_unlock ( & clocks_mutex ) ;
2005-04-17 02:20:36 +04:00
}
EXPORT_SYMBOL ( clk_unregister ) ;
2008-03-18 11:35:15 +03:00
void clk_enable_init_clocks ( void )
{
struct clk * clkp ;
list_for_each_entry ( clkp , & clocks , node ) {
if ( clkp - > flags & ENABLE_ON_INIT )
clk_enable ( clkp ) ;
}
}
2010-02-23 08:09:29 +03:00
/**
* omap_clk_get_by_name - locate OMAP struct clk by its name
* @ name : name of the struct clk to locate
*
* Locate an OMAP struct clk by its name . Assumes that struct clk
* names are unique . Returns NULL if not found or a pointer to the
* struct clk if found .
*/
struct clk * omap_clk_get_by_name ( const char * name )
{
struct clk * c ;
struct clk * ret = NULL ;
mutex_lock ( & clocks_mutex ) ;
list_for_each_entry ( c , & clocks , node ) {
if ( ! strcmp ( c - > name , name ) ) {
ret = c ;
break ;
}
}
mutex_unlock ( & clocks_mutex ) ;
return ret ;
}
2008-11-04 19:35:03 +03:00
/*
* Low level helpers
*/
static int clkll_enable_null ( struct clk * clk )
{
return 0 ;
}
static void clkll_disable_null ( struct clk * clk )
{
}
const struct clkops clkops_null = {
. enable = clkll_enable_null ,
. disable = clkll_disable_null ,
} ;
2010-02-23 08:09:40 +03:00
/*
* Dummy clock
*
* Used for clock aliases that are needed on some OMAPs , but not others
*/
struct clk dummy_ck = {
. name = " dummy " ,
. ops = & clkops_null ,
} ;
2008-03-18 11:35:15 +03:00
# ifdef CONFIG_CPU_FREQ
void clk_init_cpufreq_table ( struct cpufreq_frequency_table * * table )
{
unsigned long flags ;
spin_lock_irqsave ( & clockfw_lock , flags ) ;
if ( arch_clock - > clk_init_cpufreq_table )
arch_clock - > clk_init_cpufreq_table ( table ) ;
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
}
2010-01-09 01:23:16 +03:00
void clk_exit_cpufreq_table ( struct cpufreq_frequency_table * * table )
{
unsigned long flags ;
spin_lock_irqsave ( & clockfw_lock , flags ) ;
if ( arch_clock - > clk_exit_cpufreq_table )
arch_clock - > clk_exit_cpufreq_table ( table ) ;
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
}
2008-03-18 11:35:15 +03:00
# endif
2010-05-19 04:40:26 +04:00
/*
*
*/
2005-07-10 22:58:18 +04:00
2006-09-25 14:27:20 +04:00
# ifdef CONFIG_OMAP_RESET_CLOCKS
/*
* Disable any unused clocks left on by the bootloader
*/
static int __init clk_disable_unused ( void )
{
struct clk * ck ;
unsigned long flags ;
list_for_each_entry ( ck , & clocks , node ) {
2008-11-04 19:35:03 +03:00
if ( ck - > ops = = & clkops_null )
continue ;
2010-05-19 04:40:26 +04:00
if ( ck - > usecount > 0 | | ! ck - > enable_reg )
2006-09-25 14:27:20 +04:00
continue ;
spin_lock_irqsave ( & clockfw_lock , flags ) ;
if ( arch_clock - > clk_disable_unused )
arch_clock - > clk_disable_unused ( ck ) ;
spin_unlock_irqrestore ( & clockfw_lock , flags ) ;
}
return 0 ;
}
late_initcall ( clk_disable_unused ) ;
# endif
2005-11-10 17:26:50 +03:00
int __init clk_init ( struct clk_functions * custom_clocks )
2005-07-10 22:58:18 +04:00
{
2005-11-10 17:26:50 +03:00
if ( ! custom_clocks ) {
printk ( KERN_ERR " No custom clock functions registered \n " ) ;
BUG ( ) ;
2005-07-10 22:58:18 +04:00
}
2005-11-10 17:26:50 +03:00
arch_clock = custom_clocks ;
2005-07-10 22:58:18 +04:00
return 0 ;
}
2008-03-18 11:35:15 +03:00
2008-07-03 13:24:41 +04:00
# if defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS)
/*
* debugfs support to trace clock tree hierarchy and attributes
*/
static struct dentry * clk_debugfs_root ;
static int clk_debugfs_register_one ( struct clk * c )
{
int err ;
2010-01-31 13:00:54 +03:00
struct dentry * d , * child , * child_tmp ;
2008-07-03 13:24:41 +04:00
struct clk * pa = c - > parent ;
char s [ 255 ] ;
char * p = s ;
p + = sprintf ( p , " %s " , c - > name ) ;
d = debugfs_create_dir ( s , pa ? pa - > dent : clk_debugfs_root ) ;
2008-11-05 00:35:07 +03:00
if ( ! d )
return - ENOMEM ;
2008-07-03 13:24:41 +04:00
c - > dent = d ;
d = debugfs_create_u8 ( " usecount " , S_IRUGO , c - > dent , ( u8 * ) & c - > usecount ) ;
2008-11-05 00:35:07 +03:00
if ( ! d ) {
err = - ENOMEM ;
2008-07-03 13:24:41 +04:00
goto err_out ;
}
d = debugfs_create_u32 ( " rate " , S_IRUGO , c - > dent , ( u32 * ) & c - > rate ) ;
2008-11-05 00:35:07 +03:00
if ( ! d ) {
err = - ENOMEM ;
2008-07-03 13:24:41 +04:00
goto err_out ;
}
d = debugfs_create_x32 ( " flags " , S_IRUGO , c - > dent , ( u32 * ) & c - > flags ) ;
2008-11-05 00:35:07 +03:00
if ( ! d ) {
err = - ENOMEM ;
2008-07-03 13:24:41 +04:00
goto err_out ;
}
return 0 ;
err_out :
d = c - > dent ;
2010-01-31 13:00:54 +03:00
list_for_each_entry_safe ( child , child_tmp , & d - > d_subdirs , d_u . d_child )
2008-07-03 13:24:41 +04:00
debugfs_remove ( child ) ;
debugfs_remove ( c - > dent ) ;
return err ;
}
static int clk_debugfs_register ( struct clk * c )
{
int err ;
struct clk * pa = c - > parent ;
if ( pa & & ! pa - > dent ) {
err = clk_debugfs_register ( pa ) ;
if ( err )
return err ;
}
if ( ! c - > dent ) {
err = clk_debugfs_register_one ( c ) ;
if ( err )
return err ;
}
return 0 ;
}
static int __init clk_debugfs_init ( void )
{
struct clk * c ;
struct dentry * d ;
int err ;
d = debugfs_create_dir ( " clock " , NULL ) ;
2008-11-05 00:35:07 +03:00
if ( ! d )
return - ENOMEM ;
2008-07-03 13:24:41 +04:00
clk_debugfs_root = d ;
list_for_each_entry ( c , & clocks , node ) {
err = clk_debugfs_register ( c ) ;
if ( err )
goto err_out ;
}
return 0 ;
err_out :
2009-09-03 21:14:06 +04:00
debugfs_remove_recursive ( clk_debugfs_root ) ;
2008-07-03 13:24:41 +04:00
return err ;
}
late_initcall ( clk_debugfs_init ) ;
# endif /* defined(CONFIG_PM_DEBUG) && defined(CONFIG_DEBUG_FS) */