2012-10-21 11:01:11 +04:00
/*
* OMAP2xxx CM module functions
*
* Copyright ( C ) 2009 Nokia Corporation
2012-10-21 11:01:11 +04:00
* Copyright ( C ) 2008 - 2010 , 2012 Texas Instruments , Inc .
2012-10-21 11:01:11 +04:00
* Paul Walmsley
2012-10-21 11:01:11 +04:00
* Rajendra Nayak < rnayak @ ti . com >
2012-10-21 11:01:11 +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>
# include <linux/types.h>
# include <linux/delay.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/io.h>
2012-10-21 11:01:11 +04:00
# include "prm2xxx.h"
2012-10-21 11:01:11 +04:00
# include "cm.h"
# include "cm2xxx.h"
# include "cm-regbits-24xx.h"
2012-10-21 11:01:11 +04:00
# include "clockdomain.h"
2012-10-21 11:01:11 +04:00
/* CM_AUTOIDLE_PLL.AUTO_* bit values for DPLLs */
# define DPLL_AUTOIDLE_DISABLE 0x0
# define OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP 0x3
/* CM_AUTOIDLE_PLL.AUTO_* bit values for APLLs (OMAP2xxx only) */
# define OMAP2XXX_APLL_AUTOIDLE_DISABLE 0x0
# define OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP 0x3
2012-10-30 06:56:17 +04:00
/* CM_IDLEST_PLL bit value offset for APLLs (OMAP2xxx only) */
# define EN_APLL_LOCKED 3
2012-10-21 11:01:11 +04:00
static const u8 omap2xxx_cm_idlest_offs [ ] = {
CM_IDLEST1 , CM_IDLEST2 , OMAP2430_CM_IDLEST3 , OMAP24XX_CM_IDLEST4
} ;
/*
*
*/
static void _write_clktrctrl ( u8 c , s16 module , u32 mask )
{
u32 v ;
v = omap2_cm_read_mod_reg ( module , OMAP2_CM_CLKSTCTRL ) ;
v & = ~ mask ;
v | = c < < __ffs ( mask ) ;
omap2_cm_write_mod_reg ( v , module , OMAP2_CM_CLKSTCTRL ) ;
}
2014-10-27 18:39:24 +03:00
static bool omap2xxx_cm_is_clkdm_in_hwsup ( s16 module , u32 mask )
2012-10-21 11:01:11 +04:00
{
u32 v ;
v = omap2_cm_read_mod_reg ( module , OMAP2_CM_CLKSTCTRL ) ;
v & = mask ;
v > > = __ffs ( mask ) ;
return ( v = = OMAP24XX_CLKSTCTRL_ENABLE_AUTO ) ? 1 : 0 ;
}
2014-10-27 18:39:24 +03:00
static void omap2xxx_cm_clkdm_enable_hwsup ( s16 module , u32 mask )
2012-10-21 11:01:11 +04:00
{
_write_clktrctrl ( OMAP24XX_CLKSTCTRL_ENABLE_AUTO , module , mask ) ;
}
2014-10-27 18:39:24 +03:00
static void omap2xxx_cm_clkdm_disable_hwsup ( s16 module , u32 mask )
2012-10-21 11:01:11 +04:00
{
_write_clktrctrl ( OMAP24XX_CLKSTCTRL_DISABLE_AUTO , module , mask ) ;
}
/*
* DPLL autoidle control
*/
static void _omap2xxx_set_dpll_autoidle ( u8 m )
{
u32 v ;
v = omap2_cm_read_mod_reg ( PLL_MOD , CM_AUTOIDLE ) ;
v & = ~ OMAP24XX_AUTO_DPLL_MASK ;
v | = m < < OMAP24XX_AUTO_DPLL_SHIFT ;
omap2_cm_write_mod_reg ( v , PLL_MOD , CM_AUTOIDLE ) ;
}
void omap2xxx_cm_set_dpll_disable_autoidle ( void )
{
_omap2xxx_set_dpll_autoidle ( OMAP2XXX_DPLL_AUTOIDLE_LOW_POWER_STOP ) ;
}
void omap2xxx_cm_set_dpll_auto_low_power_stop ( void )
{
_omap2xxx_set_dpll_autoidle ( DPLL_AUTOIDLE_DISABLE ) ;
}
/*
2012-10-30 06:56:17 +04:00
* APLL control
2012-10-21 11:01:11 +04:00
*/
static void _omap2xxx_set_apll_autoidle ( u8 m , u32 mask )
{
u32 v ;
v = omap2_cm_read_mod_reg ( PLL_MOD , CM_AUTOIDLE ) ;
v & = ~ mask ;
v | = m < < __ffs ( mask ) ;
omap2_cm_write_mod_reg ( v , PLL_MOD , CM_AUTOIDLE ) ;
}
void omap2xxx_cm_set_apll54_disable_autoidle ( void )
{
_omap2xxx_set_apll_autoidle ( OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP ,
OMAP24XX_AUTO_54M_MASK ) ;
}
void omap2xxx_cm_set_apll54_auto_low_power_stop ( void )
{
_omap2xxx_set_apll_autoidle ( OMAP2XXX_APLL_AUTOIDLE_DISABLE ,
OMAP24XX_AUTO_54M_MASK ) ;
}
void omap2xxx_cm_set_apll96_disable_autoidle ( void )
{
_omap2xxx_set_apll_autoidle ( OMAP2XXX_APLL_AUTOIDLE_LOW_POWER_STOP ,
OMAP24XX_AUTO_96M_MASK ) ;
}
void omap2xxx_cm_set_apll96_auto_low_power_stop ( void )
{
_omap2xxx_set_apll_autoidle ( OMAP2XXX_APLL_AUTOIDLE_DISABLE ,
OMAP24XX_AUTO_96M_MASK ) ;
}
2012-10-30 06:56:17 +04:00
/* Enable an APLL if off */
static int _omap2xxx_apll_enable ( u8 enable_bit , u8 status_bit )
{
u32 v , m ;
m = EN_APLL_LOCKED < < enable_bit ;
v = omap2_cm_read_mod_reg ( PLL_MOD , CM_CLKEN ) ;
if ( v & m )
return 0 ; /* apll already enabled */
v | = m ;
omap2_cm_write_mod_reg ( v , PLL_MOD , CM_CLKEN ) ;
2014-10-27 18:39:23 +03:00
omap2xxx_cm_wait_module_ready ( 0 , PLL_MOD , 1 , status_bit ) ;
2012-10-30 06:56:17 +04:00
/*
* REVISIT : Should we return an error code if
* omap2xxx_cm_wait_module_ready ( ) fails ?
*/
return 0 ;
}
/* Stop APLL */
static void _omap2xxx_apll_disable ( u8 enable_bit )
{
u32 v ;
v = omap2_cm_read_mod_reg ( PLL_MOD , CM_CLKEN ) ;
v & = ~ ( EN_APLL_LOCKED < < enable_bit ) ;
omap2_cm_write_mod_reg ( v , PLL_MOD , CM_CLKEN ) ;
}
/* Enable an APLL if off */
int omap2xxx_cm_apll54_enable ( void )
{
return _omap2xxx_apll_enable ( OMAP24XX_EN_54M_PLL_SHIFT ,
OMAP24XX_ST_54M_APLL_SHIFT ) ;
}
/* Enable an APLL if off */
int omap2xxx_cm_apll96_enable ( void )
{
return _omap2xxx_apll_enable ( OMAP24XX_EN_96M_PLL_SHIFT ,
OMAP24XX_ST_96M_APLL_SHIFT ) ;
}
/* Stop APLL */
void omap2xxx_cm_apll54_disable ( void )
{
_omap2xxx_apll_disable ( OMAP24XX_EN_54M_PLL_SHIFT ) ;
}
/* Stop APLL */
void omap2xxx_cm_apll96_disable ( void )
{
_omap2xxx_apll_disable ( OMAP24XX_EN_96M_PLL_SHIFT ) ;
}
2012-10-30 06:56:29 +04:00
/**
* omap2xxx_cm_split_idlest_reg - split CM_IDLEST reg addr into its components
* @ idlest_reg : CM_IDLEST * virtual address
* @ prcm_inst : pointer to an s16 to return the PRCM instance offset
* @ idlest_reg_id : pointer to a u8 to return the CM_IDLESTx register ID
*
* XXX This function is only needed until absolute register addresses are
* removed from the OMAP struct clk records .
*/
2014-10-27 18:39:24 +03:00
static int omap2xxx_cm_split_idlest_reg ( void __iomem * idlest_reg ,
s16 * prcm_inst ,
u8 * idlest_reg_id )
2012-10-30 06:56:29 +04:00
{
unsigned long offs ;
u8 idlest_offs ;
int i ;
if ( idlest_reg < cm_base | | idlest_reg > ( cm_base + 0x0fff ) )
return - EINVAL ;
idlest_offs = ( unsigned long ) idlest_reg & 0xff ;
for ( i = 0 ; i < ARRAY_SIZE ( omap2xxx_cm_idlest_offs ) ; i + + ) {
if ( idlest_offs = = omap2xxx_cm_idlest_offs [ i ] ) {
* idlest_reg_id = i + 1 ;
break ;
}
}
if ( i = = ARRAY_SIZE ( omap2xxx_cm_idlest_offs ) )
return - EINVAL ;
offs = idlest_reg - cm_base ;
offs & = 0xff00 ;
* prcm_inst = offs ;
return 0 ;
}
2012-10-21 11:01:11 +04:00
/*
*
*/
/**
* omap2xxx_cm_wait_module_ready - wait for a module to leave idle or standby
2014-10-27 18:39:23 +03:00
* @ part : PRCM partition , ignored for OMAP2
2012-10-21 11:01:11 +04:00
* @ prcm_mod : PRCM module offset
* @ idlest_id : CM_IDLESTx register ID ( i . e . , x = 1 , 2 , 3 )
* @ idlest_shift : shift of the bit in the CM_IDLEST * register to check
*
* Wait for the PRCM to indicate that the module identified by
* ( @ prcm_mod , @ idlest_id , @ idlest_shift ) is clocked . Return 0 upon
* success or - EBUSY if the module doesn ' t enable in time .
*/
2014-10-27 18:39:23 +03:00
int omap2xxx_cm_wait_module_ready ( u8 part , s16 prcm_mod , u16 idlest_id ,
u8 idlest_shift )
2012-10-21 11:01:11 +04:00
{
int ena = 0 , i = 0 ;
u8 cm_idlest_reg ;
u32 mask ;
if ( ! idlest_id | | ( idlest_id > ARRAY_SIZE ( omap2xxx_cm_idlest_offs ) ) )
return - EINVAL ;
cm_idlest_reg = omap2xxx_cm_idlest_offs [ idlest_id - 1 ] ;
mask = 1 < < idlest_shift ;
ena = mask ;
omap_test_timeout ( ( ( omap2_cm_read_mod_reg ( prcm_mod , cm_idlest_reg ) &
mask ) = = ena ) , MAX_MODULE_READY_TIME , i ) ;
return ( i < MAX_MODULE_READY_TIME ) ? 0 : - EBUSY ;
}
2012-10-21 11:01:11 +04:00
/* Clockdomain low-level functions */
static void omap2xxx_clkdm_allow_idle ( struct clockdomain * clkdm )
{
omap2xxx_cm_clkdm_enable_hwsup ( clkdm - > pwrdm . ptr - > prcm_offs ,
clkdm - > clktrctrl_mask ) ;
}
static void omap2xxx_clkdm_deny_idle ( struct clockdomain * clkdm )
{
omap2xxx_cm_clkdm_disable_hwsup ( clkdm - > pwrdm . ptr - > prcm_offs ,
clkdm - > clktrctrl_mask ) ;
}
static int omap2xxx_clkdm_clk_enable ( struct clockdomain * clkdm )
{
bool hwsup = false ;
if ( ! clkdm - > clktrctrl_mask )
return 0 ;
hwsup = omap2xxx_cm_is_clkdm_in_hwsup ( clkdm - > pwrdm . ptr - > prcm_offs ,
clkdm - > clktrctrl_mask ) ;
2013-01-26 11:58:16 +04:00
if ( ! hwsup & & clkdm - > flags & CLKDM_CAN_FORCE_WAKEUP )
omap2xxx_clkdm_wakeup ( clkdm ) ;
2012-10-21 11:01:11 +04:00
return 0 ;
}
static int omap2xxx_clkdm_clk_disable ( struct clockdomain * clkdm )
{
bool hwsup = false ;
if ( ! clkdm - > clktrctrl_mask )
return 0 ;
hwsup = omap2xxx_cm_is_clkdm_in_hwsup ( clkdm - > pwrdm . ptr - > prcm_offs ,
clkdm - > clktrctrl_mask ) ;
2013-01-26 11:58:16 +04:00
if ( ! hwsup & & clkdm - > flags & CLKDM_CAN_FORCE_SLEEP )
omap2xxx_clkdm_sleep ( clkdm ) ;
2012-10-21 11:01:11 +04:00
return 0 ;
}
struct clkdm_ops omap2_clkdm_operations = {
. clkdm_add_wkdep = omap2_clkdm_add_wkdep ,
. clkdm_del_wkdep = omap2_clkdm_del_wkdep ,
. clkdm_read_wkdep = omap2_clkdm_read_wkdep ,
. clkdm_clear_all_wkdeps = omap2_clkdm_clear_all_wkdeps ,
. clkdm_sleep = omap2xxx_clkdm_sleep ,
. clkdm_wakeup = omap2xxx_clkdm_wakeup ,
. clkdm_allow_idle = omap2xxx_clkdm_allow_idle ,
. clkdm_deny_idle = omap2xxx_clkdm_deny_idle ,
. clkdm_clk_enable = omap2xxx_clkdm_clk_enable ,
. clkdm_clk_disable = omap2xxx_clkdm_clk_disable ,
} ;
2012-10-30 06:56:29 +04:00
2013-10-11 20:15:31 +04:00
int omap2xxx_cm_fclks_active ( void )
{
u32 f1 , f2 ;
f1 = omap2_cm_read_mod_reg ( CORE_MOD , CM_FCLKEN1 ) ;
f2 = omap2_cm_read_mod_reg ( CORE_MOD , OMAP24XX_CM_FCLKEN2 ) ;
return ( f1 | f2 ) ? 1 : 0 ;
}
int omap2xxx_cm_mpu_retention_allowed ( void )
{
u32 l ;
/* Check for MMC, UART2, UART1, McSPI2, McSPI1 and DSS1. */
l = omap2_cm_read_mod_reg ( CORE_MOD , CM_FCLKEN1 ) ;
if ( l & ( OMAP2420_EN_MMC_MASK | OMAP24XX_EN_UART2_MASK |
OMAP24XX_EN_UART1_MASK | OMAP24XX_EN_MCSPI2_MASK |
OMAP24XX_EN_MCSPI1_MASK | OMAP24XX_EN_DSS1_MASK ) )
return 0 ;
/* Check for UART3. */
l = omap2_cm_read_mod_reg ( CORE_MOD , OMAP24XX_CM_FCLKEN2 ) ;
if ( l & OMAP24XX_EN_UART3_MASK )
return 0 ;
return 1 ;
}
u32 omap2xxx_cm_get_core_clk_src ( void )
{
u32 v ;
v = omap2_cm_read_mod_reg ( PLL_MOD , CM_CLKSEL2 ) ;
v & = OMAP24XX_CORE_CLK_SRC_MASK ;
return v ;
}
u32 omap2xxx_cm_get_core_pll_config ( void )
{
return omap2_cm_read_mod_reg ( PLL_MOD , CM_CLKSEL2 ) ;
}
void omap2xxx_cm_set_mod_dividers ( u32 mpu , u32 dsp , u32 gfx , u32 core , u32 mdm )
{
u32 tmp ;
omap2_cm_write_mod_reg ( mpu , MPU_MOD , CM_CLKSEL ) ;
omap2_cm_write_mod_reg ( dsp , OMAP24XX_DSP_MOD , CM_CLKSEL ) ;
omap2_cm_write_mod_reg ( gfx , GFX_MOD , CM_CLKSEL ) ;
tmp = omap2_cm_read_mod_reg ( CORE_MOD , CM_CLKSEL1 ) &
OMAP24XX_CLKSEL_DSS2_MASK ;
omap2_cm_write_mod_reg ( core | tmp , CORE_MOD , CM_CLKSEL1 ) ;
2014-03-04 20:19:03 +04:00
if ( mdm )
2013-10-11 20:15:31 +04:00
omap2_cm_write_mod_reg ( mdm , OMAP2430_MDM_MOD , CM_CLKSEL ) ;
}
2012-10-30 06:56:29 +04:00
/*
*
*/
static struct cm_ll_data omap2xxx_cm_ll_data = {
. split_idlest_reg = & omap2xxx_cm_split_idlest_reg ,
. wait_module_ready = & omap2xxx_cm_wait_module_ready ,
} ;
2014-11-21 16:51:37 +03:00
int __init omap2xxx_cm_init ( const struct omap_prcm_init_data * data )
2012-10-30 06:56:29 +04:00
{
return cm_register ( & omap2xxx_cm_ll_data ) ;
}
static void __exit omap2xxx_cm_exit ( void )
{
2014-03-04 20:19:03 +04:00
cm_unregister ( & omap2xxx_cm_ll_data ) ;
2012-10-30 06:56:29 +04:00
}
__exitcall ( omap2xxx_cm_exit ) ;