2007-02-16 12:12:31 +01:00
/* linux/arch/arm/mach-s3c2443/clock.c
*
2010-01-30 10:25:49 +02:00
* Copyright ( c ) 2007 , 2010 Simtec Electronics
2007-02-16 12:12:31 +01:00
* Ben Dooks < ben @ simtec . co . uk >
*
* S3C2443 Clock control support
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
2010-04-28 18:03:57 +09:00
2007-02-16 12:12:31 +01:00
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/sysdev.h>
# include <linux/clk.h>
# include <linux/mutex.h>
# include <linux/serial_core.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2007-02-16 12:12:31 +01:00
# include <asm/mach/map.h>
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2007-02-16 12:12:31 +01:00
2008-08-05 16:14:15 +01:00
# include <mach/regs-s3c2443-clock.h>
2007-02-16 12:12:31 +01:00
2008-10-21 14:06:38 +01:00
# include <plat/cpu-freq.h>
2008-10-07 22:26:09 +01:00
# include <plat/s3c2443.h>
2008-10-07 23:09:51 +01:00
# include <plat/clock.h>
2010-01-30 09:19:59 +02:00
# include <plat/clock-clksrc.h>
2008-10-07 22:26:09 +01:00
# include <plat/cpu.h>
2007-02-16 12:12:31 +01:00
/* We currently have to assume that the system is running
* from the XTPll input , and that all * * * REFCLKs are being
* fed from it , as we cannot read the state of OM [ 4 ] from
* software .
*
* It would be possible for each board initialisation to
* set the correct muxing at initialisation
*/
/* clock selections */
static struct clk clk_i2s_ext = {
. name = " i2s-ext " ,
} ;
2008-07-07 18:12:39 +01:00
/* armdiv
*
* this clock is sourced from msysclk and can have a number of
* divider values applied to it to then be fed into armclk .
*/
2010-01-30 11:14:14 +02:00
/* armdiv divisor table */
static unsigned int armdiv [ 16 ] = {
[ S3C2443_CLKDIV0_ARMDIV_1 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 1 ,
[ S3C2443_CLKDIV0_ARMDIV_2 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 2 ,
[ S3C2443_CLKDIV0_ARMDIV_3 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 3 ,
[ S3C2443_CLKDIV0_ARMDIV_4 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 4 ,
[ S3C2443_CLKDIV0_ARMDIV_6 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 6 ,
[ S3C2443_CLKDIV0_ARMDIV_8 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 8 ,
[ S3C2443_CLKDIV0_ARMDIV_12 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 12 ,
[ S3C2443_CLKDIV0_ARMDIV_16 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] = 16 ,
} ;
static inline unsigned int s3c2443_fclk_div ( unsigned long clkcon0 )
{
clkcon0 & = S3C2443_CLKDIV0_ARMDIV_MASK ;
return armdiv [ clkcon0 > > S3C2443_CLKDIV0_ARMDIV_SHIFT ] ;
}
static unsigned long s3c2443_armclk_roundrate ( struct clk * clk ,
unsigned long rate )
{
unsigned long parent = clk_get_rate ( clk - > parent ) ;
unsigned long calc ;
unsigned best = 256 ; /* bigger than any value */
unsigned div ;
int ptr ;
for ( ptr = 0 ; ptr < ARRAY_SIZE ( armdiv ) ; ptr + + ) {
div = armdiv [ ptr ] ;
calc = parent / div ;
if ( calc < = rate & & div < best )
best = div ;
}
return parent / best ;
}
static int s3c2443_armclk_setrate ( struct clk * clk , unsigned long rate )
{
unsigned long parent = clk_get_rate ( clk - > parent ) ;
unsigned long calc ;
unsigned div ;
unsigned best = 256 ; /* bigger than any value */
int ptr ;
int val = - 1 ;
for ( ptr = 0 ; ptr < ARRAY_SIZE ( armdiv ) ; ptr + + ) {
div = armdiv [ ptr ] ;
calc = parent / div ;
if ( calc < = rate & & div < best ) {
best = div ;
val = ptr ;
}
}
if ( val > = 0 ) {
unsigned long clkcon0 ;
clkcon0 = __raw_readl ( S3C2443_CLKDIV0 ) ;
clkcon0 & = S3C2443_CLKDIV0_ARMDIV_MASK ;
clkcon0 | = val < < S3C2443_CLKDIV0_ARMDIV_SHIFT ;
__raw_writel ( clkcon0 , S3C2443_CLKDIV0 ) ;
}
return ( val = = - 1 ) ? - EINVAL : 0 ;
}
2008-07-07 18:12:39 +01:00
static struct clk clk_armdiv = {
. name = " armdiv " ,
2010-01-30 10:25:49 +02:00
. parent = & clk_msysclk . clk ,
2010-01-30 11:14:14 +02:00
. ops = & ( struct clk_ops ) {
. round_rate = s3c2443_armclk_roundrate ,
. set_rate = s3c2443_armclk_setrate ,
} ,
2008-07-07 18:12:39 +01:00
} ;
/* armclk
*
2010-01-30 10:25:49 +02:00
* this is the clock fed into the ARM core itself , from armdiv or from hclk .
2008-07-07 18:12:39 +01:00
*/
2010-01-30 10:25:49 +02:00
static struct clk * clk_arm_sources [ ] = {
[ 0 ] = & clk_armdiv ,
[ 1 ] = & clk_h ,
} ;
2008-07-07 18:12:39 +01:00
2010-01-30 10:25:49 +02:00
static struct clksrc_clk clk_arm = {
. clk = {
. name = " armclk " ,
2009-12-01 01:24:37 +00:00
} ,
2010-01-30 10:25:49 +02:00
. sources = & ( struct clksrc_sources ) {
. sources = clk_arm_sources ,
. nr_sources = ARRAY_SIZE ( clk_arm_sources ) ,
} ,
. reg_src = { . reg = S3C2443_CLKDIV0 , . size = 1 , . shift = 13 } ,
2008-07-07 18:12:39 +01:00
} ;
2007-02-16 12:12:31 +01:00
/* hsspi
*
* high - speed spi clock , sourced from esysclk
*/
2010-01-30 09:19:59 +02:00
static struct clksrc_clk clk_hsspi = {
. clk = {
. name = " hsspi " ,
2010-01-30 10:25:49 +02:00
. parent = & clk_esysclk . clk ,
2010-01-30 09:19:59 +02:00
. ctrlbit = S3C2443_SCLKCON_HSSPICLK ,
. enable = s3c2443_clkcon_enable_s ,
2009-12-01 01:24:37 +00:00
} ,
2010-01-30 09:19:59 +02:00
. reg_div = { . reg = S3C2443_CLKDIV1 , . size = 2 , . shift = 4 } ,
2007-02-16 12:12:31 +01:00
} ;
/* clk_hsmcc_div
*
* this clock is sourced from epll , and is fed through a divider ,
* to a mux controlled by sclkcon where either it or a extclk can
* be fed to the hsmmc block
*/
2010-01-30 09:19:59 +02:00
static struct clksrc_clk clk_hsmmc_div = {
. clk = {
. name = " hsmmc-div " ,
2011-06-14 19:12:26 +09:00
. devname = " s3c-sdhci.1 " ,
2010-01-30 10:25:49 +02:00
. parent = & clk_esysclk . clk ,
2009-12-01 01:24:37 +00:00
} ,
2010-01-30 09:19:59 +02:00
. reg_div = { . reg = S3C2443_CLKDIV1 , . size = 2 , . shift = 6 } ,
2007-02-16 12:12:31 +01:00
} ;
static int s3c2443_setparent_hsmmc ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2443_SCLKCON ) ;
clksrc & = ~ ( S3C2443_SCLKCON_HSMMCCLK_EXT |
S3C2443_SCLKCON_HSMMCCLK_EPLL ) ;
if ( parent = = & clk_epll )
clksrc | = S3C2443_SCLKCON_HSMMCCLK_EPLL ;
else if ( parent = = & clk_ext )
clksrc | = S3C2443_SCLKCON_HSMMCCLK_EXT ;
else
return - EINVAL ;
if ( clk - > usage > 0 ) {
__raw_writel ( clksrc , S3C2443_SCLKCON ) ;
}
clk - > parent = parent ;
return 0 ;
}
static int s3c2443_enable_hsmmc ( struct clk * clk , int enable )
{
return s3c2443_setparent_hsmmc ( clk , clk - > parent ) ;
}
static struct clk clk_hsmmc = {
. name = " hsmmc-if " ,
2011-06-14 19:12:26 +09:00
. devname = " s3c-sdhci.1 " ,
2010-01-30 09:19:59 +02:00
. parent = & clk_hsmmc_div . clk ,
2007-02-16 12:12:31 +01:00
. enable = s3c2443_enable_hsmmc ,
2009-12-01 01:24:37 +00:00
. ops = & ( struct clk_ops ) {
. set_parent = s3c2443_setparent_hsmmc ,
} ,
2007-02-16 12:12:31 +01:00
} ;
/* i2s_eplldiv
*
2010-05-10 14:51:19 +05:30
* This clock is the output from the I2S divisor of ESYSCLK , and is separate
2010-01-30 09:19:59 +02:00
* from the mux that comes after it ( cannot merge into one single clock )
2007-02-16 12:12:31 +01:00
*/
2010-01-30 09:19:59 +02:00
static struct clksrc_clk clk_i2s_eplldiv = {
. clk = {
. name = " i2s-eplldiv " ,
2010-01-30 10:25:49 +02:00
. parent = & clk_esysclk . clk ,
2009-12-01 01:24:37 +00:00
} ,
2010-01-30 09:19:59 +02:00
. reg_div = { . reg = S3C2443_CLKDIV1 , . size = 4 , . shift = 12 , } ,
2007-02-16 12:12:31 +01:00
} ;
/* i2s-ref
*
* i2s bus reference clock , selectable from external , esysclk or epllref
2010-01-30 09:19:59 +02:00
*
* Note , this used to be two clocks , but was compressed into one .
2007-02-16 12:12:31 +01:00
*/
2010-01-30 09:19:59 +02:00
struct clk * clk_i2s_srclist [ ] = {
[ 0 ] = & clk_i2s_eplldiv . clk ,
[ 1 ] = & clk_i2s_ext ,
[ 2 ] = & clk_epllref . clk ,
[ 3 ] = & clk_epllref . clk ,
} ;
2007-02-16 12:12:31 +01:00
2010-01-30 09:19:59 +02:00
static struct clksrc_clk clk_i2s = {
. clk = {
. name = " i2s-if " ,
. ctrlbit = S3C2443_SCLKCON_I2SCLK ,
. enable = s3c2443_clkcon_enable_s ,
2007-02-16 12:12:31 +01:00
2009-12-01 01:24:37 +00:00
} ,
2010-01-30 09:19:59 +02:00
. sources = & ( struct clksrc_sources ) {
. sources = clk_i2s_srclist ,
. nr_sources = ARRAY_SIZE ( clk_i2s_srclist ) ,
} ,
. reg_src = { . reg = S3C2443_CLKSRC , . size = 2 , . shift = 14 } ,
2007-02-16 12:12:31 +01:00
} ;
/* standard clock definitions */
2010-04-28 12:58:13 +09:00
static struct clk init_clocks_off [ ] = {
2007-02-16 12:12:31 +01:00
{
. name = " sdi " ,
. parent = & clk_p ,
. enable = s3c2443_clkcon_enable_p ,
. ctrlbit = S3C2443_PCLKCON_SDI ,
} , {
. name = " iis " ,
. parent = & clk_p ,
. enable = s3c2443_clkcon_enable_p ,
. ctrlbit = S3C2443_PCLKCON_IIS ,
} , {
. name = " spi " ,
2011-06-14 19:12:26 +09:00
. devname = " s3c2410-spi.0 " ,
2007-02-16 12:12:31 +01:00
. parent = & clk_p ,
. enable = s3c2443_clkcon_enable_p ,
. ctrlbit = S3C2443_PCLKCON_SPI0 ,
} , {
. name = " spi " ,
2011-06-14 19:12:26 +09:00
. devname = " s3c2410-spi.1 " ,
2007-02-16 12:12:31 +01:00
. parent = & clk_p ,
. enable = s3c2443_clkcon_enable_p ,
. ctrlbit = S3C2443_PCLKCON_SPI1 ,
}
} ;
static struct clk init_clocks [ ] = {
} ;
/* clocks to add straight away */
2010-01-30 09:19:59 +02:00
static struct clksrc_clk * clksrcs [ ] __initdata = {
2010-01-30 10:25:49 +02:00
& clk_arm ,
2007-02-16 12:12:31 +01:00
& clk_i2s_eplldiv ,
& clk_i2s ,
& clk_hsspi ,
& clk_hsmmc_div ,
2010-01-30 09:19:59 +02:00
} ;
static struct clk * clks [ ] __initdata = {
2007-02-16 12:12:31 +01:00
& clk_hsmmc ,
2008-07-07 18:12:39 +01:00
& clk_armdiv ,
2007-02-16 12:12:31 +01:00
} ;
2008-10-21 14:06:38 +01:00
void __init_or_cpufreq s3c2443_setup_clocks ( void )
2007-02-16 12:12:31 +01:00
{
2010-04-28 18:03:57 +09:00
s3c2443_common_setup_clocks ( s3c2443_get_mpll , s3c2443_fclk_div ) ;
2008-10-21 14:06:38 +01:00
}
void __init s3c2443_init_clocks ( int xtal )
{
unsigned long epllcon = __raw_readl ( S3C2443_EPLLCON ) ;
int ptr ;
2010-04-28 18:03:57 +09:00
clk_epll . rate = s3c2443_get_epll ( epllcon , xtal ) ;
clk_epll . parent = & clk_epllref . clk ;
s3c2443_common_init_clocks ( xtal , s3c2443_get_mpll , s3c2443_fclk_div ) ;
2008-10-21 14:06:38 +01:00
s3c2443_setup_clocks ( ) ;
2007-02-16 12:12:31 +01:00
2010-04-28 12:58:13 +09:00
s3c24xx_register_clocks ( clks , ARRAY_SIZE ( clks ) ) ;
2007-02-16 12:12:31 +01:00
2010-01-30 09:19:59 +02:00
for ( ptr = 0 ; ptr < ARRAY_SIZE ( clksrcs ) ; ptr + + )
s3c_register_clksrc ( clksrcs [ ptr ] , 1 ) ;
2007-02-16 12:12:31 +01:00
/* register clocks from clock array */
2010-01-06 01:21:38 +09:00
s3c_register_clocks ( init_clocks , ARRAY_SIZE ( init_clocks ) ) ;
2007-02-16 12:12:31 +01:00
/* We must be careful disabling the clocks we are not intending to
2007-10-19 23:10:43 +02:00
* be using at boot time , as subsystems such as the LCD which do
2007-02-16 12:12:31 +01:00
* their own DMA requests to the bus can cause the system to lockup
* if they where in the middle of requesting bus access .
*
* Disabling the LCD clock if the LCD is active is very dangerous ,
* and therefore the bootloader should be careful to not enable
* the LCD clock if it is not needed .
*/
/* install (and disable) the clocks we do not need immediately */
2010-04-28 12:58:13 +09:00
s3c_register_clocks ( init_clocks_off , ARRAY_SIZE ( init_clocks_off ) ) ;
s3c_disable_clocks ( init_clocks_off , ARRAY_SIZE ( init_clocks_off ) ) ;
2008-11-21 10:36:05 +00:00
s3c_pwmclk_init ( ) ;
2007-02-16 12:12:31 +01:00
}