2007-02-11 20:31:01 +03:00
/* linux/arch/arm/mach-s3c2412/clock.c
2006-06-25 00:21:31 +04:00
*
* Copyright ( c ) 2006 Simtec Electronics
* Ben Dooks < ben @ simtec . co . uk >
*
* S3C2412 , S3C2413 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>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/list.h>
# include <linux/errno.h>
# include <linux/err.h>
2011-12-22 04:26:03 +04:00
# include <linux/device.h>
2006-06-25 00:21:31 +04:00
# include <linux/clk.h>
# include <linux/mutex.h>
# include <linux/delay.h>
2006-12-18 01:22:26 +03:00
# include <linux/serial_core.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2006-06-25 00:21:31 +04:00
2006-12-17 22:59:37 +03:00
# include <asm/mach/map.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2006-06-25 00:21:31 +04:00
2008-10-08 01:26:09 +04:00
# include <plat/regs-serial.h>
2008-08-05 19:14:15 +04:00
# include <mach/regs-clock.h>
# include <mach/regs-gpio.h>
2006-06-25 00:21:31 +04:00
2008-10-08 02:09:51 +04:00
# include <plat/s3c2412.h>
# include <plat/clock.h>
2008-10-08 01:26:09 +04:00
# include <plat/cpu.h>
2006-06-25 00:21:31 +04: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
*/
2006-12-17 22:59:37 +03:00
static int s3c2412_clkcon_enable ( struct clk * clk , int enable )
2006-06-25 00:21:31 +04:00
{
unsigned int clocks = clk - > ctrlbit ;
unsigned long clkcon ;
clkcon = __raw_readl ( S3C2410_CLKCON ) ;
if ( enable )
clkcon | = clocks ;
else
clkcon & = ~ clocks ;
__raw_writel ( clkcon , S3C2410_CLKCON ) ;
return 0 ;
}
static int s3c2412_upll_enable ( struct clk * clk , int enable )
{
unsigned long upllcon = __raw_readl ( S3C2410_UPLLCON ) ;
unsigned long orig = upllcon ;
if ( ! enable )
upllcon | = S3C2412_PLLCON_OFF ;
else
upllcon & = ~ S3C2412_PLLCON_OFF ;
__raw_writel ( upllcon , S3C2410_UPLLCON ) ;
/* allow ~150uS for the PLL to settle and lock */
if ( enable & & ( orig & S3C2412_PLLCON_OFF ) )
udelay ( 150 ) ;
return 0 ;
}
/* clock selections */
static struct clk clk_erefclk = {
. name = " erefclk " ,
} ;
static struct clk clk_urefclk = {
. name = " urefclk " ,
} ;
static int s3c2412_setparent_usysclk ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
if ( parent = = & clk_urefclk )
clksrc & = ~ S3C2412_CLKSRC_USYSCLK_UPLL ;
else if ( parent = = & clk_upll )
clksrc | = S3C2412_CLKSRC_USYSCLK_UPLL ;
else
return - EINVAL ;
clk - > parent = parent ;
__raw_writel ( clksrc , S3C2412_CLKSRC ) ;
return 0 ;
}
static struct clk clk_usysclk = {
. name = " usysclk " ,
. parent = & clk_xtal ,
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. set_parent = s3c2412_setparent_usysclk ,
} ,
2006-06-25 00:21:31 +04:00
} ;
static struct clk clk_mrefclk = {
. name = " mrefclk " ,
. parent = & clk_xtal ,
} ;
static struct clk clk_mdivclk = {
. name = " mdivclk " ,
. parent = & clk_xtal ,
} ;
static int s3c2412_setparent_usbsrc ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
if ( parent = = & clk_usysclk )
clksrc & = ~ S3C2412_CLKSRC_USBCLK_HCLK ;
else if ( parent = = & clk_h )
clksrc | = S3C2412_CLKSRC_USBCLK_HCLK ;
else
return - EINVAL ;
clk - > parent = parent ;
__raw_writel ( clksrc , S3C2412_CLKSRC ) ;
return 0 ;
}
static unsigned long s3c2412_roundrate_usbsrc ( struct clk * clk ,
unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
int div ;
if ( rate > parent_rate )
return parent_rate ;
div = parent_rate / rate ;
if ( div > 2 )
div = 2 ;
return parent_rate / div ;
}
static unsigned long s3c2412_getrate_usbsrc ( struct clk * clk )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long div = __raw_readl ( S3C2410_CLKDIVN ) ;
return parent_rate / ( ( div & S3C2412_CLKDIVN_USB48DIV ) ? 2 : 1 ) ;
}
static int s3c2412_setrate_usbsrc ( struct clk * clk , unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long clkdivn = __raw_readl ( S3C2410_CLKDIVN ) ;
rate = s3c2412_roundrate_usbsrc ( clk , rate ) ;
if ( ( parent_rate / rate ) = = 2 )
clkdivn | = S3C2412_CLKDIVN_USB48DIV ;
else
clkdivn & = ~ S3C2412_CLKDIVN_USB48DIV ;
__raw_writel ( clkdivn , S3C2410_CLKDIVN ) ;
return 0 ;
}
static struct clk clk_usbsrc = {
. name = " usbsrc " ,
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. get_rate = s3c2412_getrate_usbsrc ,
. set_rate = s3c2412_setrate_usbsrc ,
. round_rate = s3c2412_roundrate_usbsrc ,
. set_parent = s3c2412_setparent_usbsrc ,
} ,
2006-06-25 00:21:31 +04:00
} ;
static int s3c2412_setparent_msysclk ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
if ( parent = = & clk_mdivclk )
clksrc & = ~ S3C2412_CLKSRC_MSYSCLK_MPLL ;
2008-01-28 15:01:30 +03:00
else if ( parent = = & clk_mpll )
2006-06-25 00:21:31 +04:00
clksrc | = S3C2412_CLKSRC_MSYSCLK_MPLL ;
else
return - EINVAL ;
clk - > parent = parent ;
__raw_writel ( clksrc , S3C2412_CLKSRC ) ;
return 0 ;
}
static struct clk clk_msysclk = {
. name = " msysclk " ,
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. set_parent = s3c2412_setparent_msysclk ,
} ,
2006-06-25 00:21:31 +04:00
} ;
2008-01-28 15:01:18 +03:00
static int s3c2412_setparent_armclk ( struct clk * clk , struct clk * parent )
{
unsigned long flags ;
unsigned long clkdiv ;
unsigned long dvs ;
/* Note, we current equate fclk andf msysclk for S3C2412 */
if ( parent = = & clk_msysclk | | parent = = & clk_f )
dvs = 0 ;
else if ( parent = = & clk_h )
dvs = S3C2412_CLKDIVN_DVSEN ;
else
return - EINVAL ;
clk - > parent = parent ;
/* update this under irq lockdown, clkdivn is not protected
* by the clock system . */
local_irq_save ( flags ) ;
clkdiv = __raw_readl ( S3C2410_CLKDIVN ) ;
clkdiv & = ~ S3C2412_CLKDIVN_DVSEN ;
clkdiv | = dvs ;
__raw_writel ( clkdiv , S3C2410_CLKDIVN ) ;
local_irq_restore ( flags ) ;
return 0 ;
}
static struct clk clk_armclk = {
. name = " armclk " ,
. parent = & clk_msysclk ,
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. set_parent = s3c2412_setparent_armclk ,
} ,
2008-01-28 15:01:18 +03:00
} ;
2006-06-25 00:21:31 +04:00
/* these next clocks have an divider immediately after them,
* so we can register them with their divider and leave out the
* intermediate clock stage
*/
static unsigned long s3c2412_roundrate_clksrc ( 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 as they cancel out */
div = ( rate / parent_rate ) ;
if ( div < 1 )
div = 1 ;
else if ( div > 16 )
div = 16 ;
return parent_rate / div ;
}
static int s3c2412_setparent_uart ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
if ( parent = = & clk_erefclk )
clksrc & = ~ S3C2412_CLKSRC_UARTCLK_MPLL ;
else if ( parent = = & clk_mpll )
clksrc | = S3C2412_CLKSRC_UARTCLK_MPLL ;
else
return - EINVAL ;
clk - > parent = parent ;
__raw_writel ( clksrc , S3C2412_CLKSRC ) ;
return 0 ;
}
static unsigned long s3c2412_getrate_uart ( struct clk * clk )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long div = __raw_readl ( S3C2410_CLKDIVN ) ;
div & = S3C2412_CLKDIVN_UARTDIV_MASK ;
div > > = S3C2412_CLKDIVN_UARTDIV_SHIFT ;
return parent_rate / ( div + 1 ) ;
}
static int s3c2412_setrate_uart ( struct clk * clk , unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long clkdivn = __raw_readl ( S3C2410_CLKDIVN ) ;
rate = s3c2412_roundrate_clksrc ( clk , rate ) ;
clkdivn & = ~ S3C2412_CLKDIVN_UARTDIV_MASK ;
clkdivn | = ( ( parent_rate / rate ) - 1 ) < < S3C2412_CLKDIVN_UARTDIV_SHIFT ;
__raw_writel ( clkdivn , S3C2410_CLKDIVN ) ;
return 0 ;
}
static struct clk clk_uart = {
. name = " uartclk " ,
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. get_rate = s3c2412_getrate_uart ,
. set_rate = s3c2412_setrate_uart ,
. set_parent = s3c2412_setparent_uart ,
. round_rate = s3c2412_roundrate_clksrc ,
} ,
2006-06-25 00:21:31 +04:00
} ;
static int s3c2412_setparent_i2s ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
if ( parent = = & clk_erefclk )
clksrc & = ~ S3C2412_CLKSRC_I2SCLK_MPLL ;
else if ( parent = = & clk_mpll )
clksrc | = S3C2412_CLKSRC_I2SCLK_MPLL ;
else
return - EINVAL ;
clk - > parent = parent ;
__raw_writel ( clksrc , S3C2412_CLKSRC ) ;
return 0 ;
}
static unsigned long s3c2412_getrate_i2s ( struct clk * clk )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long div = __raw_readl ( S3C2410_CLKDIVN ) ;
div & = S3C2412_CLKDIVN_I2SDIV_MASK ;
div > > = S3C2412_CLKDIVN_I2SDIV_SHIFT ;
return parent_rate / ( div + 1 ) ;
}
static int s3c2412_setrate_i2s ( struct clk * clk , unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long clkdivn = __raw_readl ( S3C2410_CLKDIVN ) ;
rate = s3c2412_roundrate_clksrc ( clk , rate ) ;
clkdivn & = ~ S3C2412_CLKDIVN_I2SDIV_MASK ;
clkdivn | = ( ( parent_rate / rate ) - 1 ) < < S3C2412_CLKDIVN_I2SDIV_SHIFT ;
__raw_writel ( clkdivn , S3C2410_CLKDIVN ) ;
return 0 ;
}
static struct clk clk_i2s = {
. name = " i2sclk " ,
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. get_rate = s3c2412_getrate_i2s ,
. set_rate = s3c2412_setrate_i2s ,
. set_parent = s3c2412_setparent_i2s ,
. round_rate = s3c2412_roundrate_clksrc ,
} ,
2006-06-25 00:21:31 +04:00
} ;
static int s3c2412_setparent_cam ( struct clk * clk , struct clk * parent )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
if ( parent = = & clk_usysclk )
clksrc & = ~ S3C2412_CLKSRC_CAMCLK_HCLK ;
else if ( parent = = & clk_h )
clksrc | = S3C2412_CLKSRC_CAMCLK_HCLK ;
else
return - EINVAL ;
clk - > parent = parent ;
__raw_writel ( clksrc , S3C2412_CLKSRC ) ;
return 0 ;
}
static unsigned long s3c2412_getrate_cam ( struct clk * clk )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long div = __raw_readl ( S3C2410_CLKDIVN ) ;
div & = S3C2412_CLKDIVN_CAMDIV_MASK ;
div > > = S3C2412_CLKDIVN_CAMDIV_SHIFT ;
return parent_rate / ( div + 1 ) ;
}
static int s3c2412_setrate_cam ( struct clk * clk , unsigned long rate )
{
unsigned long parent_rate = clk_get_rate ( clk - > parent ) ;
unsigned long clkdivn = __raw_readl ( S3C2410_CLKDIVN ) ;
rate = s3c2412_roundrate_clksrc ( clk , rate ) ;
clkdivn & = ~ S3C2412_CLKDIVN_CAMDIV_MASK ;
clkdivn | = ( ( parent_rate / rate ) - 1 ) < < S3C2412_CLKDIVN_CAMDIV_SHIFT ;
__raw_writel ( clkdivn , S3C2410_CLKDIVN ) ;
return 0 ;
}
static struct clk clk_cam = {
. name = " camif-upll " , /* same as 2440 name */
2009-12-01 04:24:37 +03:00
. ops = & ( struct clk_ops ) {
. get_rate = s3c2412_getrate_cam ,
. set_rate = s3c2412_setrate_cam ,
. set_parent = s3c2412_setparent_cam ,
. round_rate = s3c2412_roundrate_clksrc ,
} ,
2006-06-25 00:21:31 +04:00
} ;
/* standard clock definitions */
static struct clk init_clocks_disable [ ] = {
{
. name = " nand " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_NAND ,
} , {
. name = " sdi " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_SDI ,
} , {
. name = " adc " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_ADC ,
} , {
. name = " i2c " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_IIC ,
} , {
. name = " iis " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_IIS ,
} , {
. name = " spi " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_SPI ,
}
} ;
static struct clk init_clocks [ ] = {
{
. name = " dma " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_DMA0 ,
} , {
. name = " dma " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_DMA1 ,
} , {
. name = " dma " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_DMA2 ,
} , {
. name = " dma " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_DMA3 ,
} , {
. name = " lcd " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_LCDC ,
} , {
. name = " gpio " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_GPIO ,
} , {
. name = " usb-host " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_USBH ,
} , {
. name = " usb-device " ,
. parent = & clk_h ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_USBD ,
} , {
. name = " timers " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_PWMT ,
} , {
. name = " uart " ,
2011-06-14 14:12:26 +04:00
. devname = " s3c2412-uart.0 " ,
2006-06-25 00:21:31 +04:00
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_UART0 ,
} , {
. name = " uart " ,
2011-06-14 14:12:26 +04:00
. devname = " s3c2412-uart.1 " ,
2006-06-25 00:21:31 +04:00
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_UART1 ,
} , {
. name = " uart " ,
2011-06-14 14:12:26 +04:00
. devname = " s3c2412-uart.2 " ,
2006-06-25 00:21:31 +04:00
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_UART2 ,
} , {
. name = " rtc " ,
. parent = & clk_p ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_RTC ,
} , {
. name = " watchdog " ,
. parent = & clk_p ,
. ctrlbit = 0 ,
} , {
. name = " usb-bus-gadget " ,
. parent = & clk_usb_bus ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_USB_DEV48 ,
} , {
. name = " usb-bus-host " ,
. parent = & clk_usb_bus ,
. enable = s3c2412_clkcon_enable ,
. ctrlbit = S3C2412_CLKCON_USB_HOST48 ,
}
} ;
/* clocks to add where we need to check their parentage */
struct clk_init {
struct clk * clk ;
unsigned int bit ;
struct clk * src_0 ;
struct clk * src_1 ;
} ;
2006-12-17 22:59:37 +03:00
static struct clk_init clks_src [ ] __initdata = {
2006-06-25 00:21:31 +04:00
{
. clk = & clk_usysclk ,
. bit = S3C2412_CLKSRC_USBCLK_HCLK ,
. src_0 = & clk_urefclk ,
. src_1 = & clk_upll ,
} , {
. clk = & clk_i2s ,
. bit = S3C2412_CLKSRC_I2SCLK_MPLL ,
. src_0 = & clk_erefclk ,
. src_1 = & clk_mpll ,
} , {
. clk = & clk_cam ,
. bit = S3C2412_CLKSRC_CAMCLK_HCLK ,
. src_0 = & clk_usysclk ,
. src_1 = & clk_h ,
} , {
. clk = & clk_msysclk ,
. bit = S3C2412_CLKSRC_MSYSCLK_MPLL ,
. src_0 = & clk_mdivclk ,
. src_1 = & clk_mpll ,
} , {
. clk = & clk_uart ,
. bit = S3C2412_CLKSRC_UARTCLK_MPLL ,
. src_0 = & clk_erefclk ,
. src_1 = & clk_mpll ,
} , {
. clk = & clk_usbsrc ,
. bit = S3C2412_CLKSRC_USBCLK_HCLK ,
. src_0 = & clk_usysclk ,
. src_1 = & clk_h ,
2008-07-03 14:24:45 +04:00
/* here we assume OM[4] select xtal */
} , {
. clk = & clk_erefclk ,
. bit = S3C2412_CLKSRC_EREFCLK_EXTCLK ,
. src_0 = & clk_xtal ,
. src_1 = & clk_ext ,
} , {
. clk = & clk_urefclk ,
. bit = S3C2412_CLKSRC_UREFCLK_EXTCLK ,
. src_0 = & clk_xtal ,
. src_1 = & clk_ext ,
2006-06-25 00:21:31 +04:00
} ,
} ;
/* s3c2412_clk_initparents
*
* Initialise the parents for the clocks that we get at start - time
*/
static void __init s3c2412_clk_initparents ( void )
{
unsigned long clksrc = __raw_readl ( S3C2412_CLKSRC ) ;
struct clk_init * cip = clks_src ;
struct clk * src ;
int ptr ;
int ret ;
for ( ptr = 0 ; ptr < ARRAY_SIZE ( clks_src ) ; ptr + + , cip + + ) {
ret = s3c24xx_register_clock ( cip - > clk ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " Failed to register clock %s (%d) \n " ,
cip - > clk - > name , ret ) ;
}
src = ( clksrc & cip - > bit ) ? cip - > src_1 : cip - > src_0 ;
printk ( KERN_INFO " %s: parent %s \n " , cip - > clk - > name , src - > name ) ;
clk_set_parent ( cip - > clk , src ) ;
}
}
/* clocks to add straight away */
2006-12-17 22:59:37 +03:00
static struct clk * clks [ ] __initdata = {
2006-06-25 00:21:31 +04:00
& clk_ext ,
& clk_usb_bus ,
& clk_mrefclk ,
2008-01-28 15:01:18 +03:00
& clk_armclk ,
2006-06-25 00:21:31 +04:00
} ;
2011-10-24 14:08:42 +04:00
static struct clk_lookup s3c2412_clk_lookup [ ] = {
CLKDEV_INIT ( NULL , " clk_uart_baud1 " , & s3c24xx_uclk ) ,
CLKDEV_INIT ( NULL , " clk_uart_baud2 " , & clk_p ) ,
CLKDEV_INIT ( NULL , " clk_uart_baud3 " , & clk_usysclk ) ,
} ;
2006-06-25 00:21:31 +04:00
int __init s3c2412_baseclk_add ( void )
{
unsigned long clkcon = __raw_readl ( S3C2410_CLKCON ) ;
2008-01-28 15:01:18 +03:00
unsigned int dvs ;
2006-06-25 00:21:31 +04:00
struct clk * clkp ;
int ret ;
int ptr ;
clk_upll . enable = s3c2412_upll_enable ;
clk_usb_bus . parent = & clk_usbsrc ;
clk_usb_bus . rate = 0x0 ;
2008-01-28 15:01:31 +03:00
clk_f . parent = & clk_msysclk ;
2006-06-25 00:21:31 +04:00
s3c2412_clk_initparents ( ) ;
for ( ptr = 0 ; ptr < ARRAY_SIZE ( clks ) ; ptr + + ) {
clkp = clks [ ptr ] ;
ret = s3c24xx_register_clock ( clkp ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " Failed to register clock %s (%d) \n " ,
clkp - > name , ret ) ;
}
}
2008-01-28 15:01:18 +03:00
/* set the dvs state according to what we got at boot time */
dvs = __raw_readl ( S3C2410_CLKDIVN ) & S3C2412_CLKDIVN_DVSEN ;
if ( dvs )
clk_armclk . parent = & clk_h ;
printk ( KERN_INFO " S3C2412: DVS is %s \n " , dvs ? " on " : " off " ) ;
2006-06-25 00:21:31 +04:00
/* ensure usb bus clock is within correct rate of 48MHz */
if ( clk_get_rate ( & clk_usb_bus ) ! = ( 48 * 1000 * 1000 ) ) {
printk ( KERN_INFO " Warning: USB bus clock not at 48MHz \n " ) ;
/* for the moment, let's use the UPLL, and see if we can
* get 48 MHz */
clk_set_parent ( & clk_usysclk , & clk_upll ) ;
clk_set_parent ( & clk_usbsrc , & clk_usysclk ) ;
clk_set_rate ( & clk_usbsrc , 48 * 1000 * 1000 ) ;
}
printk ( " S3C2412: upll %s, %ld.%03ld MHz, usb-bus %ld.%03ld MHz \n " ,
( __raw_readl ( S3C2410_UPLLCON ) & S3C2412_PLLCON_OFF ) ? " off " : " on " ,
print_mhz ( clk_get_rate ( & clk_upll ) ) ,
print_mhz ( clk_get_rate ( & clk_usb_bus ) ) ) ;
/* register clocks from clock array */
clkp = init_clocks ;
for ( ptr = 0 ; ptr < ARRAY_SIZE ( init_clocks ) ; ptr + + , clkp + + ) {
/* ensure that we note the clock state */
clkp - > usage = clkcon & clkp - > ctrlbit ? 1 : 0 ;
ret = s3c24xx_register_clock ( clkp ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " Failed to register clock %s (%d) \n " ,
clkp - > name , ret ) ;
}
}
/* We must be careful disabling the clocks we are not intending to
2007-10-20 01:10:43 +04:00
* be using at boot time , as subsystems such as the LCD which do
2006-06-25 00:21:31 +04: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 */
clkp = init_clocks_disable ;
for ( ptr = 0 ; ptr < ARRAY_SIZE ( init_clocks_disable ) ; ptr + + , clkp + + ) {
ret = s3c24xx_register_clock ( clkp ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " Failed to register clock %s (%d) \n " ,
clkp - > name , ret ) ;
}
s3c2412_clkcon_enable ( clkp , 0 ) ;
}
2011-10-24 14:08:42 +04:00
clkdev_add_table ( s3c2412_clk_lookup , ARRAY_SIZE ( s3c2412_clk_lookup ) ) ;
2008-11-21 13:36:05 +03:00
s3c_pwmclk_init ( ) ;
2006-06-25 00:21:31 +04:00
return 0 ;
}