2008-03-18 10:22:06 +02:00
/*
* linux / arch / arm / mach - omap2 / clock . c
*
2008-03-18 11:56:39 +02:00
* Copyright ( C ) 2005 - 2008 Texas Instruments , Inc .
* Copyright ( C ) 2004 - 2008 Nokia Corporation
2008-03-18 10:22:06 +02:00
*
2008-03-18 11:56:39 +02:00
* Contacts :
* Richard Woodruff < r - woodruff2 @ ti . com >
2008-03-18 10:22:06 +02: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/module.h>
# include <linux/kernel.h>
# include <linux/device.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/delay.h>
# include <linux/clk.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2008-09-06 12:13:59 +01:00
# include <linux/bitops.h>
2008-03-18 10:22:06 +02:00
2009-10-20 09:40:47 -07:00
# include <plat/clock.h>
# include <plat/clockdomain.h>
# include <plat/cpu.h>
# include <plat/prcm.h>
2008-03-18 10:22:06 +02: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-08 18:47:17 -07:00
* OMAP2 / 3 / 4 specific clock functions
2008-03-18 10:22:06 +02:00
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2009-01-28 12:35:03 -07:00
/**
2010-01-26 20:13:04 -07:00
* omap2xxx_clk_commit - commit clock parent / rate changes in hardware
2009-01-28 12:35:03 -07:00
* @ clk : struct clk *
*
* If @ clk has the DELAYED_APP flag set , meaning that parent / rate changes
* don ' t take effect until the VALID_CONFIG bit is written , write the
* VALID_CONFIG bit and wait for the write to complete . No return value .
*/
2010-01-26 20:13:04 -07:00
void omap2xxx_clk_commit ( struct clk * clk )
2009-01-28 12:35:03 -07:00
{
if ( ! cpu_is_omap24xx ( ) )
return ;
if ( ! ( clk - > flags & DELAYED_APP ) )
return ;
prm_write_mod_reg ( OMAP24XX_VALID_CONFIG , OMAP24XX_GR_MOD ,
2009-05-25 11:26:42 -07:00
OMAP2_PRCM_CLKCFG_CTRL_OFFSET ) ;
2009-01-28 12:35:03 -07:00
/* OCP barrier */
2009-05-25 11:26:42 -07:00
prm_read_mod_reg ( OMAP24XX_GR_MOD , OMAP2_PRCM_CLKCFG_CTRL_OFFSET ) ;
2009-01-28 12:35:03 -07:00
}
2008-08-19 11:08:45 +03: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 10:22:06 +02:00
/**
2009-07-24 19:44:03 -06: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 10:22:06 +02:00
*
2009-07-24 19:44:03 -06: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 10:22:06 +02:00
*/
2009-07-24 19:44:03 -06:00
void omap2_clk_dflt_find_companion ( struct clk * clk , void __iomem * * other_reg ,
u8 * other_bit )
2008-03-18 10:22:06 +02:00
{
2009-07-24 19:44:03 -06:00
u32 r ;
2008-03-18 10:22:06 +02:00
/*
2009-07-24 19:44:03 -06:00
* Convert CM_ICLKEN * < - > CM_FCLKEN * . This conversion assumes
* it ' s just a matter of XORing the bits .
2008-03-18 10:22:06 +02:00
*/
2009-07-24 19:44:03 -06:00
r = ( ( __force u32 ) clk - > enable_reg ^ ( CM_FCLKEN ^ CM_ICLKEN ) ) ;
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
* other_reg = ( __force void __iomem * ) r ;
* other_bit = clk - > enable_bit ;
}
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06: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
* @ idlest_bit : u8 * * to return the CM_IDLEST bit shift in
*
* 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 10:22:06 +02:00
*/
2009-07-24 19:44:03 -06:00
void omap2_clk_dflt_find_idlest ( struct clk * clk , void __iomem * * idlest_reg ,
u8 * idlest_bit )
2008-03-18 10:22:06 +02:00
{
2009-07-24 19:44:03 -06:00
u32 r ;
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
r = ( ( ( __force u32 ) clk - > enable_reg & ~ 0xf0 ) | 0x20 ) ;
* idlest_reg = ( __force void __iomem * ) r ;
* idlest_bit = clk - > enable_bit ;
}
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
/**
* 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 ;
u8 other_bit , idlest_bit ;
/* 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 ;
}
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
clk - > ops - > find_idlest ( clk , & idlest_reg , & idlest_bit ) ;
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
omap2_cm_wait_idlest ( idlest_reg , ( 1 < < idlest_bit ) , clk - > name ) ;
2008-03-18 10:22:06 +02:00
}
2009-07-24 19:44:03 -06:00
int omap2_dflt_clk_enable ( struct clk * clk )
2008-03-18 10:22:06 +02:00
{
2009-01-28 12:18:19 -07:00
u32 v ;
2008-03-18 10:22:06 +02:00
2008-09-05 15:10:27 +01:00
if ( unlikely ( clk - > enable_reg = = NULL ) ) {
2009-07-24 19:44:03 -06:00
pr_err ( " clock.c: Enable for %s without enable code \n " ,
2008-03-18 10:22:06 +02:00
clk - > name ) ;
return 0 ; /* REVISIT: -EINVAL */
}
2009-01-28 12:18:19 -07:00
v = __raw_readl ( clk - > enable_reg ) ;
2008-03-18 10:22:06 +02:00
if ( clk - > flags & INVERT_ENABLE )
2009-01-28 12:18:19 -07:00
v & = ~ ( 1 < < clk - > enable_bit ) ;
2008-03-18 10:22:06 +02:00
else
2009-01-28 12:18:19 -07:00
v | = ( 1 < < clk - > enable_bit ) ;
__raw_writel ( v , clk - > enable_reg ) ;
2009-01-28 12:35:06 -07:00
v = __raw_readl ( clk - > enable_reg ) ; /* OCP barrier */
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
if ( clk - > ops - > find_idlest )
omap2_module_wait_ready ( clk ) ;
2008-03-18 10:22:06 +02:00
2009-07-24 19:44:03 -06:00
return 0 ;
2008-11-04 18:59:32 +00:00
}
2009-07-24 19:44:03 -06:00
void omap2_dflt_clk_disable ( struct clk * clk )
2008-03-18 10:22:06 +02:00
{
2009-01-28 12:18:19 -07:00
u32 v ;
2008-03-18 10:22:06 +02:00
2009-01-27 19:12:50 -07:00
if ( ! clk - > enable_reg ) {
2008-03-18 10:22:06 +02: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 12:18:19 -07:00
v = __raw_readl ( clk - > enable_reg ) ;
2008-03-18 10:22:06 +02:00
if ( clk - > flags & INVERT_ENABLE )
2009-01-28 12:18:19 -07:00
v | = ( 1 < < clk - > enable_bit ) ;
2008-03-18 10:22:06 +02:00
else
2009-01-28 12:18:19 -07:00
v & = ~ ( 1 < < clk - > enable_bit ) ;
__raw_writel ( v , clk - > enable_reg ) ;
2009-01-28 12:35:01 -07:00
/* No OCP barrier needed here since it is a disable operation */
2008-03-18 10:22:06 +02:00
}
2008-11-04 17:59:52 +00:00
const struct clkops clkops_omap2_dflt_wait = {
2009-07-24 19:44:03 -06:00
. enable = omap2_dflt_clk_enable ,
2008-11-04 17:59:52 +00:00
. disable = omap2_dflt_clk_disable ,
2009-07-24 19:44:03 -06:00
. find_companion = omap2_clk_dflt_find_companion ,
. find_idlest = omap2_clk_dflt_find_idlest ,
2008-11-04 17:59:52 +00:00
} ;
2008-11-04 18:59:32 +00:00
const struct clkops clkops_omap2_dflt = {
. enable = omap2_dflt_clk_enable ,
. disable = omap2_dflt_clk_disable ,
} ;
2008-11-04 17:59:52 +00: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 ) ;
2008-03-18 10:22:06 +02:00
}
void omap2_clk_disable ( struct clk * clk )
{
if ( clk - > usecount > 0 & & ! ( - - clk - > usecount ) ) {
_omap2_clk_disable ( clk ) ;
2009-01-27 19:12:50 -07:00
if ( clk - > parent )
2008-03-18 10:22:06 +02:00
omap2_clk_disable ( clk - > parent ) ;
2008-08-19 11:08:45 +03:00
if ( clk - > clkdm )
omap2_clkdm_clk_disable ( clk - > clkdm , clk ) ;
2008-03-18 10:22:06 +02:00
}
}
int omap2_clk_enable ( struct clk * clk )
{
int ret = 0 ;
if ( clk - > usecount + + = = 0 ) {
2008-08-19 11:08:45 +03:00
if ( clk - > clkdm )
omap2_clkdm_clk_enable ( clk - > clkdm , clk ) ;
2009-01-31 11:00:17 +00:00
if ( clk - > parent ) {
2008-03-18 10:22:06 +02:00
ret = omap2_clk_enable ( clk - > parent ) ;
2009-01-31 11:00:17 +00:00
if ( ret )
goto err ;
2008-03-18 10:22:06 +02:00
}
2008-08-19 11:08:45 +03:00
2008-03-18 10:22:06 +02:00
ret = _omap2_clk_enable ( clk ) ;
2009-01-31 11:00:17 +00:00
if ( ret ) {
if ( clk - > parent )
2008-08-19 11:08:45 +03:00
omap2_clk_disable ( clk - > parent ) ;
2009-01-31 11:00:17 +00:00
goto err ;
2008-03-18 10:22:06 +02:00
}
}
2009-01-31 11:00:17 +00:00
return ret ;
2008-03-18 10:22:06 +02:00
2009-01-31 11:00:17 +00:00
err :
2009-01-31 11:02:37 +00:00
if ( clk - > clkdm )
omap2_clkdm_clk_disable ( clk - > clkdm , clk ) ;
2009-01-31 11:00:17 +00:00
clk - > usecount - - ;
2008-03-18 10:22:06 +02: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 ) ;
/* CONFIG_PARTICIPANT clocks are changed only in sets via the
rate table mechanism , driven by mpu_speed */
if ( clk - > flags & CONFIG_PARTICIPANT )
return - EINVAL ;
/* dpll_ck, core_ck, virt_prcm_set; plus all clksel clocks */
2009-01-27 19:12:50 -07:00
if ( clk - > set_rate )
2008-03-18 10:22:06 +02:00
ret = clk - > set_rate ( clk , rate ) ;
return ret ;
}
int omap2_clk_set_parent ( struct clk * clk , struct clk * new_parent )
{
2009-01-27 19:12:50 -07:00
if ( clk - > flags & CONFIG_PARTICIPANT )
2008-03-18 10:22:06 +02:00
return - EINVAL ;
if ( ! clk - > clksel )
return - EINVAL ;
2010-01-26 20:13:04 -07:00
return omap2_clksel_set_parent ( clk , new_parent ) ;
2008-03-18 10:22:06 +02: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-12 17:34:40 -06:00
printk ( KERN_DEBUG " Disabling unused clock \" %s \" \n " , clk - > name ) ;
2009-01-28 12:27:45 -07:00
if ( cpu_is_omap34xx ( ) ) {
omap2_clk_enable ( clk ) ;
omap2_clk_disable ( clk ) ;
} else
_omap2_clk_disable ( clk ) ;
2008-10-15 17:48:44 +03:00
if ( clk - > clkdm ! = NULL )
pwrdm_clkdm_state_switch ( clk - > clkdm ) ;
2008-03-18 10:22:06 +02:00
}
# endif