2013-01-31 16:54:38 -08:00
/*
2007-02-11 18:31:01 +01:00
* Copyright ( c ) 2006 Simtec Electronics
2005-04-16 15:20:36 -07:00
* Ben Dooks < ben @ simtec . co . uk >
*
2007-02-11 18:31:01 +01:00
* S3C2410 , S3C2440 , S3C2442 Clock control support
2005-04-16 15:20:36 -07:00
*
* 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-21 16:26:03 -08:00
# include <linux/device.h>
2006-01-07 16:15:52 +00:00
# include <linux/clk.h>
2006-01-12 18:42:23 +00:00
# include <linux/mutex.h>
2006-03-20 17:10:04 +00:00
# include <linux/delay.h>
2007-02-11 18:31:01 +01:00
# include <linux/serial_core.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2007-02-11 18:31:01 +01:00
# include <asm/mach/map.h>
2005-04-16 15:20:36 -07:00
2008-08-05 16:14:15 +01:00
# include <mach/hardware.h>
2005-04-16 15:20:36 -07:00
2008-10-07 22:26:09 +01:00
# include <plat/regs-serial.h>
2008-08-05 16:14:15 +01:00
# include <mach/regs-clock.h>
# include <mach/regs-gpio.h>
2005-04-16 15:20:36 -07: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>
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
int s3c2410_clkcon_enable ( struct clk * clk , int enable )
2005-04-16 15:20:36 -07:00
{
2007-02-11 18:31:01 +01:00
unsigned int clocks = clk - > ctrlbit ;
unsigned long clkcon ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
clkcon = __raw_readl ( S3C2410_CLKCON ) ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
if ( enable )
clkcon | = clocks ;
2005-10-18 07:51:34 +01:00
else
2007-02-11 18:31:01 +01:00
clkcon & = ~ clocks ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
/* ensure none of the special function bits set */
clkcon & = ~ ( S3C2410_CLKCON_IDLE | S3C2410_CLKCON_POWER ) ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
__raw_writel ( clkcon , S3C2410_CLKCON ) ;
2005-04-16 15:20:36 -07:00
2006-02-08 21:09:05 +00:00
return 0 ;
2005-04-16 15:20:36 -07:00
}
2007-02-11 18:31:01 +01:00
static int s3c2410_upll_enable ( struct clk * clk , int enable )
2005-04-16 15:20:36 -07:00
{
2007-02-11 18:31:01 +01:00
unsigned long clkslow = __raw_readl ( S3C2410_CLKSLOW ) ;
unsigned long orig = clkslow ;
2006-03-20 17:10:07 +00:00
if ( enable )
2007-02-11 18:31:01 +01:00
clkslow & = ~ S3C2410_CLKSLOW_UCLK_OFF ;
2006-03-20 17:10:07 +00:00
else
2007-02-11 18:31:01 +01:00
clkslow | = S3C2410_CLKSLOW_UCLK_OFF ;
2006-03-20 17:10:07 +00:00
2007-02-11 18:31:01 +01:00
__raw_writel ( clkslow , S3C2410_CLKSLOW ) ;
2006-03-20 17:10:07 +00:00
2007-02-11 18:31:01 +01:00
/* if we started the UPLL, then allow to settle */
2006-03-20 17:10:07 +00:00
2007-02-11 18:31:01 +01:00
if ( enable & & ( orig & S3C2410_CLKSLOW_UCLK_OFF ) )
udelay ( 200 ) ;
2006-03-20 17:10:07 +00:00
return 0 ;
}
2007-02-11 18:31:01 +01:00
/* standard clock definitions */
2010-04-28 12:58:13 +09:00
static struct clk init_clocks_off [ ] = {
2007-02-11 18:31:01 +01:00
{
. name = " nand " ,
. parent = & clk_h ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_NAND ,
} , {
. name = " sdi " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_SDI ,
} , {
. name = " adc " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_ADC ,
} , {
. name = " i2c " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_IIC ,
} , {
. name = " iis " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_IIS ,
} , {
. name = " spi " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_SPI ,
2006-03-20 17:10:07 +00:00
}
2005-04-16 15:20:36 -07:00
} ;
2013-07-24 13:23:51 +09:00
static struct clk clk_lcd = {
. name = " lcd " ,
. parent = & clk_h ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_LCDC ,
} ;
static struct clk clk_gpio = {
. name = " gpio " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_GPIO ,
} ;
static struct clk clk_usb_host = {
. name = " usb-host " ,
. parent = & clk_h ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_USBH ,
} ;
static struct clk clk_usb_device = {
. name = " usb-device " ,
. parent = & clk_h ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_USBD ,
} ;
static struct clk clk_timers = {
. name = " timers " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_PWMT ,
} ;
struct clk s3c24xx_clk_uart0 = {
. name = " uart " ,
. devname = " s3c2410-uart.0 " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_UART0 ,
} ;
struct clk s3c24xx_clk_uart1 = {
. name = " uart " ,
. devname = " s3c2410-uart.1 " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_UART1 ,
} ;
struct clk s3c24xx_clk_uart2 = {
. name = " uart " ,
. devname = " s3c2410-uart.2 " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_UART2 ,
} ;
static struct clk clk_rtc = {
. name = " rtc " ,
. parent = & clk_p ,
. enable = s3c2410_clkcon_enable ,
. ctrlbit = S3C2410_CLKCON_RTC ,
} ;
static struct clk clk_watchdog = {
. name = " watchdog " ,
. parent = & clk_p ,
. ctrlbit = 0 ,
} ;
static struct clk clk_usb_bus_host = {
. name = " usb-bus-host " ,
. parent = & clk_usb_bus ,
} ;
static struct clk clk_usb_bus_gadget = {
. name = " usb-bus-gadget " ,
. parent = & clk_usb_bus ,
} ;
static struct clk * init_clocks [ ] = {
& clk_lcd ,
& clk_gpio ,
& clk_usb_host ,
& clk_usb_device ,
& clk_timers ,
& s3c24xx_clk_uart0 ,
& s3c24xx_clk_uart1 ,
& s3c24xx_clk_uart2 ,
& clk_rtc ,
& clk_watchdog ,
& clk_usb_bus_host ,
& clk_usb_bus_gadget ,
2005-04-16 15:20:36 -07:00
} ;
2007-02-11 18:31:01 +01:00
/* s3c2410_baseclk_add()
*
* Add all the clocks used by the s3c2410 or compatible CPUs
* such as the S3C2440 and S3C2442 .
*
* We cannot use a system device as we are needed before any
* of the init - calls that initialise the devices are actually
* done .
*/
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
int __init s3c2410_baseclk_add ( void )
2005-04-16 15:20:36 -07:00
{
2007-02-11 18:31:01 +01:00
unsigned long clkslow = __raw_readl ( S3C2410_CLKSLOW ) ;
unsigned long clkcon = __raw_readl ( S3C2410_CLKCON ) ;
struct clk * xtal ;
int ret ;
int ptr ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
clk_upll . enable = s3c2410_upll_enable ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
if ( s3c24xx_register_clock ( & clk_usb_bus ) < 0 )
printk ( KERN_ERR " failed to register usb bus clock \n " ) ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
/* register clocks from clock array */
2005-04-16 15:20:36 -07:00
2013-07-24 13:23:51 +09:00
for ( ptr = 0 ; ptr < ARRAY_SIZE ( init_clocks ) ; ptr + + ) {
struct clk * clkp = init_clocks [ ptr ] ;
2007-02-11 18:31:01 +01:00
/* ensure that we note the clock state */
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
clkp - > usage = clkcon & clkp - > ctrlbit ? 1 : 0 ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
ret = s3c24xx_register_clock ( clkp ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " Failed to register clock %s (%d) \n " ,
clkp - > name , ret ) ;
}
}
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +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-11 18:31:01 +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 ) ) ;
2006-03-20 17:10:04 +00:00
2007-02-11 18:31:01 +01:00
/* show the clock-slow value */
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
xtal = clk_get ( NULL , " xtal " ) ;
2005-04-16 15:20:36 -07:00
2007-02-11 18:31:01 +01:00
printk ( " CLOCK: Slow mode (%ld.%ld MHz), %s, MPLL %s, UPLL %s \n " ,
print_mhz ( clk_get_rate ( xtal ) /
( 2 * S3C2410_CLKSLOW_GET_SLOWVAL ( clkslow ) ) ) ,
( clkslow & S3C2410_CLKSLOW_SLOW ) ? " slow " : " fast " ,
( clkslow & S3C2410_CLKSLOW_MPLL_OFF ) ? " off " : " on " ,
( clkslow & S3C2410_CLKSLOW_UCLK_OFF ) ? " off " : " on " ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}