2008-03-18 11:22:06 +03:00
/*
* linux / arch / arm / mach - omap2 / clock . c
*
2008-03-18 12:56:39 +03:00
* Copyright ( C ) 2005 - 2008 Texas Instruments , Inc .
2010-02-23 08:09:24 +03:00
* Copyright ( C ) 2004 - 2010 Nokia Corporation
2008-03-18 11:22:06 +03:00
*
2008-03-18 12:56:39 +03:00
* Contacts :
* Richard Woodruff < r - woodruff2 @ ti . com >
2008-03-18 11:22:06 +03:00
* Paul Walmsley
*
* 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 .
*/
# undef DEBUG
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/errno.h>
2010-02-23 08:09:36 +03:00
# include <linux/err.h>
# include <linux/delay.h>
2008-03-18 11:22:06 +03:00
# include <linux/clk.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2008-09-06 15:13:59 +04:00
# include <linux/bitops.h>
2008-03-18 11:22:06 +03:00
2009-10-20 20:40:47 +04:00
# include <plat/clock.h>
# include <plat/clockdomain.h>
# include <plat/cpu.h>
# include <plat/prcm.h>
2008-03-18 11:22:06 +03:00
# include "clock.h"
# include "prm.h"
# include "prm-regbits-24xx.h"
# include "cm.h"
# include "cm-regbits-24xx.h"
# include "cm-regbits-34xx.h"
u8 cpu_mask ;
/*-------------------------------------------------------------------------
2009-12-09 04:47:17 +03:00
* OMAP2 / 3 / 4 specific clock functions
2008-03-18 11:22:06 +03:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2010-01-27 06:13:04 +03:00
/* Private functions */
/**
* _omap2_module_wait_ready - wait for an OMAP module to leave IDLE
* @ clk : struct clk * belonging to the module
*
* If the necessary clocks for the OMAP hardware IP block that
* corresponds to clock @ clk are enabled , then wait for the module to
* indicate readiness ( i . e . , to leave IDLE ) . This code does not
* belong in the clock code and will be moved in the medium term to
* module - dependent code . No return value .
*/
static void _omap2_module_wait_ready ( struct clk * clk )
{
void __iomem * companion_reg , * idlest_reg ;
2010-02-24 22:05:54 +03:00
u8 other_bit , idlest_bit , idlest_val ;
2010-01-27 06:13:04 +03:00
/* Not all modules have multiple clocks that their IDLEST depends on */
if ( clk - > ops - > find_companion ) {
clk - > ops - > find_companion ( clk , & companion_reg , & other_bit ) ;
if ( ! ( __raw_readl ( companion_reg ) & ( 1 < < other_bit ) ) )
return ;
}
2010-02-24 22:05:54 +03:00
clk - > ops - > find_idlest ( clk , & idlest_reg , & idlest_bit , & idlest_val ) ;
2010-01-27 06:13:04 +03:00
2010-02-24 22:05:54 +03:00
omap2_cm_wait_idlest ( idlest_reg , ( 1 < < idlest_bit ) , idlest_val ,
clk - > name ) ;
2010-01-27 06:13:04 +03:00
}
/* Enables clock without considering parent dependencies or use count
* REVISIT : Maybe change this to use clk - > enable like on omap1 ?
*/
static int _omap2_clk_enable ( struct clk * clk )
{
return clk - > ops - > enable ( clk ) ;
}
/* Disables clock without considering parent dependencies or use count */
static void _omap2_clk_disable ( struct clk * clk )
{
clk - > ops - > disable ( clk ) ;
}
/* Public functions */
2008-08-19 12:08:45 +04:00
/**
* omap2_init_clk_clkdm - look up a clockdomain name , store pointer in clk
* @ clk : OMAP clock struct ptr to use
*
* Convert a clockdomain name stored in a struct clk ' clk ' into a
* clockdomain pointer , and save it into the struct clk . Intended to be
* called during clk_register ( ) . No return value .
*/
void omap2_init_clk_clkdm ( struct clk * clk )
{
struct clockdomain * clkdm ;
if ( ! clk - > clkdm_name )
return ;
clkdm = clkdm_lookup ( clk - > clkdm_name ) ;
if ( clkdm ) {
pr_debug ( " clock: associated clk %s to clkdm %s \n " ,
clk - > name , clk - > clkdm_name ) ;
clk - > clkdm = clkdm ;
} else {
pr_debug ( " clock: could not associate clk %s to "
" clkdm %s \n " , clk - > name , clk - > clkdm_name ) ;
}
}
2008-03-18 11:22:06 +03:00
/**
2009-07-25 05:44:03 +04:00
* omap2_clk_dflt_find_companion - find companion clock to @ clk
* @ clk : struct clk * to find the companion clock of
* @ other_reg : void __iomem * * to return the companion clock CM_ * CLKEN va in
* @ other_bit : u8 * * to return the companion clock bit shift in
*
* Note : We don ' t need special code here for INVERT_ENABLE for the
* time being since INVERT_ENABLE only applies to clocks enabled by
* CM_CLKEN_PLL
2008-03-18 11:22:06 +03:00
*
2009-07-25 05:44:03 +04:00
* Convert CM_ICLKEN * < - > CM_FCLKEN * . This conversion assumes it ' s
* just a matter of XORing the bits .
*
* Some clocks don ' t have companion clocks . For example , modules with
* only an interface clock ( such as MAILBOXES ) don ' t have a companion
* clock . Right now , this code relies on the hardware exporting a bit
* in the correct companion register that indicates that the
* nonexistent ' companion clock ' is active . Future patches will
* associate this type of code with per - module data structures to
* avoid this issue , and remove the casts . No return value .
2008-03-18 11:22:06 +03:00
*/
2009-07-25 05:44:03 +04:00
void omap2_clk_dflt_find_companion ( struct clk * clk , void __iomem * * other_reg ,
u8 * other_bit )
2008-03-18 11:22:06 +03:00
{
2009-07-25 05:44:03 +04:00
u32 r ;
2008-03-18 11:22:06 +03:00
/*
2009-07-25 05:44:03 +04:00
* Convert CM_ICLKEN * < - > CM_FCLKEN * . This conversion assumes
* it ' s just a matter of XORing the bits .
2008-03-18 11:22:06 +03:00
*/
2009-07-25 05:44:03 +04:00
r = ( ( __force u32 ) clk - > enable_reg ^ ( CM_FCLKEN ^ CM_ICLKEN ) ) ;
2008-03-18 11:22:06 +03:00
2009-07-25 05:44:03 +04:00
* other_reg = ( __force void __iomem * ) r ;
* other_bit = clk - > enable_bit ;
}
2008-03-18 11:22:06 +03:00
2009-07-25 05:44:03 +04:00
/**
* omap2_clk_dflt_find_idlest - find CM_IDLEST reg va , bit shift for @ clk
* @ clk : struct clk * to find IDLEST info for
* @ idlest_reg : void __iomem * * to return the CM_IDLEST va in
2010-02-24 22:05:54 +03:00
* @ idlest_bit : u8 * to return the CM_IDLEST bit shift in
* @ idlest_val : u8 * to return the idle status indicator
2009-07-25 05:44:03 +04:00
*
* Return the CM_IDLEST register address and bit shift corresponding
* to the module that " owns " this clock . This default code assumes
* that the CM_IDLEST bit shift is the CM_ * CLKEN bit shift , and that
* the IDLEST register address ID corresponds to the CM_ * CLKEN
* register address ID ( e . g . , that CM_FCLKEN2 corresponds to
* CM_IDLEST2 ) . This is not true for all modules . No return value .
2008-03-18 11:22:06 +03:00
*/
2009-07-25 05:44:03 +04:00
void omap2_clk_dflt_find_idlest ( struct clk * clk , void __iomem * * idlest_reg ,
2010-02-24 22:05:54 +03:00
u8 * idlest_bit , u8 * idlest_val )
2008-03-18 11:22:06 +03:00
{
2009-07-25 05:44:03 +04:00
u32 r ;
2008-03-18 11:22:06 +03:00
2009-07-25 05:44:03 +04:00
r = ( ( ( __force u32 ) clk - > enable_reg & ~ 0xf0 ) | 0x20 ) ;
* idlest_reg = ( __force void __iomem * ) r ;
* idlest_bit = clk - > enable_bit ;
2010-02-24 22:05:54 +03:00
/*
* 24 xx uses 0 to indicate not ready , and 1 to indicate ready .
* 34 xx reverses this , just to keep us on our toes
* AM35xx uses both , depending on the module .
*/
if ( cpu_is_omap24xx ( ) )
* idlest_val = OMAP24XX_CM_IDLEST_VAL ;
else if ( cpu_is_omap34xx ( ) )
* idlest_val = OMAP34XX_CM_IDLEST_VAL ;
else
BUG ( ) ;
2009-07-25 05:44:03 +04:00
}
2008-03-18 11:22:06 +03:00
2009-07-25 05:44:03 +04:00
int omap2_dflt_clk_enable ( struct clk * clk )
2008-03-18 11:22:06 +03:00
{
2009-01-28 22:18:19 +03:00
u32 v ;
2008-03-18 11:22:06 +03:00
2008-09-05 18:10:27 +04:00
if ( unlikely ( clk - > enable_reg = = NULL ) ) {
2009-07-25 05:44:03 +04:00
pr_err ( " clock.c: Enable for %s without enable code \n " ,
2008-03-18 11:22:06 +03:00
clk - > name ) ;
return 0 ; /* REVISIT: -EINVAL */
}
2009-01-28 22:18:19 +03:00
v = __raw_readl ( clk - > enable_reg ) ;
2008-03-18 11:22:06 +03:00
if ( clk - > flags & INVERT_ENABLE )
2009-01-28 22:18:19 +03:00
v & = ~ ( 1 < < clk - > enable_bit ) ;
2008-03-18 11:22:06 +03:00
else
2009-01-28 22:18:19 +03:00
v | = ( 1 < < clk - > enable_bit ) ;
__raw_writel ( v , clk - > enable_reg ) ;
2009-01-28 22:35:06 +03:00
v = __raw_readl ( clk - > enable_reg ) ; /* OCP barrier */
2008-03-18 11:22:06 +03:00
2009-07-25 05:44:03 +04:00
if ( clk - > ops - > find_idlest )
2010-01-27 06:13:04 +03:00
_omap2_module_wait_ready ( clk ) ;
2008-03-18 11:22:06 +03:00
2009-07-25 05:44:03 +04:00
return 0 ;
2008-11-04 21:59:32 +03:00
}
2009-07-25 05:44:03 +04:00
void omap2_dflt_clk_disable ( struct clk * clk )
2008-03-18 11:22:06 +03:00
{
2009-01-28 22:18:19 +03:00
u32 v ;
2008-03-18 11:22:06 +03:00
2009-01-28 05:12:50 +03:00
if ( ! clk - > enable_reg ) {
2008-03-18 11:22:06 +03:00
/*
* ' Independent ' here refers to a clock which is not
* controlled by its parent .
*/
printk ( KERN_ERR " clock: clk_disable called on independent "
" clock %s which has no enable_reg \n " , clk - > name ) ;
return ;
}
2009-01-28 22:18:19 +03:00
v = __raw_readl ( clk - > enable_reg ) ;
2008-03-18 11:22:06 +03:00
if ( clk - > flags & INVERT_ENABLE )
2009-01-28 22:18:19 +03:00
v | = ( 1 < < clk - > enable_bit ) ;
2008-03-18 11:22:06 +03:00
else
2009-01-28 22:18:19 +03:00
v & = ~ ( 1 < < clk - > enable_bit ) ;
__raw_writel ( v , clk - > enable_reg ) ;
2009-01-28 22:35:01 +03:00
/* No OCP barrier needed here since it is a disable operation */
2008-03-18 11:22:06 +03:00
}
2008-11-04 20:59:52 +03:00
const struct clkops clkops_omap2_dflt_wait = {
2009-07-25 05:44:03 +04:00
. enable = omap2_dflt_clk_enable ,
2008-11-04 20:59:52 +03:00
. disable = omap2_dflt_clk_disable ,
2009-07-25 05:44:03 +04:00
. find_companion = omap2_clk_dflt_find_companion ,
. find_idlest = omap2_clk_dflt_find_idlest ,
2008-11-04 20:59:52 +03:00
} ;
2008-11-04 21:59:32 +03:00
const struct clkops clkops_omap2_dflt = {
. enable = omap2_dflt_clk_enable ,
. disable = omap2_dflt_clk_disable ,
} ;
2008-03-18 11:22:06 +03:00
void omap2_clk_disable ( struct clk * clk )
{
if ( clk - > usecount > 0 & & ! ( - - clk - > usecount ) ) {
_omap2_clk_disable ( clk ) ;
2009-01-28 05:12:50 +03:00
if ( clk - > parent )
2008-03-18 11:22:06 +03:00
omap2_clk_disable ( clk - > parent ) ;
2008-08-19 12:08:45 +04:00
if ( clk - > clkdm )
omap2_clkdm_clk_disable ( clk - > clkdm , clk ) ;
2008-03-18 11:22:06 +03:00
}
}
int omap2_clk_enable ( struct clk * clk )
{
int ret = 0 ;
if ( clk - > usecount + + = = 0 ) {
2008-08-19 12:08:45 +04:00
if ( clk - > clkdm )
omap2_clkdm_clk_enable ( clk - > clkdm , clk ) ;
2009-01-31 14:00:17 +03:00
if ( clk - > parent ) {
2008-03-18 11:22:06 +03:00
ret = omap2_clk_enable ( clk - > parent ) ;
2009-01-31 14:00:17 +03:00
if ( ret )
goto err ;
2008-03-18 11:22:06 +03:00
}
2008-08-19 12:08:45 +04:00
2008-03-18 11:22:06 +03:00
ret = _omap2_clk_enable ( clk ) ;
2009-01-31 14:00:17 +03:00
if ( ret ) {
if ( clk - > parent )
2008-08-19 12:08:45 +04:00
omap2_clk_disable ( clk - > parent ) ;
2009-01-31 14:00:17 +03:00
goto err ;
2008-03-18 11:22:06 +03:00
}
}
2009-01-31 14:00:17 +03:00
return ret ;
2008-03-18 11:22:06 +03:00
2009-01-31 14:00:17 +03:00
err :
2009-01-31 14:02:37 +03:00
if ( clk - > clkdm )
omap2_clkdm_clk_disable ( clk - > clkdm , clk ) ;
2009-01-31 14:00:17 +03:00
clk - > usecount - - ;
2008-03-18 11:22:06 +03:00
return ret ;
}
/* Set the clock rate for a clock source */
int omap2_clk_set_rate ( struct clk * clk , unsigned long rate )
{
int ret = - EINVAL ;
pr_debug ( " clock: set_rate for clock %s to rate %ld \n " , clk - > name , rate ) ;
/* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */
2009-01-28 05:12:50 +03:00
if ( clk - > set_rate )
2008-03-18 11:22:06 +03:00
ret = clk - > set_rate ( clk , rate ) ;
return ret ;
}
int omap2_clk_set_parent ( struct clk * clk , struct clk * new_parent )
{
if ( ! clk - > clksel )
return - EINVAL ;
2010-02-23 08:09:16 +03:00
if ( clk - > parent = = new_parent )
return 0 ;
2010-01-27 06:13:04 +03:00
return omap2_clksel_set_parent ( clk , new_parent ) ;
2008-03-18 11:22:06 +03:00
}
OMAP3/4 clock: split into per-chip family files
clock34xx_data.c now contains data for the OMAP34xx family, the
OMAP36xx family, and the OMAP3517 family, so rename it to
clock3xxx_data.c. Rename clock34xx.c to clock3xxx.c, and move the
chip family-specific clock functions to clock34xx.c, clock36xx.c, or
clock3517.c, as appropriate. So now "clock3xxx.*" refers to the OMAP3
superset.
The main goal here is to prepare to compile chip family-specific clock
functions only for kernel builds that target that chip family. To get to
that point, we also need to add CONFIG_SOC_* options for those other
chip families; that will be done in future patches, planned for 2.6.35.
OMAP4 is also affected by this. It duplicated the OMAP3 non-CORE DPLL
clkops structure. The OMAP4 variant of this clkops structure has been
removed, and since there was nothing else currently in clock44xx.c, it
too has been removed -- it can always be added back later when there
is some content for it. (The OMAP4 clock autogeneration scripts have been
updated accordingly.)
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Benoît Cousson <b-cousson@ti.com>
Cc: Rajendra Nayak <rnayak@ti.com>
Cc: Ranjith Lohithakshan <ranjithl@ti.com>
Cc: Tony Lindgren <tony@atomide.com>
2010-02-23 08:09:20 +03:00
/* OMAP3/4 non-CORE DPLL clkops */
# if defined(CONFIG_ARCH_OMAP3) || defined(CONFIG_ARCH_OMAP4)
const struct clkops clkops_omap3_noncore_dpll_ops = {
. enable = omap3_noncore_dpll_enable ,
. disable = omap3_noncore_dpll_disable ,
} ;
# endif
2008-03-18 11:22:06 +03:00
/*-------------------------------------------------------------------------
* Omap2 clock reset and init functions
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
# ifdef CONFIG_OMAP_RESET_CLOCKS
void omap2_clk_disable_unused ( struct clk * clk )
{
u32 regval32 , v ;
v = ( clk - > flags & INVERT_ENABLE ) ? ( 1 < < clk - > enable_bit ) : 0 ;
regval32 = __raw_readl ( clk - > enable_reg ) ;
if ( ( regval32 & ( 1 < < clk - > enable_bit ) ) = = v )
return ;
2009-05-13 03:34:40 +04:00
printk ( KERN_DEBUG " Disabling unused clock \" %s \" \n " , clk - > name ) ;
2009-01-28 22:27:45 +03:00
if ( cpu_is_omap34xx ( ) ) {
omap2_clk_enable ( clk ) ;
omap2_clk_disable ( clk ) ;
} else
_omap2_clk_disable ( clk ) ;
2008-10-15 18:48:44 +04:00
if ( clk - > clkdm ! = NULL )
pwrdm_clkdm_state_switch ( clk - > clkdm ) ;
2008-03-18 11:22:06 +03:00
}
# endif
2010-01-27 06:13:04 +03:00
2010-02-23 08:09:36 +03:00
/**
* omap2_clk_switch_mpurate_at_boot - switch ARM MPU rate by boot - time argument
* @ mpurate_ck_name : clk name of the clock to change rate
*
* Change the ARM MPU clock rate to the rate specified on the command
* line , if one was specified . @ mpurate_ck_name should be
* " virt_prcm_set " on OMAP2xxx and " dpll1_ck " on OMAP34xx / OMAP36xx .
* XXX Does not handle voltage scaling - on OMAP2xxx this is currently
* handled by the virt_prcm_set clock , but this should be handled by
* the OPP layer . XXX This is intended to be handled by the OPP layer
* code in the near future and should be removed from the clock code .
* Returns - EINVAL if ' mpurate ' is zero or if clk_set_rate ( ) rejects
* the rate , - ENOENT if the struct clk referred to by @ mpurate_ck_name
* cannot be found , or 0 upon success .
*/
int __init omap2_clk_switch_mpurate_at_boot ( const char * mpurate_ck_name )
{
struct clk * mpurate_ck ;
int r ;
if ( ! mpurate )
return - EINVAL ;
mpurate_ck = clk_get ( NULL , mpurate_ck_name ) ;
if ( WARN ( IS_ERR ( mpurate_ck ) , " Failed to get %s. \n " , mpurate_ck_name ) )
return - ENOENT ;
r = clk_set_rate ( mpurate_ck , mpurate ) ;
if ( IS_ERR_VALUE ( r ) ) {
WARN ( 1 , " clock: %s: unable to set MPU rate to %d: %d \n " ,
mpurate_ck - > name , mpurate , r ) ;
return - EINVAL ;
}
calibrate_delay ( ) ;
recalculate_root_clocks ( ) ;
clk_put ( mpurate_ck ) ;
return 0 ;
}
/**
* omap2_clk_print_new_rates - print summary of current clock tree rates
* @ hfclkin_ck_name : clk name for the off - chip HF oscillator
* @ core_ck_name : clk name for the on - chip CORE_CLK
* @ mpu_ck_name : clk name for the ARM MPU clock
*
* Prints a short message to the console with the HFCLKIN oscillator
* rate , the rate of the CORE clock , and the rate of the ARM MPU clock .
* Called by the boot - time MPU rate switching code . XXX This is intended
* to be handled by the OPP layer code in the near future and should be
* removed from the clock code . No return value .
*/
void __init omap2_clk_print_new_rates ( const char * hfclkin_ck_name ,
const char * core_ck_name ,
const char * mpu_ck_name )
{
struct clk * hfclkin_ck , * core_ck , * mpu_ck ;
unsigned long hfclkin_rate ;
mpu_ck = clk_get ( NULL , mpu_ck_name ) ;
if ( WARN ( IS_ERR ( mpu_ck ) , " clock: failed to get %s. \n " , mpu_ck_name ) )
return ;
core_ck = clk_get ( NULL , core_ck_name ) ;
if ( WARN ( IS_ERR ( core_ck ) , " clock: failed to get %s. \n " , core_ck_name ) )
return ;
hfclkin_ck = clk_get ( NULL , hfclkin_ck_name ) ;
if ( WARN ( IS_ERR ( hfclkin_ck ) , " Failed to get %s. \n " , hfclkin_ck_name ) )
return ;
hfclkin_rate = clk_get_rate ( hfclkin_ck ) ;
pr_info ( " Switched to new clocking rate (Crystal/Core/MPU): "
" %ld.%01ld/%ld/%ld MHz \n " ,
( hfclkin_rate / 1000000 ) ,
( ( hfclkin_rate / 100000 ) % 10 ) ,
( clk_get_rate ( core_ck ) / 1000000 ) ,
( clk_get_rate ( mpu_ck ) / 1000000 ) ) ;
}
2010-01-27 06:13:04 +03:00
/* Common data */
struct clk_functions omap2_clk_functions = {
. clk_enable = omap2_clk_enable ,
. clk_disable = omap2_clk_disable ,
. clk_round_rate = omap2_clk_round_rate ,
. clk_set_rate = omap2_clk_set_rate ,
. clk_set_parent = omap2_clk_set_parent ,
. clk_disable_unused = omap2_clk_disable_unused ,
# ifdef CONFIG_CPU_FREQ
/* These will be removed when the OPP code is integrated */
. clk_init_cpufreq_table = omap2_clk_init_cpufreq_table ,
. clk_exit_cpufreq_table = omap2_clk_exit_cpufreq_table ,
# endif
} ;