2008-10-21 14:06:33 +01:00
/* linux/arch/arm/plat-s3c24xx/clock-dclk.c
*
2009-11-13 22:54:14 +00:00
* Copyright ( c ) 2004 - 2008 Simtec Electronics
2008-10-21 14:06:33 +01:00
* Ben Dooks < ben @ simtec . co . uk >
* http : //armlinux.simtec.co.uk/
*
* 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 .
*
* S3C24XX - definitions for DCLK and CLKOUT registers
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <mach/regs-clock.h>
# include <mach/regs-gpio.h>
# include <plat/clock.h>
# include <plat/cpu.h>
/* clocks that could be registered by external code */
static int s3c24xx_dclk_enable ( struct clk * clk , int enable )
{
unsigned long dclkcon = __raw_readl ( S3C24XX_DCLKCON ) ;
if ( enable )
dclkcon | = clk - > ctrlbit ;
else
dclkcon & = ~ clk - > ctrlbit ;
__raw_writel ( dclkcon , S3C24XX_DCLKCON ) ;
return 0 ;
}
static int s3c24xx_dclk_setparent ( struct clk * clk , struct clk * parent )
{
unsigned long dclkcon ;
unsigned int uclk ;
if ( parent = = & clk_upll )
uclk = 1 ;
else if ( parent = = & clk_p )
uclk = 0 ;
else
return - EINVAL ;
clk - > parent = parent ;
dclkcon = __raw_readl ( S3C24XX_DCLKCON ) ;
if ( clk - > ctrlbit = = S3C2410_DCLKCON_DCLK0EN ) {
if ( uclk )
dclkcon | = S3C2410_DCLKCON_DCLK0_UCLK ;
else
dclkcon & = ~ S3C2410_DCLKCON_DCLK0_UCLK ;
} else {
if ( uclk )
dclkcon | = S3C2410_DCLKCON_DCLK1_UCLK ;
else
dclkcon & = ~ S3C2410_DCLKCON_DCLK1_UCLK ;
}
__raw_writel ( dclkcon , S3C24XX_DCLKCON ) ;
return 0 ;
}
static unsigned long s3c24xx_calc_div ( struct clk * clk , unsigned long rate )
{
unsigned long div ;
if ( ( rate = = 0 ) | | ! clk - > parent )
return 0 ;
div = clk_get_rate ( clk - > parent ) / rate ;
if ( div < 2 )
div = 2 ;
else if ( div > 16 )
div = 16 ;
return div ;
}
static unsigned long s3c24xx_round_dclk_rate ( struct clk * clk ,
unsigned long rate )
{
unsigned long div = s3c24xx_calc_div ( clk , rate ) ;
if ( div = = 0 )
return 0 ;
return clk_get_rate ( clk - > parent ) / div ;
}
static int s3c24xx_set_dclk_rate ( struct clk * clk , unsigned long rate )
{
unsigned long mask , data , div = s3c24xx_calc_div ( clk , rate ) ;
if ( div = = 0 )
return - EINVAL ;
if ( clk = = & s3c24xx_dclk0 ) {
mask = S3C2410_DCLKCON_DCLK0_DIV_MASK |
S3C2410_DCLKCON_DCLK0_CMP_MASK ;
data = S3C2410_DCLKCON_DCLK0_DIV ( div ) |
S3C2410_DCLKCON_DCLK0_CMP ( ( div + 1 ) / 2 ) ;
} else if ( clk = = & s3c24xx_dclk1 ) {
mask = S3C2410_DCLKCON_DCLK1_DIV_MASK |
S3C2410_DCLKCON_DCLK1_CMP_MASK ;
data = S3C2410_DCLKCON_DCLK1_DIV ( div ) |
S3C2410_DCLKCON_DCLK1_CMP ( ( div + 1 ) / 2 ) ;
} else
return - EINVAL ;
clk - > rate = clk_get_rate ( clk - > parent ) / div ;
__raw_writel ( ( ( __raw_readl ( S3C24XX_DCLKCON ) & ~ mask ) | data ) ,
S3C24XX_DCLKCON ) ;
return clk - > rate ;
}
static int s3c24xx_clkout_setparent ( struct clk * clk , struct clk * parent )
{
unsigned long mask ;
unsigned long source ;
/* calculate the MISCCR setting for the clock */
2009-08-13 11:53:53 +02:00
if ( parent = = & clk_mpll )
2008-10-21 14:06:33 +01:00
source = S3C2410_MISCCR_CLK0_MPLL ;
else if ( parent = = & clk_upll )
source = S3C2410_MISCCR_CLK0_UPLL ;
else if ( parent = = & clk_f )
source = S3C2410_MISCCR_CLK0_FCLK ;
else if ( parent = = & clk_h )
source = S3C2410_MISCCR_CLK0_HCLK ;
else if ( parent = = & clk_p )
source = S3C2410_MISCCR_CLK0_PCLK ;
else if ( clk = = & s3c24xx_clkout0 & & parent = = & s3c24xx_dclk0 )
source = S3C2410_MISCCR_CLK0_DCLK0 ;
else if ( clk = = & s3c24xx_clkout1 & & parent = = & s3c24xx_dclk1 )
source = S3C2410_MISCCR_CLK0_DCLK0 ;
else
return - EINVAL ;
clk - > parent = parent ;
if ( clk = = & s3c24xx_clkout0 )
mask = S3C2410_MISCCR_CLK0_MASK ;
else {
source < < = 4 ;
mask = S3C2410_MISCCR_CLK1_MASK ;
}
s3c2410_modify_misccr ( mask , source ) ;
return 0 ;
}
/* external clock definitions */
2009-12-01 01:24:37 +00:00
static struct clk_ops dclk_ops = {
. set_parent = s3c24xx_dclk_setparent ,
. set_rate = s3c24xx_set_dclk_rate ,
. round_rate = s3c24xx_round_dclk_rate ,
} ;
2008-10-21 14:06:33 +01:00
struct clk s3c24xx_dclk0 = {
. name = " dclk0 " ,
. id = - 1 ,
. ctrlbit = S3C2410_DCLKCON_DCLK0EN ,
. enable = s3c24xx_dclk_enable ,
2009-12-01 01:24:37 +00:00
. ops = & dclk_ops ,
2008-10-21 14:06:33 +01:00
} ;
struct clk s3c24xx_dclk1 = {
. name = " dclk1 " ,
. id = - 1 ,
. ctrlbit = S3C2410_DCLKCON_DCLK1EN ,
. enable = s3c24xx_dclk_enable ,
2009-12-01 01:24:37 +00:00
. ops = & dclk_ops ,
} ;
static struct clk_ops clkout_ops = {
. set_parent = s3c24xx_clkout_setparent ,
2008-10-21 14:06:33 +01:00
} ;
struct clk s3c24xx_clkout0 = {
. name = " clkout0 " ,
. id = - 1 ,
2009-12-01 01:24:37 +00:00
. ops = & clkout_ops ,
2008-10-21 14:06:33 +01:00
} ;
struct clk s3c24xx_clkout1 = {
. name = " clkout1 " ,
. id = - 1 ,
2009-12-01 01:24:37 +00:00
. ops = & clkout_ops ,
2008-10-21 14:06:33 +01:00
} ;