2008-07-05 10:02:47 +02:00
/*
* Based on arch / arm / plat - omap / clock . c
*
* Copyright ( C ) 2004 - 2005 Nokia corporation
* Written by Tuukka Tikkanen < tuukka . tikkanen @ elektrobit . com >
* Modified for omap shared clock framework by Tony Lindgren < tony @ atomide . com >
* Copyright 2007 Freescale Semiconductor , Inc . All Rights Reserved .
* Copyright 2008 Juergen Beisert , kernel @ pengutronix . de
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation ; either version 2
* of the License , or ( at your option ) any later version .
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston ,
* MA 02110 - 1301 , USA .
*/
/* #define DEBUG */
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/platform_device.h>
# include <linux/proc_fs.h>
# include <linux/semaphore.h>
# include <linux/string.h>
2008-08-05 16:14:15 +01:00
# include <mach/clock.h>
2009-06-03 11:22:32 +02:00
# include <mach/hardware.h>
2008-07-05 10:02:47 +02:00
static LIST_HEAD ( clocks ) ;
static DEFINE_MUTEX ( clocks_mutex ) ;
/*-------------------------------------------------------------------------
* Standard clock functions defined in include / linux / clk . h
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static void __clk_disable ( struct clk * clk )
{
if ( clk = = NULL | | IS_ERR ( clk ) )
return ;
__clk_disable ( clk - > parent ) ;
__clk_disable ( clk - > secondary ) ;
2010-01-13 17:58:04 +01:00
WARN_ON ( ! clk - > usecount ) ;
2008-07-05 10:02:47 +02:00
if ( ! ( - - clk - > usecount ) & & clk - > disable )
clk - > disable ( clk ) ;
}
static int __clk_enable ( struct clk * clk )
{
if ( clk = = NULL | | IS_ERR ( clk ) )
return - EINVAL ;
__clk_enable ( clk - > parent ) ;
__clk_enable ( clk - > secondary ) ;
if ( clk - > usecount + + = = 0 & & clk - > enable )
clk - > enable ( clk ) ;
return 0 ;
}
/* This function increments the reference count on the clock and enables the
* clock if not already enabled . The parent clock tree is recursively enabled
*/
int clk_enable ( struct clk * clk )
{
int ret = 0 ;
if ( clk = = NULL | | IS_ERR ( clk ) )
return - EINVAL ;
mutex_lock ( & clocks_mutex ) ;
ret = __clk_enable ( clk ) ;
mutex_unlock ( & clocks_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_enable ) ;
/* This function decrements the reference count on the clock and disables
* the clock when reference count is 0. The parent clock tree is
* recursively disabled
*/
void clk_disable ( struct clk * clk )
{
if ( clk = = NULL | | IS_ERR ( clk ) )
return ;
mutex_lock ( & clocks_mutex ) ;
__clk_disable ( clk ) ;
mutex_unlock ( & clocks_mutex ) ;
}
EXPORT_SYMBOL ( clk_disable ) ;
/* Retrieve the *current* clock rate. If the clock itself
* does not provide a special calculation routine , ask
* its parent and so on , until one is able to return
* a valid clock rate
*/
unsigned long clk_get_rate ( struct clk * clk )
{
if ( clk = = NULL | | IS_ERR ( clk ) )
return 0UL ;
if ( clk - > get_rate )
return clk - > get_rate ( clk ) ;
return clk_get_rate ( clk - > parent ) ;
}
EXPORT_SYMBOL ( clk_get_rate ) ;
/* Round the requested clock rate to the nearest supported
* rate that is less than or equal to the requested rate .
* This is dependent on the clock ' s current parent .
*/
long clk_round_rate ( struct clk * clk , unsigned long rate )
{
if ( clk = = NULL | | IS_ERR ( clk ) | | ! clk - > round_rate )
return 0 ;
return clk - > round_rate ( clk , rate ) ;
}
EXPORT_SYMBOL ( clk_round_rate ) ;
/* Set the clock to the requested clock rate. The rate must
* match a supported rate exactly based on what clk_round_rate returns
*/
int clk_set_rate ( struct clk * clk , unsigned long rate )
{
int ret = - EINVAL ;
if ( clk = = NULL | | IS_ERR ( clk ) | | clk - > set_rate = = NULL | | rate = = 0 )
return ret ;
mutex_lock ( & clocks_mutex ) ;
ret = clk - > set_rate ( clk , rate ) ;
mutex_unlock ( & clocks_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_set_rate ) ;
/* Set the clock's parent to another clock source */
int clk_set_parent ( struct clk * clk , struct clk * parent )
{
int ret = - EINVAL ;
if ( clk = = NULL | | IS_ERR ( clk ) | | parent = = NULL | |
IS_ERR ( parent ) | | clk - > set_parent = = NULL )
return ret ;
mutex_lock ( & clocks_mutex ) ;
ret = clk - > set_parent ( clk , parent ) ;
if ( ret = = 0 )
clk - > parent = parent ;
mutex_unlock ( & clocks_mutex ) ;
return ret ;
}
EXPORT_SYMBOL ( clk_set_parent ) ;
/* Retrieve the clock's parent clock source */
struct clk * clk_get_parent ( struct clk * clk )
{
struct clk * ret = NULL ;
if ( clk = = NULL | | IS_ERR ( clk ) )
return ret ;
return clk - > parent ;
}
EXPORT_SYMBOL ( clk_get_parent ) ;
2009-01-26 15:41:16 +01:00
/*
* Get the resulting clock rate from a PLL register value and the input
* frequency . PLLs with this register layout can at least be found on
* MX1 , MX21 , MX27 and MX31
*
* mfi + mfn / ( mfd + 1 )
* f = 2 * f_ref * - - - - - - - - - - - - - - - - - - - -
* pd + 1
*/
unsigned long mxc_decode_pll ( unsigned int reg_val , u32 freq )
{
long long ll ;
int mfn_abs ;
unsigned int mfi , mfn , mfd , pd ;
mfi = ( reg_val > > 10 ) & 0xf ;
mfn = reg_val & 0x3ff ;
mfd = ( reg_val > > 16 ) & 0x3ff ;
pd = ( reg_val > > 26 ) & 0xf ;
mfi = mfi < = 5 ? 5 : mfi ;
mfn_abs = mfn ;
2009-06-03 11:22:32 +02:00
/* On all i.MXs except i.MX1 and i.MX21 mfn is a 10bit
* 2 ' s complements number
*/
if ( ! cpu_is_mx1 ( ) & & ! cpu_is_mx21 ( ) & & mfn > = 0x200 )
mfn_abs = 0x400 - mfn ;
2009-01-26 15:41:16 +01:00
freq * = 2 ;
freq / = pd + 1 ;
ll = ( unsigned long long ) freq * mfn_abs ;
do_div ( ll , mfd + 1 ) ;
2009-06-03 11:22:32 +02:00
if ( ! cpu_is_mx1 ( ) & & ! cpu_is_mx21 ( ) & & mfn > = 0x200 )
2009-01-26 15:41:16 +01:00
ll = - ll ;
2009-06-03 11:22:32 +02:00
2009-01-26 15:41:16 +01:00
ll = ( freq * mfi ) + ll ;
return ll ;
}