2007-02-11 20:31:01 +03:00
/* linux/arch/arm/plat-s3c24xx/cpu.c
2005-04-17 02:20:36 +04:00
*
* Copyright ( c ) 2004 - 2005 Simtec Electronics
* http : //www.simtec.co.uk/products/SWLINUX/
* Ben Dooks < ben @ simtec . co . uk >
*
2012-05-12 11:22:17 +04:00
* Common code for S3C24XX machines
2005-04-17 02:20:36 +04: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/interrupt.h>
# include <linux/ioport.h>
2006-12-18 01:22:26 +03:00
# include <linux/serial_core.h>
2005-10-29 22:07:23 +04:00
# include <linux/platform_device.h>
2008-04-16 03:15:20 +04:00
# include <linux/delay.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2005-04-17 02:20:36 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2011-08-03 19:34:59 +04:00
# include <mach/regs-clock.h>
2005-04-17 02:20:36 +04:00
# include <asm/irq.h>
2008-04-16 03:15:20 +04:00
# include <asm/cacheflush.h>
2012-03-28 21:30:01 +04:00
# include <asm/system_info.h>
2012-03-30 10:22:44 +04:00
# include <asm/system_misc.h>
2005-04-17 02:20:36 +04:00
# include <asm/mach/arch.h>
# include <asm/mach/map.h>
2012-05-12 11:22:18 +04:00
# include <mach/regs-clock.h>
2008-08-05 19:14:15 +04:00
# include <mach/regs-gpio.h>
2008-10-08 01:26:09 +04:00
# include <plat/regs-serial.h>
2005-04-17 02:20:36 +04:00
2008-10-08 01:26:09 +04:00
# include <plat/cpu.h>
# include <plat/devs.h>
2008-10-08 02:09:51 +04:00
# include <plat/clock.h>
2008-10-08 01:26:09 +04:00
# include <plat/s3c2410.h>
2008-10-08 02:09:51 +04:00
# include <plat/s3c2412.h>
2010-04-28 13:09:01 +04:00
# include <plat/s3c2416.h>
2010-01-26 10:47:41 +03:00
# include <plat/s3c244x.h>
2008-10-08 01:26:09 +04:00
# include <plat/s3c2443.h>
2012-05-12 11:22:18 +04:00
# include <plat/cpu-freq.h>
# include <plat/pll.h>
2005-04-17 02:20:36 +04:00
/* table of supported CPUs */
static const char name_s3c2410 [ ] = " S3C2410 " ;
2006-06-25 00:21:27 +04:00
static const char name_s3c2412 [ ] = " S3C2412 " ;
2010-04-30 11:32:26 +04:00
static const char name_s3c2416 [ ] = " S3C2416/S3C2450 " ;
2005-04-17 02:20:36 +04:00
static const char name_s3c2440 [ ] = " S3C2440 " ;
2006-06-19 02:06:41 +04:00
static const char name_s3c2442 [ ] = " S3C2442 " ;
2009-09-23 00:40:39 +04:00
static const char name_s3c2442b [ ] = " S3C2442B " ;
2007-02-16 14:12:31 +03:00
static const char name_s3c2443 [ ] = " S3C2443 " ;
2005-04-17 02:20:36 +04:00
static const char name_s3c2410a [ ] = " S3C2410A " ;
static const char name_s3c2440a [ ] = " S3C2440A " ;
static struct cpu_table cpu_ids [ ] __initdata = {
{
. idcode = 0x32410000 ,
. idmask = 0xffffffff ,
. map_io = s3c2410_map_io ,
. init_clocks = s3c2410_init_clocks ,
. init_uarts = s3c2410_init_uarts ,
. init = s3c2410_init ,
. name = name_s3c2410
} ,
{
. idcode = 0x32410002 ,
. idmask = 0xffffffff ,
. map_io = s3c2410_map_io ,
. init_clocks = s3c2410_init_clocks ,
. init_uarts = s3c2410_init_uarts ,
2009-07-31 02:23:38 +04:00
. init = s3c2410a_init ,
2005-04-17 02:20:36 +04:00
. name = name_s3c2410a
} ,
{
. idcode = 0x32440000 ,
. idmask = 0xffffffff ,
2010-12-01 09:29:23 +03:00
. map_io = s3c2440_map_io ,
2006-06-19 02:06:41 +04:00
. init_clocks = s3c244x_init_clocks ,
. init_uarts = s3c244x_init_uarts ,
2005-04-17 02:20:36 +04:00
. init = s3c2440_init ,
. name = name_s3c2440
} ,
{
. idcode = 0x32440001 ,
. idmask = 0xffffffff ,
2010-12-01 09:29:23 +03:00
. map_io = s3c2440_map_io ,
2006-06-19 02:06:41 +04:00
. init_clocks = s3c244x_init_clocks ,
. init_uarts = s3c244x_init_uarts ,
2005-04-17 02:20:36 +04:00
. init = s3c2440_init ,
. name = name_s3c2440a
2006-02-02 00:24:24 +03:00
} ,
2006-06-19 02:06:41 +04:00
{
. idcode = 0x32440aaa ,
. idmask = 0xffffffff ,
2010-12-01 09:29:23 +03:00
. map_io = s3c2442_map_io ,
2006-06-19 02:06:41 +04:00
. init_clocks = s3c244x_init_clocks ,
. init_uarts = s3c244x_init_uarts ,
. init = s3c2442_init ,
. name = name_s3c2442
} ,
2009-09-23 00:40:39 +04:00
{
. idcode = 0x32440aab ,
. idmask = 0xffffffff ,
2010-12-01 09:29:23 +03:00
. map_io = s3c2442_map_io ,
2009-09-23 00:40:39 +04:00
. init_clocks = s3c244x_init_clocks ,
. init_uarts = s3c244x_init_uarts ,
. init = s3c2442_init ,
. name = name_s3c2442b
} ,
2006-06-25 00:21:27 +04:00
{
. idcode = 0x32412001 ,
. idmask = 0xffffffff ,
. map_io = s3c2412_map_io ,
. init_clocks = s3c2412_init_clocks ,
. init_uarts = s3c2412_init_uarts ,
. init = s3c2412_init ,
. name = name_s3c2412 ,
} ,
2006-09-20 23:39:15 +04:00
{ /* a newer version of the s3c2412 */
. idcode = 0x32412003 ,
. idmask = 0xffffffff ,
. map_io = s3c2412_map_io ,
. init_clocks = s3c2412_init_clocks ,
. init_uarts = s3c2412_init_uarts ,
. init = s3c2412_init ,
. name = name_s3c2412 ,
} ,
2010-04-28 13:09:01 +04:00
{ /* a strange version of the s3c2416 */
. idcode = 0x32450003 ,
. idmask = 0xffffffff ,
. map_io = s3c2416_map_io ,
. init_clocks = s3c2416_init_clocks ,
. init_uarts = s3c2416_init_uarts ,
. init = s3c2416_init ,
. name = name_s3c2416 ,
} ,
2007-02-16 14:12:31 +03:00
{
. idcode = 0x32443001 ,
. idmask = 0xffffffff ,
. map_io = s3c2443_map_io ,
. init_clocks = s3c2443_init_clocks ,
. init_uarts = s3c2443_init_uarts ,
. init = s3c2443_init ,
. name = name_s3c2443 ,
} ,
2005-04-17 02:20:36 +04:00
} ;
/* minimal IO mapping */
static struct map_desc s3c_iodesc [ ] __initdata = {
IODESC_ENT ( GPIO ) ,
IODESC_ENT ( IRQ ) ,
IODESC_ENT ( MEMCTRL ) ,
IODESC_ENT ( UART )
} ;
2008-10-21 17:06:31 +04:00
/* read cpu identificaiton code */
2005-04-17 02:20:36 +04:00
2006-06-25 00:21:27 +04:00
static unsigned long s3c24xx_read_idcode_v5 ( void )
{
2010-04-28 13:00:07 +04:00
# if defined(CONFIG_CPU_S3C2416)
/* s3c2416 is v5, with S3C24XX_GSTATUS1 instead of S3C2412_GSTATUS1 */
u32 gs = __raw_readl ( S3C24XX_GSTATUS1 ) ;
/* test for s3c2416 or similar device */
if ( ( gs > > 16 ) = = 0x3245 )
return gs ;
# endif
2006-06-25 00:21:27 +04:00
# if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
return __raw_readl ( S3C2412_GSTATUS1 ) ;
# else
return 1UL ; /* don't look like an 2400 */
# endif
}
static unsigned long s3c24xx_read_idcode_v4 ( void )
{
return __raw_readl ( S3C2410_GSTATUS1 ) ;
}
2011-08-03 19:34:59 +04:00
static void s3c24xx_default_idle ( void )
{
unsigned long tmp ;
int i ;
/* idle the system by using the idle mode which will wait for an
* interrupt to happen before restarting the system .
*/
/* Warning: going into idle state upsets jtag scanning */
__raw_writel ( __raw_readl ( S3C2410_CLKCON ) | S3C2410_CLKCON_IDLE ,
S3C2410_CLKCON ) ;
/* the samsung port seems to do a loop and then unset idle.. */
for ( i = 0 ; i < 50 ; i + + )
tmp + = __raw_readl ( S3C2410_CLKCON ) ; /* ensure loop not optimised out */
/* this bit is not cleared on re-start... */
__raw_writel ( __raw_readl ( S3C2410_CLKCON ) & ~ S3C2410_CLKCON_IDLE ,
S3C2410_CLKCON ) ;
}
2005-04-17 02:20:36 +04:00
void __init s3c24xx_init_io ( struct map_desc * mach_desc , int size )
{
2011-08-03 19:34:59 +04:00
arm_pm_idle = s3c24xx_default_idle ;
2005-04-17 02:20:36 +04:00
/* initialise the io descriptors we need for initialisation */
2008-10-21 17:06:31 +04:00
iotable_init ( mach_desc , size ) ;
2005-04-17 02:20:36 +04:00
iotable_init ( s3c_iodesc , ARRAY_SIZE ( s3c_iodesc ) ) ;
2006-06-25 00:21:27 +04:00
if ( cpu_architecture ( ) > = CPU_ARCH_ARMv5 ) {
2011-08-19 21:18:18 +04:00
samsung_cpu_id = s3c24xx_read_idcode_v5 ( ) ;
2006-06-25 00:21:27 +04:00
} else {
2011-08-19 21:18:18 +04:00
samsung_cpu_id = s3c24xx_read_idcode_v4 ( ) ;
2006-06-25 00:21:27 +04:00
}
2011-08-20 07:18:07 +04:00
s3c24xx_init_cpu ( ) ;
2006-02-02 00:24:24 +03:00
2011-08-19 21:18:18 +04:00
s3c_init_cpu ( samsung_cpu_id , cpu_ids , ARRAY_SIZE ( cpu_ids ) ) ;
2006-06-19 02:04:05 +04:00
}
2012-05-12 11:22:17 +04:00
/* Serial port registrations */
static struct resource s3c2410_uart0_resource [ ] = {
[ 0 ] = {
. start = S3C2410_PA_UART0 ,
. end = S3C2410_PA_UART0 + 0x3fff ,
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. start = IRQ_S3CUART_RX0 ,
. end = IRQ_S3CUART_ERR0 ,
. flags = IORESOURCE_IRQ ,
}
} ;
static struct resource s3c2410_uart1_resource [ ] = {
[ 0 ] = {
. start = S3C2410_PA_UART1 ,
. end = S3C2410_PA_UART1 + 0x3fff ,
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. start = IRQ_S3CUART_RX1 ,
. end = IRQ_S3CUART_ERR1 ,
. flags = IORESOURCE_IRQ ,
}
} ;
static struct resource s3c2410_uart2_resource [ ] = {
[ 0 ] = {
. start = S3C2410_PA_UART2 ,
. end = S3C2410_PA_UART2 + 0x3fff ,
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. start = IRQ_S3CUART_RX2 ,
. end = IRQ_S3CUART_ERR2 ,
. flags = IORESOURCE_IRQ ,
}
} ;
static struct resource s3c2410_uart3_resource [ ] = {
[ 0 ] = {
. start = S3C2443_PA_UART3 ,
. end = S3C2443_PA_UART3 + 0x3fff ,
. flags = IORESOURCE_MEM ,
} ,
[ 1 ] = {
. start = IRQ_S3CUART_RX3 ,
. end = IRQ_S3CUART_ERR3 ,
. flags = IORESOURCE_IRQ ,
} ,
} ;
struct s3c24xx_uart_resources s3c2410_uart_resources [ ] __initdata = {
[ 0 ] = {
. resources = s3c2410_uart0_resource ,
. nr_resources = ARRAY_SIZE ( s3c2410_uart0_resource ) ,
} ,
[ 1 ] = {
. resources = s3c2410_uart1_resource ,
. nr_resources = ARRAY_SIZE ( s3c2410_uart1_resource ) ,
} ,
[ 2 ] = {
. resources = s3c2410_uart2_resource ,
. nr_resources = ARRAY_SIZE ( s3c2410_uart2_resource ) ,
} ,
[ 3 ] = {
. resources = s3c2410_uart3_resource ,
. nr_resources = ARRAY_SIZE ( s3c2410_uart3_resource ) ,
} ,
} ;
2012-05-12 11:22:18 +04:00
/* initialise all the clocks */
void __init_or_cpufreq s3c24xx_setup_clocks ( unsigned long fclk ,
unsigned long hclk ,
unsigned long pclk )
{
clk_upll . rate = s3c24xx_get_pll ( __raw_readl ( S3C2410_UPLLCON ) ,
clk_xtal . rate ) ;
clk_mpll . rate = fclk ;
clk_h . rate = hclk ;
clk_p . rate = pclk ;
clk_f . rate = fclk ;
}