2009-02-06 19:48:59 +03:00
/*
* Copyright ( C ) 2009 by Sascha Hauer , Pengutronix
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/list.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <asm/clkdev.h>
# include <mach/clock.h>
# include <mach/hardware.h>
# include <mach/common.h>
# define CCM_BASE IO_ADDRESS(CCM_BASE_ADDR)
# define CCM_CCMR 0x00
# define CCM_PDR0 0x04
# define CCM_PDR1 0x08
# define CCM_PDR2 0x0C
# define CCM_PDR3 0x10
# define CCM_PDR4 0x14
# define CCM_RCSR 0x18
# define CCM_MPCTL 0x1C
# define CCM_PPCTL 0x20
# define CCM_ACMR 0x24
# define CCM_COSR 0x28
# define CCM_CGR0 0x2C
# define CCM_CGR1 0x30
# define CCM_CGR2 0x34
# define CCM_CGR3 0x38
# ifdef HAVE_SET_RATE_SUPPORT
static void calc_dividers ( u32 div , u32 * pre , u32 * post , u32 maxpost )
{
u32 min_pre , temp_pre , old_err , err ;
min_pre = ( div - 1 ) / maxpost + 1 ;
old_err = 8 ;
for ( temp_pre = 8 ; temp_pre > = min_pre ; temp_pre - - ) {
if ( div > ( temp_pre * maxpost ) )
break ;
if ( div < ( temp_pre * temp_pre ) )
continue ;
err = div % temp_pre ;
if ( err = = 0 ) {
* pre = temp_pre ;
break ;
}
err = temp_pre - err ;
if ( err < old_err ) {
old_err = err ;
* pre = temp_pre ;
}
}
* post = ( div + * pre - 1 ) / * pre ;
}
/* get the best values for a 3-bit divider combined with a 6-bit divider */
static void calc_dividers_3_6 ( u32 div , u32 * pre , u32 * post )
{
if ( div > = 512 ) {
* pre = 8 ;
* post = 64 ;
} else if ( div > = 64 ) {
calc_dividers ( div , pre , post , 64 ) ;
} else if ( div < = 8 ) {
* pre = div ;
* post = 1 ;
} else {
* pre = 1 ;
* post = div ;
}
}
/* get the best values for two cascaded 3-bit dividers */
static void calc_dividers_3_3 ( u32 div , u32 * pre , u32 * post )
{
if ( div > = 64 ) {
* pre = * post = 8 ;
} else if ( div > 8 ) {
calc_dividers ( div , pre , post , 8 ) ;
} else {
* pre = 1 ;
* post = div ;
}
}
# endif
static unsigned long get_rate_mpll ( void )
{
ulong mpctl = __raw_readl ( CCM_BASE + CCM_MPCTL ) ;
return mxc_decode_pll ( mpctl , 24000000 ) ;
}
static unsigned long get_rate_ppll ( void )
{
ulong ppctl = __raw_readl ( CCM_BASE + CCM_PPCTL ) ;
return mxc_decode_pll ( ppctl , 24000000 ) ;
}
struct arm_ahb_div {
unsigned char arm , ahb , sel ;
} ;
static struct arm_ahb_div clk_consumer [ ] = {
{ . arm = 1 , . ahb = 4 , . sel = 0 } ,
{ . arm = 1 , . ahb = 3 , . sel = 1 } ,
{ . arm = 2 , . ahb = 2 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
{ . arm = 4 , . ahb = 1 , . sel = 0 } ,
{ . arm = 1 , . ahb = 5 , . sel = 0 } ,
{ . arm = 1 , . ahb = 8 , . sel = 0 } ,
{ . arm = 1 , . ahb = 6 , . sel = 1 } ,
{ . arm = 2 , . ahb = 4 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
{ . arm = 4 , . ahb = 2 , . sel = 0 } ,
{ . arm = 0 , . ahb = 0 , . sel = 0 } ,
} ;
static unsigned long get_rate_arm ( void )
{
unsigned long pdr0 = __raw_readl ( CCM_BASE + CCM_PDR0 ) ;
struct arm_ahb_div * aad ;
unsigned long fref = get_rate_mpll ( ) ;
2009-03-23 12:07:48 +03:00
aad = & clk_consumer [ ( pdr0 > > 16 ) & 0xf ] ;
if ( aad - > sel )
fref = fref * 2 / 3 ;
2009-02-06 19:48:59 +03:00
return fref / aad - > arm ;
}
static unsigned long get_rate_ahb ( struct clk * clk )
{
unsigned long pdr0 = __raw_readl ( CCM_BASE + CCM_PDR0 ) ;
struct arm_ahb_div * aad ;
unsigned long fref = get_rate_mpll ( ) ;
2009-03-23 12:07:48 +03:00
aad = & clk_consumer [ ( pdr0 > > 16 ) & 0xf ] ;
2009-02-06 19:48:59 +03:00
return fref / aad - > ahb ;
}
static unsigned long get_rate_ipg ( struct clk * clk )
{
return get_rate_ahb ( NULL ) > > 1 ;
}
static unsigned long get_3_3_div ( unsigned long in )
{
return ( ( ( in > > 3 ) & 0x7 ) + 1 ) * ( ( in & 0x7 ) + 1 ) ;
}
static unsigned long get_rate_uart ( struct clk * clk )
{
unsigned long pdr3 = __raw_readl ( CCM_BASE + CCM_PDR3 ) ;
unsigned long pdr4 = __raw_readl ( CCM_BASE + CCM_PDR4 ) ;
unsigned long div = get_3_3_div ( pdr4 > > 10 ) ;
if ( pdr3 & ( 1 < < 14 ) )
return get_rate_arm ( ) / div ;
else
return get_rate_ppll ( ) / div ;
}
static unsigned long get_rate_sdhc ( struct clk * clk )
{
unsigned long pdr3 = __raw_readl ( CCM_BASE + CCM_PDR3 ) ;
unsigned long div , rate ;
if ( pdr3 & ( 1 < < 6 ) )
rate = get_rate_arm ( ) ;
else
rate = get_rate_ppll ( ) ;
switch ( clk - > id ) {
default :
case 0 :
div = pdr3 & 0x3f ;
break ;
case 1 :
div = ( pdr3 > > 8 ) & 0x3f ;
break ;
case 2 :
div = ( pdr3 > > 16 ) & 0x3f ;
break ;
}
return rate / get_3_3_div ( div ) ;
}
static unsigned long get_rate_mshc ( struct clk * clk )
{
unsigned long pdr1 = __raw_readl ( CCM_BASE + CCM_PDR1 ) ;
unsigned long div1 , div2 , rate ;
if ( pdr1 & ( 1 < < 7 ) )
rate = get_rate_arm ( ) ;
else
rate = get_rate_ppll ( ) ;
div1 = ( pdr1 > > 29 ) & 0x7 ;
div2 = ( pdr1 > > 22 ) & 0x3f ;
return rate / ( ( div1 + 1 ) * ( div2 + 1 ) ) ;
}
static unsigned long get_rate_ssi ( struct clk * clk )
{
unsigned long pdr2 = __raw_readl ( CCM_BASE + CCM_PDR2 ) ;
unsigned long div1 , div2 , rate ;
if ( pdr2 & ( 1 < < 6 ) )
rate = get_rate_arm ( ) ;
else
rate = get_rate_ppll ( ) ;
switch ( clk - > id ) {
default :
case 0 :
div1 = pdr2 & 0x3f ;
div2 = ( pdr2 > > 24 ) & 0x7 ;
break ;
case 1 :
div1 = ( pdr2 > > 8 ) & 0x3f ;
div2 = ( pdr2 > > 27 ) & 0x7 ;
break ;
}
return rate / ( ( div1 + 1 ) * ( div2 + 1 ) ) ;
}
static unsigned long get_rate_csi ( struct clk * clk )
{
unsigned long pdr2 = __raw_readl ( CCM_BASE + CCM_PDR2 ) ;
unsigned long rate ;
if ( pdr2 & ( 1 < < 7 ) )
rate = get_rate_arm ( ) ;
else
rate = get_rate_ppll ( ) ;
return rate / get_3_3_div ( ( pdr2 > > 16 ) & 0x3f ) ;
}
2009-03-17 17:43:54 +03:00
static unsigned long get_rate_otg ( struct clk * clk )
{
unsigned long pdr4 = __raw_readl ( CCM_BASE + CCM_PDR4 ) ;
unsigned long rate ;
if ( pdr4 & ( 1 < < 9 ) )
rate = get_rate_arm ( ) ;
else
rate = get_rate_ppll ( ) ;
return rate / get_3_3_div ( ( pdr4 > > 22 ) & 0x3f ) ;
}
2009-02-06 19:48:59 +03:00
static unsigned long get_rate_ipg_per ( struct clk * clk )
{
unsigned long pdr0 = __raw_readl ( CCM_BASE + CCM_PDR0 ) ;
unsigned long pdr4 = __raw_readl ( CCM_BASE + CCM_PDR4 ) ;
unsigned long div1 , div2 ;
if ( pdr0 & ( 1 < < 26 ) ) {
div1 = ( pdr4 > > 19 ) & 0x7 ;
div2 = ( pdr4 > > 16 ) & 0x7 ;
return get_rate_arm ( ) / ( ( div1 + 1 ) * ( div2 + 1 ) ) ;
} else {
div1 = ( pdr0 > > 12 ) & 0x7 ;
return get_rate_ahb ( NULL ) / div1 ;
}
}
static int clk_cgr_enable ( struct clk * clk )
{
u32 reg ;
reg = __raw_readl ( clk - > enable_reg ) ;
reg | = 3 < < clk - > enable_shift ;
__raw_writel ( reg , clk - > enable_reg ) ;
return 0 ;
}
static void clk_cgr_disable ( struct clk * clk )
{
u32 reg ;
reg = __raw_readl ( clk - > enable_reg ) ;
reg & = ~ ( 3 < < clk - > enable_shift ) ;
__raw_writel ( reg , clk - > enable_reg ) ;
}
# define DEFINE_CLOCK(name, i, er, es, gr, sr) \
static struct clk name = { \
. id = i , \
. enable_reg = CCM_BASE + er , \
. enable_shift = es , \
. get_rate = gr , \
. set_rate = sr , \
. enable = clk_cgr_enable , \
. disable = clk_cgr_disable , \
}
DEFINE_CLOCK ( asrc_clk , 0 , CCM_CGR0 , 0 , NULL , NULL ) ;
DEFINE_CLOCK ( ata_clk , 0 , CCM_CGR0 , 2 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( audmux_clk , 0 , CCM_CGR0 , 4 , NULL , NULL ) ;
DEFINE_CLOCK ( can1_clk , 0 , CCM_CGR0 , 6 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( can2_clk , 1 , CCM_CGR0 , 8 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( cspi1_clk , 0 , CCM_CGR0 , 10 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( cspi2_clk , 1 , CCM_CGR0 , 12 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( ect_clk , 0 , CCM_CGR0 , 14 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( edio_clk , 0 , CCM_CGR0 , 16 , NULL , NULL ) ;
DEFINE_CLOCK ( emi_clk , 0 , CCM_CGR0 , 18 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( epit1_clk , 0 , CCM_CGR0 , 20 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( epit2_clk , 1 , CCM_CGR0 , 22 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( esai_clk , 0 , CCM_CGR0 , 24 , NULL , NULL ) ;
DEFINE_CLOCK ( esdhc1_clk , 0 , CCM_CGR0 , 26 , get_rate_sdhc , NULL ) ;
DEFINE_CLOCK ( esdhc2_clk , 1 , CCM_CGR0 , 28 , get_rate_sdhc , NULL ) ;
DEFINE_CLOCK ( esdhc3_clk , 2 , CCM_CGR0 , 30 , get_rate_sdhc , NULL ) ;
DEFINE_CLOCK ( fec_clk , 0 , CCM_CGR1 , 0 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( gpio1_clk , 0 , CCM_CGR1 , 2 , NULL , NULL ) ;
DEFINE_CLOCK ( gpio2_clk , 1 , CCM_CGR1 , 4 , NULL , NULL ) ;
DEFINE_CLOCK ( gpio3_clk , 2 , CCM_CGR1 , 6 , NULL , NULL ) ;
DEFINE_CLOCK ( gpt_clk , 0 , CCM_CGR1 , 8 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( i2c1_clk , 0 , CCM_CGR1 , 10 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( i2c2_clk , 1 , CCM_CGR1 , 12 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( i2c3_clk , 2 , CCM_CGR1 , 14 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( iomuxc_clk , 0 , CCM_CGR1 , 16 , NULL , NULL ) ;
DEFINE_CLOCK ( ipu_clk , 0 , CCM_CGR1 , 18 , NULL , NULL ) ;
DEFINE_CLOCK ( kpp_clk , 0 , CCM_CGR1 , 20 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( mlb_clk , 0 , CCM_CGR1 , 22 , get_rate_ahb , NULL ) ;
DEFINE_CLOCK ( mshc_clk , 0 , CCM_CGR1 , 24 , get_rate_mshc , NULL ) ;
DEFINE_CLOCK ( owire_clk , 0 , CCM_CGR1 , 26 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( pwm_clk , 0 , CCM_CGR1 , 28 , get_rate_ipg_per , NULL ) ;
DEFINE_CLOCK ( rngc_clk , 0 , CCM_CGR1 , 30 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( rtc_clk , 0 , CCM_CGR2 , 0 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( rtic_clk , 0 , CCM_CGR2 , 2 , get_rate_ahb , NULL ) ;
DEFINE_CLOCK ( scc_clk , 0 , CCM_CGR2 , 4 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( sdma_clk , 0 , CCM_CGR2 , 6 , NULL , NULL ) ;
DEFINE_CLOCK ( spba_clk , 0 , CCM_CGR2 , 8 , get_rate_ipg , NULL ) ;
DEFINE_CLOCK ( spdif_clk , 0 , CCM_CGR2 , 10 , NULL , NULL ) ;
DEFINE_CLOCK ( ssi1_clk , 0 , CCM_CGR2 , 12 , get_rate_ssi , NULL ) ;
DEFINE_CLOCK ( ssi2_clk , 1 , CCM_CGR2 , 14 , get_rate_ssi , NULL ) ;
DEFINE_CLOCK ( uart1_clk , 0 , CCM_CGR2 , 16 , get_rate_uart , NULL ) ;
DEFINE_CLOCK ( uart2_clk , 1 , CCM_CGR2 , 18 , get_rate_uart , NULL ) ;
DEFINE_CLOCK ( uart3_clk , 2 , CCM_CGR2 , 20 , get_rate_uart , NULL ) ;
2009-03-17 17:43:54 +03:00
DEFINE_CLOCK ( usbotg_clk , 0 , CCM_CGR2 , 22 , get_rate_otg , NULL ) ;
2009-02-06 19:48:59 +03:00
DEFINE_CLOCK ( wdog_clk , 0 , CCM_CGR2 , 24 , NULL , NULL ) ;
DEFINE_CLOCK ( max_clk , 0 , CCM_CGR2 , 26 , NULL , NULL ) ;
DEFINE_CLOCK ( admux_clk , 0 , CCM_CGR2 , 30 , NULL , NULL ) ;
DEFINE_CLOCK ( csi_clk , 0 , CCM_CGR3 , 0 , get_rate_csi , NULL ) ;
DEFINE_CLOCK ( iim_clk , 0 , CCM_CGR3 , 2 , NULL , NULL ) ;
DEFINE_CLOCK ( gpu2d_clk , 0 , CCM_CGR3 , 4 , NULL , NULL ) ;
# define _REGISTER_CLOCK(d, n, c) \
{ \
. dev_id = d , \
. con_id = n , \
. clk = & c , \
} ,
2009-05-26 21:01:46 +04:00
static struct clk_lookup lookups [ ] = {
2009-02-06 19:48:59 +03:00
_REGISTER_CLOCK ( NULL , " asrc " , asrc_clk )
_REGISTER_CLOCK ( NULL , " ata " , ata_clk )
_REGISTER_CLOCK ( NULL , " audmux " , audmux_clk )
_REGISTER_CLOCK ( NULL , " can " , can1_clk )
_REGISTER_CLOCK ( NULL , " can " , can2_clk )
_REGISTER_CLOCK ( " spi_imx.0 " , NULL , cspi1_clk )
_REGISTER_CLOCK ( " spi_imx.1 " , NULL , cspi2_clk )
_REGISTER_CLOCK ( NULL , " ect " , ect_clk )
_REGISTER_CLOCK ( NULL , " edio " , edio_clk )
_REGISTER_CLOCK ( NULL , " emi " , emi_clk )
_REGISTER_CLOCK ( NULL , " epit " , epit1_clk )
_REGISTER_CLOCK ( NULL , " epit " , epit2_clk )
_REGISTER_CLOCK ( NULL , " esai " , esai_clk )
_REGISTER_CLOCK ( NULL , " sdhc " , esdhc1_clk )
_REGISTER_CLOCK ( NULL , " sdhc " , esdhc2_clk )
_REGISTER_CLOCK ( NULL , " sdhc " , esdhc3_clk )
_REGISTER_CLOCK ( " fec.0 " , NULL , fec_clk )
_REGISTER_CLOCK ( NULL , " gpio " , gpio1_clk )
_REGISTER_CLOCK ( NULL , " gpio " , gpio2_clk )
_REGISTER_CLOCK ( NULL , " gpio " , gpio3_clk )
_REGISTER_CLOCK ( " gpt.0 " , NULL , gpt_clk )
_REGISTER_CLOCK ( " imx-i2c.0 " , NULL , i2c1_clk )
_REGISTER_CLOCK ( " imx-i2c.1 " , NULL , i2c2_clk )
_REGISTER_CLOCK ( " imx-i2c.2 " , NULL , i2c3_clk )
_REGISTER_CLOCK ( NULL , " iomuxc " , iomuxc_clk )
2009-04-01 12:09:18 +04:00
_REGISTER_CLOCK ( " ipu-core " , NULL , ipu_clk )
_REGISTER_CLOCK ( " mx3_sdc_fb " , NULL , ipu_clk )
2009-02-06 19:48:59 +03:00
_REGISTER_CLOCK ( NULL , " kpp " , kpp_clk )
_REGISTER_CLOCK ( NULL , " mlb " , mlb_clk )
_REGISTER_CLOCK ( NULL , " mshc " , mshc_clk )
_REGISTER_CLOCK ( " mxc_w1 " , NULL , owire_clk )
_REGISTER_CLOCK ( NULL , " pwm " , pwm_clk )
_REGISTER_CLOCK ( NULL , " rngc " , rngc_clk )
_REGISTER_CLOCK ( NULL , " rtc " , rtc_clk )
_REGISTER_CLOCK ( NULL , " rtic " , rtic_clk )
_REGISTER_CLOCK ( NULL , " scc " , scc_clk )
_REGISTER_CLOCK ( NULL , " sdma " , sdma_clk )
_REGISTER_CLOCK ( NULL , " spba " , spba_clk )
_REGISTER_CLOCK ( NULL , " spdif " , spdif_clk )
_REGISTER_CLOCK ( NULL , " ssi " , ssi1_clk )
_REGISTER_CLOCK ( NULL , " ssi " , ssi2_clk )
_REGISTER_CLOCK ( " imx-uart.0 " , NULL , uart1_clk )
_REGISTER_CLOCK ( " imx-uart.1 " , NULL , uart2_clk )
_REGISTER_CLOCK ( " imx-uart.2 " , NULL , uart3_clk )
2009-03-17 17:43:54 +03:00
_REGISTER_CLOCK ( " mxc-ehci.0 " , " usb " , usbotg_clk )
_REGISTER_CLOCK ( " mxc-ehci.1 " , " usb " , usbotg_clk )
_REGISTER_CLOCK ( " mxc-ehci.2 " , " usb " , usbotg_clk )
_REGISTER_CLOCK ( " fsl-usb2-udc " , " usb " , usbotg_clk )
2009-10-08 20:12:24 +04:00
_REGISTER_CLOCK ( " imx-wdt.0 " , NULL , wdog_clk )
2009-02-06 19:48:59 +03:00
_REGISTER_CLOCK ( NULL , " max " , max_clk )
_REGISTER_CLOCK ( NULL , " admux " , admux_clk )
_REGISTER_CLOCK ( NULL , " csi " , csi_clk )
_REGISTER_CLOCK ( NULL , " iim " , iim_clk )
_REGISTER_CLOCK ( NULL , " gpu2d " , gpu2d_clk )
} ;
int __init mx35_clocks_init ( )
{
int i ;
unsigned int ll = 0 ;
# ifdef CONFIG_DEBUG_LL_CONSOLE
ll = ( 3 < < 16 ) ;
# endif
for ( i = 0 ; i < ARRAY_SIZE ( lookups ) ; i + + )
clkdev_add ( & lookups [ i ] ) ;
/* Turn off all clocks except the ones we need to survive, namely:
* EMI , GPIO1 / 2 / 3 , GPT , IOMUX , MAX and eventually uart
*/
__raw_writel ( ( 3 < < 18 ) , CCM_BASE + CCM_CGR0 ) ;
__raw_writel ( ( 3 < < 2 ) | ( 3 < < 4 ) | ( 3 < < 6 ) | ( 3 < < 8 ) | ( 3 < < 16 ) ,
CCM_BASE + CCM_CGR1 ) ;
__raw_writel ( ( 3 < < 26 ) | ll , CCM_BASE + CCM_CGR2 ) ;
__raw_writel ( 0 , CCM_BASE + CCM_CGR3 ) ;
2009-07-15 11:31:15 +04:00
mxc_timer_init ( & gpt_clk , IO_ADDRESS ( GPT1_BASE_ADDR ) , MXC_INT_GPT ) ;
2009-02-06 19:48:59 +03:00
return 0 ;
}