2007-02-11 18:31:01 +01:00
/* linux/arch/arm/mach-s3c2440/clock.c
2005-07-26 22:39:14 +01:00
*
* Copyright ( c ) 2004 - 2005 Simtec Electronics
* http : //armlinux.simtec.co.uk/
* Ben Dooks < ben @ simtec . co . uk >
*
* S3C2440 Clock 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>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
2006-03-20 21:00:48 +00:00
# include <linux/mutex.h>
2006-01-07 16:15:52 +00:00
# include <linux/clk.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2011-10-24 11:47:40 +02:00
# include <linux/serial_core.h>
2005-07-26 22:39:14 +01:00
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2011-07-26 16:09:06 -07:00
# include <linux/atomic.h>
2005-07-26 22:39:14 +01:00
# include <asm/irq.h>
2008-08-05 16:14:15 +01:00
# include <mach/regs-clock.h>
2005-07-26 22:39:14 +01:00
2008-10-07 23:09:51 +01:00
# include <plat/clock.h>
2008-10-07 22:26:09 +01:00
# include <plat/cpu.h>
2011-10-24 11:47:40 +02:00
# include <plat/regs-serial.h>
2005-07-26 22:39:14 +01:00
/* S3C2440 extended clock support */
2006-03-20 21:00:12 +00:00
static unsigned long s3c2440_camif_upll_round ( struct clk * clk ,
unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
int div ;
if ( rate > parent_rate )
return parent_rate ;
/* note, we remove the +/- 1 calculations for the divisor */
div = ( parent_rate / rate ) / 2 ;
if ( div < 1 )
div = 1 ;
else if ( div > 16 )
div = 16 ;
return parent_rate / ( div * 2 ) ;
}
static int s3c2440_camif_upll_setrate ( struct clk * clk , unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long camdivn = __raw_readl ( S3C2440_CAMDIVN ) ;
rate = s3c2440_camif_upll_round ( clk , rate ) ;
camdivn & = ~ ( S3C2440_CAMDIVN_CAMCLK_SEL | S3C2440_CAMDIVN_CAMCLK_MASK ) ;
if ( rate ! = parent_rate ) {
camdivn | = S3C2440_CAMDIVN_CAMCLK_SEL ;
camdivn | = ( ( ( parent_rate / rate ) / 2 ) - 1 ) ;
}
__raw_writel ( camdivn , S3C2440_CAMDIVN ) ;
return 0 ;
}
/* Extra S3C2440 clocks */
2005-07-26 22:39:14 +01:00
static struct clk s3c2440_clk_cam = {
. name = " camif " ,
2006-06-22 22:18:20 +01:00
. enable = s3c2410_clkcon_enable ,
2005-07-26 22:39:14 +01:00
. ctrlbit = S3C2440_CLKCON_CAMERA ,
} ;
2006-03-20 21:00:12 +00:00
static struct clk s3c2440_clk_cam_upll = {
. name = " camif-upll " ,
2009-12-01 01:24:37 +00:00
. ops = & ( struct clk_ops ) {
. set_rate = s3c2440_camif_upll_setrate ,
. round_rate = s3c2440_camif_upll_round ,
} ,
2006-03-20 21:00:12 +00:00
} ;
2005-07-26 22:39:14 +01:00
static struct clk s3c2440_clk_ac97 = {
. name = " ac97 " ,
2006-06-22 22:18:20 +01:00
. enable = s3c2410_clkcon_enable ,
2012-07-13 17:48:56 +09:00
. ctrlbit = S3C2440_CLKCON_AC97 ,
2005-07-26 22:39:14 +01:00
} ;
2011-10-24 11:47:40 +02:00
static unsigned long s3c2440_fclk_n_getrate ( struct clk * clk )
{
unsigned long ucon0 , ucon1 , ucon2 , divisor ;
/* the fun of calculating the uart divisors on the s3c2440 */
ucon0 = __raw_readl ( S3C24XX_VA_UART0 + S3C2410_UCON ) ;
ucon1 = __raw_readl ( S3C24XX_VA_UART1 + S3C2410_UCON ) ;
ucon2 = __raw_readl ( S3C24XX_VA_UART2 + S3C2410_UCON ) ;
ucon0 & = S3C2440_UCON0_DIVMASK ;
ucon1 & = S3C2440_UCON1_DIVMASK ;
ucon2 & = S3C2440_UCON2_DIVMASK ;
if ( ucon0 ! = 0 )
divisor = ( ucon0 > > S3C2440_UCON_DIVSHIFT ) + 6 ;
else if ( ucon1 ! = 0 )
divisor = ( ucon1 > > S3C2440_UCON_DIVSHIFT ) + 21 ;
else if ( ucon2 ! = 0 )
divisor = ( ucon2 > > S3C2440_UCON_DIVSHIFT ) + 36 ;
else
/* manual calims 44, seems to be 9 */
divisor = 9 ;
return clk_get_rate ( clk - > parent ) / divisor ;
}
static struct clk s3c2440_clk_fclk_n = {
. name = " fclk_n " ,
. parent = & clk_f ,
. ops = & ( struct clk_ops ) {
. get_rate = s3c2440_fclk_n_getrate ,
} ,
} ;
2011-10-24 12:08:42 +02:00
static struct clk_lookup s3c2440_clk_lookup [ ] = {
CLKDEV_INIT ( NULL , " clk_uart_baud1 " , & s3c24xx_uclk ) ,
CLKDEV_INIT ( NULL , " clk_uart_baud2 " , & clk_p ) ,
CLKDEV_INIT ( NULL , " clk_uart_baud3 " , & s3c2440_clk_fclk_n ) ,
} ;
2012-01-27 15:35:25 +09:00
static int s3c2440_clk_add ( struct device * dev , struct subsys_interface * sif )
2005-07-26 22:39:14 +01:00
{
2008-01-28 13:01:34 +01:00
struct clk * clock_upll ;
2006-12-17 20:38:14 +01:00
struct clk * clock_h ;
struct clk * clock_p ;
2005-07-26 22:39:14 +01:00
2006-12-17 20:38:14 +01:00
clock_p = clk_get ( NULL , " pclk " ) ;
clock_h = clk_get ( NULL , " hclk " ) ;
clock_upll = clk_get ( NULL , " upll " ) ;
2005-07-26 22:39:14 +01:00
2006-12-17 20:38:14 +01:00
if ( IS_ERR ( clock_p ) | | IS_ERR ( clock_h ) | | IS_ERR ( clock_upll ) ) {
2005-07-26 22:39:14 +01:00
printk ( KERN_ERR " S3C2440: Failed to get parent clocks \n " ) ;
return - EINVAL ;
}
2006-12-17 20:38:14 +01:00
s3c2440_clk_cam . parent = clock_h ;
s3c2440_clk_ac97 . parent = clock_p ;
s3c2440_clk_cam_upll . parent = clock_upll ;
2011-10-24 11:47:40 +02:00
s3c24xx_register_clock ( & s3c2440_clk_fclk_n ) ;
2005-07-26 22:39:14 +01:00
s3c24xx_register_clock ( & s3c2440_clk_ac97 ) ;
s3c24xx_register_clock ( & s3c2440_clk_cam ) ;
2006-03-20 21:00:12 +00:00
s3c24xx_register_clock ( & s3c2440_clk_cam_upll ) ;
2011-10-24 12:08:42 +02:00
clkdev_add_table ( s3c2440_clk_lookup , ARRAY_SIZE ( s3c2440_clk_lookup ) ) ;
2005-07-26 22:39:14 +01:00
clk_disable ( & s3c2440_clk_ac97 ) ;
clk_disable ( & s3c2440_clk_cam ) ;
return 0 ;
}
2011-12-21 16:01:38 -08:00
static struct subsys_interface s3c2440_clk_interface = {
. name = " s3c2440_clk " ,
. subsys = & s3c2440_subsys ,
. add_dev = s3c2440_clk_add ,
2005-07-26 22:39:14 +01:00
} ;
2011-12-21 16:01:38 -08:00
static __init int s3c24xx_clk_init ( void )
2005-07-26 22:39:14 +01:00
{
2011-12-21 16:01:38 -08:00
return subsys_interface_register ( & s3c2440_clk_interface ) ;
2005-07-26 22:39:14 +01:00
}
2011-12-21 16:01:38 -08:00
arch_initcall ( s3c24xx_clk_init ) ;