瀏覽代碼

Merge S3C24xx branch

Russell King 19 年之前
父節點
當前提交
e11c910b5b

+ 20 - 0
arch/arm/mach-s3c2410/Kconfig

@@ -81,6 +81,12 @@ config SMDK2440_CPU2442
 	depends on ARCH_S3C2440
 	select CPU_S3C2442
 
+config MACH_SMDK2413
+	bool "SMDK2413"
+	select CPU_S3C2412
+	select MACH_SMDK
+	help
+	  Say Y here if you are using an SMDK2413
 
 config MACH_VR1000
 	bool "Thorcom VR1000"
@@ -127,6 +133,20 @@ config CPU_S3C2410
 	  Support for S3C2410 and S3C2410A family from the S3C24XX line
 	  of Samsung Mobile CPUs.
 
+# internal node to signify if we are only dealing with an S3C2412
+
+config CPU_S3C2412_ONLY
+	bool
+	depends on ARCH_S3C2410 && !CPU_S3C2400 && !CPU_S3C2410 && \
+		   !CPU_S3C2440 && !CPU_S3C2442 && CPU_S3C2412
+	default y if CPU_S3C2412
+
+config CPU_S3C2412
+	bool
+	depends on ARCH_S3C2410
+	help
+	  Support for the S3C2412 and S3C2413 SoCs from the S3C24XX line
+
 config CPU_S3C244X
 	bool
 	depends on ARCH_S3C2410 && (CPU_S3C2440 || CPU_S3C2442)

+ 6 - 0
arch/arm/mach-s3c2410/Makefile

@@ -24,6 +24,11 @@ obj-$(CONFIG_S3C2410_DMA)  += dma.o
 obj-$(CONFIG_PM)	   += pm.o sleep.o
 obj-$(CONFIG_PM_SIMTEC)	   += pm-simtec.o
 
+# S3C2412 support
+obj-$(CONFIG_CPU_S3C2412)  += s3c2412.o
+obj-$(CONFIG_CPU_S3C2412)  += s3c2412-clock.o
+
+#
 # S3C244X support
 
 obj-$(CONFIG_CPU_S3C244X)  += s3c244x.o
@@ -57,6 +62,7 @@ obj-$(CONFIG_ARCH_BAST)		+= mach-bast.o usb-simtec.o
 obj-$(CONFIG_ARCH_H1940)	+= mach-h1940.o
 obj-$(CONFIG_MACH_N30)		+= mach-n30.o
 obj-$(CONFIG_ARCH_SMDK2410)	+= mach-smdk2410.o
+obj-$(CONFIG_MACH_SMDK2413)	+= mach-smdk2413.o
 obj-$(CONFIG_ARCH_S3C2440)	+= mach-smdk2440.o
 obj-$(CONFIG_MACH_VR1000)	+= mach-vr1000.o usb-simtec.o
 obj-$(CONFIG_MACH_RX3715)	+= mach-rx3715.o

+ 15 - 6
arch/arm/mach-s3c2410/clock.c

@@ -213,7 +213,7 @@ EXPORT_SYMBOL(clk_set_parent);
 
 /* base clocks */
 
-static struct clk clk_xtal = {
+struct clk clk_xtal = {
 	.name		= "xtal",
 	.id		= -1,
 	.rate		= 0,
@@ -221,6 +221,11 @@ static struct clk clk_xtal = {
 	.ctrlbit	= 0,
 };
 
+struct clk clk_mpll = {
+	.name		= "mpll",
+	.id		= -1,
+};
+
 struct clk clk_upll = {
 	.name		= "upll",
 	.id		= -1,
@@ -232,7 +237,7 @@ struct clk clk_f = {
 	.name		= "fclk",
 	.id		= -1,
 	.rate		= 0,
-	.parent		= NULL,
+	.parent		= &clk_mpll,
 	.ctrlbit	= 0,
 };
 
@@ -263,14 +268,14 @@ struct clk clk_usb_bus = {
 
 static int s3c24xx_dclk_enable(struct clk *clk, int enable)
 {
-	unsigned long dclkcon = __raw_readl(S3C2410_DCLKCON);
+	unsigned long dclkcon = __raw_readl(S3C24XX_DCLKCON);
 
 	if (enable)
 		dclkcon |= clk->ctrlbit;
 	else
 		dclkcon &= ~clk->ctrlbit;
 
-	__raw_writel(dclkcon, S3C2410_DCLKCON);
+	__raw_writel(dclkcon, S3C24XX_DCLKCON);
 
 	return 0;
 }
@@ -289,7 +294,7 @@ static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
 
 	clk->parent = parent;
 
-	dclkcon = __raw_readl(S3C2410_DCLKCON);
+	dclkcon = __raw_readl(S3C24XX_DCLKCON);
 
 	if (clk->ctrlbit == S3C2410_DCLKCON_DCLK0EN) {
 		if (uclk)
@@ -303,7 +308,7 @@ static int s3c24xx_dclk_setparent(struct clk *clk, struct clk *parent)
 			dclkcon &= ~S3C2410_DCLKCON_DCLK1_UCLK;
 	}
 
-	__raw_writel(dclkcon, S3C2410_DCLKCON);
+	__raw_writel(dclkcon, S3C24XX_DCLKCON);
 
 	return 0;
 }
@@ -413,6 +418,7 @@ int __init s3c24xx_setup_clocks(unsigned long xtal,
 	clk_xtal.rate = xtal;
 	clk_upll.rate = s3c2410_get_pll(__raw_readl(S3C2410_UPLLCON), xtal);
 
+	clk_mpll.rate = fclk;
 	clk_h.rate = hclk;
 	clk_p.rate = pclk;
 	clk_f.rate = fclk;
@@ -424,6 +430,9 @@ int __init s3c24xx_setup_clocks(unsigned long xtal,
 	if (s3c24xx_register_clock(&clk_xtal) < 0)
 		printk(KERN_ERR "failed to register master xtal\n");
 
+	if (s3c24xx_register_clock(&clk_mpll) < 0)
+		printk(KERN_ERR "failed to register mpll clock\n");
+
 	if (s3c24xx_register_clock(&clk_upll) < 0)
 		printk(KERN_ERR "failed to register upll clock\n");
 

+ 2 - 0
arch/arm/mach-s3c2410/clock.h

@@ -42,7 +42,9 @@ extern struct clk clk_usb_bus;
 extern struct clk clk_f;
 extern struct clk clk_h;
 extern struct clk clk_p;
+extern struct clk clk_mpll;
 extern struct clk clk_upll;
+extern struct clk clk_xtal;
 
 /* exports for arch/arm/mach-s3c2410
  *

+ 34 - 3
arch/arm/mach-s3c2410/cpu.c

@@ -44,6 +44,7 @@
 #include "clock.h"
 #include "s3c2400.h"
 #include "s3c2410.h"
+#include "s3c2412.h"
 #include "s3c244x.h"
 #include "s3c2440.h"
 #include "s3c2442.h"
@@ -62,6 +63,7 @@ struct cpu_table {
 
 static const char name_s3c2400[]  = "S3C2400";
 static const char name_s3c2410[]  = "S3C2410";
+static const char name_s3c2412[]  = "S3C2412";
 static const char name_s3c2440[]  = "S3C2440";
 static const char name_s3c2442[]  = "S3C2442";
 static const char name_s3c2410a[] = "S3C2410A";
@@ -113,6 +115,15 @@ static struct cpu_table cpu_ids[] __initdata = {
 		.init		= s3c2442_init,
 		.name		= name_s3c2442
 	},
+	{
+		.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,
+	},
 	{
 		.idcode		= 0x0,   /* S3C2400 doesn't have an idcode */
 		.idmask		= 0xffffffff,
@@ -171,6 +182,24 @@ void s3c24xx_set_board(struct s3c24xx_board *b)
 
 static struct cpu_table *cpu;
 
+static unsigned long s3c24xx_read_idcode_v5(void)
+{
+#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)
+{
+#ifndef CONFIG_CPU_S3C2400
+	return __raw_readl(S3C2410_GSTATUS1);
+#else
+	return 0UL;
+#endif
+}
+
 void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
 {
 	unsigned long idcode = 0x0;
@@ -178,9 +207,11 @@ void __init s3c24xx_init_io(struct map_desc *mach_desc, int size)
 	/* initialise the io descriptors we need for initialisation */
 	iotable_init(s3c_iodesc, ARRAY_SIZE(s3c_iodesc));
 
-#ifndef CONFIG_CPU_S3C2400
-	idcode = __raw_readl(S3C2410_GSTATUS1);
-#endif
+	if (cpu_architecture() >= CPU_ARCH_ARMv5) {
+		idcode = s3c24xx_read_idcode_v5();
+	} else {
+		idcode = s3c24xx_read_idcode_v4();
+	}
 
 	cpu = s3c_lookup_cpu(idcode);
 

+ 1 - 0
arch/arm/mach-s3c2410/cpu.h

@@ -74,5 +74,6 @@ extern struct sys_timer s3c24xx_timer;
 /* system device classes */
 
 extern struct sysdev_class s3c2410_sysclass;
+extern struct sysdev_class s3c2412_sysclass;
 extern struct sysdev_class s3c2440_sysclass;
 extern struct sysdev_class s3c2442_sysclass;

+ 37 - 20
arch/arm/mach-s3c2410/irq.c

@@ -191,13 +191,9 @@ static struct irqchip s3c_irq_chip = {
 	.ack	   = s3c_irq_ack,
 	.mask	   = s3c_irq_mask,
 	.unmask	   = s3c_irq_unmask,
-	.set_wake	   = s3c_irq_wake
+	.set_wake  = s3c_irq_wake
 };
 
-/* S3C2410_EINTMASK
- * S3C2410_EINTPEND
- */
-
 static void
 s3c_irqext_mask(unsigned int irqno)
 {
@@ -205,9 +201,9 @@ s3c_irqext_mask(unsigned int irqno)
 
 	irqno -= EXTINT_OFF;
 
-	mask = __raw_readl(S3C2410_EINTMASK);
+	mask = __raw_readl(S3C24XX_EINTMASK);
 	mask |= ( 1UL << irqno);
-	__raw_writel(mask, S3C2410_EINTMASK);
+	__raw_writel(mask, S3C24XX_EINTMASK);
 
 	if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) {
 		/* check to see if all need masking */
@@ -232,11 +228,11 @@ s3c_irqext_ack(unsigned int irqno)
 	bit = 1UL << (irqno - EXTINT_OFF);
 
 
-	mask = __raw_readl(S3C2410_EINTMASK);
+	mask = __raw_readl(S3C24XX_EINTMASK);
 
-	__raw_writel(bit, S3C2410_EINTPEND);
+	__raw_writel(bit, S3C24XX_EINTPEND);
 
-	req = __raw_readl(S3C2410_EINTPEND);
+	req = __raw_readl(S3C24XX_EINTPEND);
 	req &= ~mask;
 
 	/* not sure if we should be acking the parent irq... */
@@ -257,9 +253,9 @@ s3c_irqext_unmask(unsigned int irqno)
 
 	irqno -= EXTINT_OFF;
 
-	mask = __raw_readl(S3C2410_EINTMASK);
+	mask = __raw_readl(S3C24XX_EINTMASK);
 	mask &= ~( 1UL << irqno);
-	__raw_writel(mask, S3C2410_EINTMASK);
+	__raw_writel(mask, S3C24XX_EINTMASK);
 
 	s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23);
 }
@@ -275,28 +271,28 @@ s3c_irqext_type(unsigned int irq, unsigned int type)
 	if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3))
 	{
 		gpcon_reg = S3C2410_GPFCON;
-		extint_reg = S3C2410_EXTINT0;
+		extint_reg = S3C24XX_EXTINT0;
 		gpcon_offset = (irq - IRQ_EINT0) * 2;
 		extint_offset = (irq - IRQ_EINT0) * 4;
 	}
 	else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7))
 	{
 		gpcon_reg = S3C2410_GPFCON;
-		extint_reg = S3C2410_EXTINT0;
+		extint_reg = S3C24XX_EXTINT0;
 		gpcon_offset = (irq - (EXTINT_OFF)) * 2;
 		extint_offset = (irq - (EXTINT_OFF)) * 4;
 	}
 	else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15))
 	{
 		gpcon_reg = S3C2410_GPGCON;
-		extint_reg = S3C2410_EXTINT1;
+		extint_reg = S3C24XX_EXTINT1;
 		gpcon_offset = (irq - IRQ_EINT8) * 2;
 		extint_offset = (irq - IRQ_EINT8) * 4;
 	}
 	else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))
 	{
 		gpcon_reg = S3C2410_GPGCON;
-		extint_reg = S3C2410_EXTINT2;
+		extint_reg = S3C24XX_EXTINT2;
 		gpcon_offset = (irq - IRQ_EINT8) * 2;
 		extint_offset = (irq - IRQ_EINT16) * 4;
 	} else
@@ -572,6 +568,23 @@ s3c_irq_demux_uart2(unsigned int irq,
 	s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs);
 }
 
+static void
+s3c_irq_demux_extint(unsigned int irq,
+		     struct irqdesc *desc,
+		     struct pt_regs *regs)
+{
+	unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
+	unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);
+
+	eintpnd &= ~eintmsk;
+
+	if (eintpnd) {
+		irq = fls(eintpnd);
+		irq += (IRQ_EINT4 - (4 + 1));
+
+		desc_handle_irq(irq, irq_desc + irq, regs);
+	}
+}
 
 /* s3c24xx_init_irq
  *
@@ -591,12 +604,12 @@ void __init s3c24xx_init_irq(void)
 
 	last = 0;
 	for (i = 0; i < 4; i++) {
-		pend = __raw_readl(S3C2410_EINTPEND);
+		pend = __raw_readl(S3C24XX_EINTPEND);
 
 		if (pend == 0 || pend == last)
 			break;
 
-		__raw_writel(pend, S3C2410_EINTPEND);
+		__raw_writel(pend, S3C24XX_EINTPEND);
 		printk("irq: clearing pending ext status %08x\n", (int)pend);
 		last = pend;
 	}
@@ -630,12 +643,14 @@ void __init s3c24xx_init_irq(void)
 
 	irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");
 
-	for (irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) {
+	for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
 		/* set all the s3c2410 internal irqs */
 
 		switch (irqno) {
 			/* deal with the special IRQs (cascaded) */
 
+		case IRQ_EINT4t7:
+		case IRQ_EINT8t23:
 		case IRQ_UART0:
 		case IRQ_UART1:
 		case IRQ_UART2:
@@ -659,12 +674,14 @@ void __init s3c24xx_init_irq(void)
 
 	/* setup the cascade irq handlers */
 
+	set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint);
+	set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint);
+
 	set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
 	set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
 	set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
 	set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
 
-
 	/* external interrupts */
 
 	for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {

+ 126 - 0
arch/arm/mach-s3c2410/mach-smdk2413.c

@@ -0,0 +1,126 @@
+/* linux/arch/arm/mach-s3c2410/mach-smdk2413.c
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * Thanks to Dimity Andric (TomTom) and Steven Ryu (Samsung) for the
+ * loans of SMDK2413 to work with.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware.h>
+#include <asm/hardware/iomd.h>
+#include <asm/setup.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+//#include <asm/debug-ll.h>
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-lcd.h>
+
+#include <asm/arch/idle.h>
+#include <asm/arch/fb.h>
+
+#include "s3c2410.h"
+#include "s3c2412.h"
+#include "clock.h"
+#include "devs.h"
+#include "cpu.h"
+
+#include "common-smdk.h"
+
+static struct map_desc smdk2413_iodesc[] __initdata = {
+};
+
+static struct s3c2410_uartcfg smdk2413_uartcfgs[] __initdata = {
+	[0] = {
+		.hwport	     = 0,
+		.flags	     = 0,
+		.ucon	     = 0x3c5,
+		.ulcon	     = 0x03,
+		.ufcon	     = 0x51,
+	},
+	[1] = {
+		.hwport	     = 1,
+		.flags	     = 0,
+		.ucon	     = 0x3c5,
+		.ulcon	     = 0x03,
+		.ufcon	     = 0x51,
+	},
+	/* IR port */
+	[2] = {
+		.hwport	     = 2,
+		.flags	     = 0,
+		.ucon	     = 0x3c5,
+		.ulcon	     = 0x43,
+		.ufcon	     = 0x51,
+	}
+};
+
+static struct platform_device *smdk2413_devices[] __initdata = {
+	&s3c_device_usb,
+	//&s3c_device_lcd,
+	&s3c_device_wdt,
+	&s3c_device_i2c,
+	&s3c_device_iis,
+};
+
+static struct s3c24xx_board smdk2413_board __initdata = {
+	.devices       = smdk2413_devices,
+	.devices_count = ARRAY_SIZE(smdk2413_devices)
+};
+
+static void __init smdk2413_fixup(struct machine_desc *desc,
+				  struct tag *tags, char **cmdline,
+				  struct meminfo *mi)
+{
+	if (tags != phys_to_virt(S3C2410_SDRAM_PA + 0x100)) {
+		mi->nr_banks=1;
+		mi->bank[0].start = 0x30000000;
+		mi->bank[0].size = SZ_64M;
+		mi->bank[0].node = 0;
+	}
+}
+
+static void __init smdk2413_map_io(void)
+{
+	s3c24xx_init_io(smdk2413_iodesc, ARRAY_SIZE(smdk2413_iodesc));
+	s3c24xx_init_clocks(12000000);
+	s3c24xx_init_uarts(smdk2413_uartcfgs, ARRAY_SIZE(smdk2413_uartcfgs));
+	s3c24xx_set_board(&smdk2413_board);
+}
+
+static void __init smdk2413_machine_init(void)
+{
+	smdk_machine_init();
+}
+
+MACHINE_START(S3C2413, "SMDK2413")
+	/* Maintainer: Ben Dooks <ben@fluff.org> */
+	.phys_io	= S3C2410_PA_UART,
+	.io_pg_offst	= (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
+	.boot_params	= S3C2410_SDRAM_PA + 0x100,
+
+	.fixup		= smdk2413_fixup,
+	.init_irq	= s3c24xx_init_irq,
+	.map_io		= smdk2413_map_io,
+	.init_machine	= smdk2413_machine_init,
+	.timer		= &s3c24xx_timer,
+MACHINE_END

+ 2 - 1
arch/arm/mach-s3c2410/pm-simtec.c

@@ -48,7 +48,8 @@ static __init int pm_simtec_init(void)
 
 	/* check which machine we are running on */
 
-	if (!machine_is_bast() && !machine_is_vr1000() && !machine_is_anubis())
+	if (!machine_is_bast() && !machine_is_vr1000() &&
+	    !machine_is_anubis() && !machine_is_osiris())
 		return 0;
 
 	printk(KERN_INFO "Simtec Board Power Manangement" COPYRIGHT "\n");

+ 9 - 1
arch/arm/mach-s3c2410/s3c2410-clock.c

@@ -182,7 +182,15 @@ static struct clk init_clocks[] = {
 		.id		= -1,
 		.parent		= &clk_p,
 		.ctrlbit	= 0,
-	}
+	}, {
+		.name		= "usb-bus-host",
+		.id		= -1,
+		.parent		= &clk_usb_bus,
+	}, {
+		.name		= "usb-bus-gadget",
+		.id		= -1,
+		.parent		= &clk_usb_bus,
+	},
 };
 
 /* s3c2410_baseclk_add()

+ 5 - 8
arch/arm/mach-s3c2410/s3c2410-gpio.c

@@ -18,9 +18,6 @@
  * 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
- *
- * Changelog
- *	15-Jan-2006  LCVR  Splitted from gpio.c
  */
 
 #include <linux/kernel.h>
@@ -38,7 +35,7 @@
 int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on,
 			   unsigned int config)
 {
-	void __iomem *reg = S3C2410_EINFLT0;
+	void __iomem *reg = S3C24XX_EINFLT0;
 	unsigned long flags;
 	unsigned long val;
 
@@ -47,7 +44,7 @@ int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on,
 
 	config &= 0xff;
 
-	pin -= S3C2410_GPG8_EINT16;
+	pin -= S3C2410_GPG8;
 	reg += pin & ~3;
 
 	local_irq_save(flags);
@@ -61,10 +58,10 @@ int s3c2410_gpio_irqfilter(unsigned int pin, unsigned int on,
 
 	/* update filter enable */
 
-	val = __raw_readl(S3C2410_EXTINT2);
+	val = __raw_readl(S3C24XX_EXTINT2);
 	val &= ~(1 << ((pin * 4) + 3));
 	val |= on << ((pin * 4) + 3);
-	__raw_writel(val, S3C2410_EXTINT2);
+	__raw_writel(val, S3C24XX_EXTINT2);
 
 	local_irq_restore(flags);
 
@@ -75,7 +72,7 @@ EXPORT_SYMBOL(s3c2410_gpio_irqfilter);
 
 int s3c2410_gpio_getirq(unsigned int pin)
 {
-	if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15_EINT23)
+	if (pin < S3C2410_GPF0 || pin > S3C2410_GPG15)
 		return -1;	/* not valid interrupts */
 
 	if (pin < S3C2410_GPG0 && pin > S3C2410_GPF7)

+ 711 - 0
arch/arm/mach-s3c2410/s3c2412-clock.c

@@ -0,0 +1,711 @@
+/* linux/arch/arm/mach-s3c2410/s3c2412-clock.c
+ *
+ * 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>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-gpio.h>
+
+#include "clock.h"
+#include "cpu.h"
+
+/* 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
+*/
+
+int s3c2412_clkcon_enable(struct clk *clk, int enable)
+{
+	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 */
+
+/* CPU EXTCLK input */
+static struct clk clk_ext = {
+	.name		= "extclk",
+	.id		= -1,
+};
+
+static struct clk clk_erefclk = {
+	.name		= "erefclk",
+	.id		= -1,
+};
+
+static struct clk clk_urefclk = {
+	.name		= "urefclk",
+	.id		= -1,
+};
+
+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",
+	.id		= -1,
+	.parent		= &clk_xtal,
+	.set_parent	= s3c2412_setparent_usysclk,
+};
+
+static struct clk clk_mrefclk = {
+	.name		= "mrefclk",
+	.parent		= &clk_xtal,
+	.id		= -1,
+};
+
+static struct clk clk_mdivclk = {
+	.name		= "mdivclk",
+	.parent		= &clk_xtal,
+	.id		= -1,
+};
+
+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",
+	.id		= -1,
+	.get_rate	= s3c2412_getrate_usbsrc,
+	.set_rate	= s3c2412_setrate_usbsrc,
+	.round_rate	= s3c2412_roundrate_usbsrc,
+	.set_parent	= s3c2412_setparent_usbsrc,
+};
+
+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;
+	else if (parent == &clk_upll)
+		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",
+	.id		= -1,
+	.set_parent	= s3c2412_setparent_msysclk,
+};
+
+/* 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",
+	.id		= -1,
+	.get_rate	= s3c2412_getrate_uart,
+	.set_rate	= s3c2412_setrate_uart,
+	.set_parent	= s3c2412_setparent_uart,
+	.round_rate	= s3c2412_roundrate_clksrc,
+};
+
+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",
+	.id		= -1,
+	.get_rate	= s3c2412_getrate_i2s,
+	.set_rate	= s3c2412_setrate_i2s,
+	.set_parent	= s3c2412_setparent_i2s,
+	.round_rate	= s3c2412_roundrate_clksrc,
+};
+
+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 */
+	.id		= -1,
+	.get_rate	= s3c2412_getrate_cam,
+	.set_rate	= s3c2412_setrate_cam,
+	.set_parent	= s3c2412_setparent_cam,
+	.round_rate	= s3c2412_roundrate_clksrc,
+};
+
+/* standard clock definitions */
+
+static struct clk init_clocks_disable[] = {
+	{
+		.name		= "nand",
+		.id		= -1,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_NAND,
+	}, {
+		.name		= "sdi",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_SDI,
+	}, {
+		.name		= "adc",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_ADC,
+	}, {
+		.name		= "i2c",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_IIC,
+	}, {
+		.name		= "iis",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_IIS,
+	}, {
+		.name		= "spi",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_SPI,
+	}
+};
+
+static struct clk init_clocks[] = {
+	{
+		.name		= "dma",
+		.id		= 0,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_DMA0,
+	}, {
+		.name		= "dma",
+		.id		= 1,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_DMA1,
+	}, {
+		.name		= "dma",
+		.id		= 2,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_DMA2,
+	}, {
+		.name		= "dma",
+		.id		= 3,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_DMA3,
+	}, {
+		.name		= "lcd",
+		.id		= -1,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_LCDC,
+	}, {
+		.name		= "gpio",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_GPIO,
+	}, {
+		.name		= "usb-host",
+		.id		= -1,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_USBH,
+	}, {
+		.name		= "usb-device",
+		.id		= -1,
+		.parent		= &clk_h,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_USBD,
+	}, {
+		.name		= "timers",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_PWMT,
+	}, {
+		.name		= "uart",
+		.id		= 0,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_UART0,
+	}, {
+		.name		= "uart",
+		.id		= 1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_UART1,
+	}, {
+		.name		= "uart",
+		.id		= 2,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_UART2,
+	}, {
+		.name		= "rtc",
+		.id		= -1,
+		.parent		= &clk_p,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_RTC,
+	}, {
+		.name		= "watchdog",
+		.id		= -1,
+		.parent		= &clk_p,
+		.ctrlbit	= 0,
+	}, {
+		.name		= "usb-bus-gadget",
+		.id		= -1,
+		.parent		= &clk_usb_bus,
+		.enable		= s3c2412_clkcon_enable,
+		.ctrlbit	= S3C2412_CLKCON_USB_DEV48,
+	}, {
+		.name		= "usb-bus-host",
+		.id		= -1,
+		.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;
+};
+
+struct clk_init clks_src[] __initdata = {
+	{
+		.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,
+	},
+};
+
+/* 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 */
+
+struct clk *clks[] __initdata = {
+	&clk_ext,
+	&clk_usb_bus,
+	&clk_erefclk,
+	&clk_urefclk,
+	&clk_mrefclk,
+};
+
+int __init s3c2412_baseclk_add(void)
+{
+	unsigned long clkcon  = __raw_readl(S3C2410_CLKCON);
+	struct clk *clkp;
+	int ret;
+	int ptr;
+
+	clk_upll.enable = s3c2412_upll_enable;
+	clk_usb_bus.parent = &clk_usbsrc;
+	clk_usb_bus.rate = 0x0;
+
+	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);
+		}
+	}
+
+	/* 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 48MHz */
+
+		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
+	 * be using at boot time, as subsytems such as the LCD which do
+	 * 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);
+	}
+
+	return 0;
+}

+ 195 - 0
arch/arm/mach-s3c2410/s3c2412.c

@@ -0,0 +1,195 @@
+/* linux/arch/arm/mach-s3c2410/s3c2412.c
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ *	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.
+ *
+ * Modifications:
+ *     16-May-2003 BJD  Created initial version
+ *     16-Aug-2003 BJD  Fixed header files and copyright, added URL
+ *     05-Sep-2003 BJD  Moved to kernel v2.6
+ *     18-Jan-2004 BJD  Added serial port configuration
+ *     21-Aug-2004 BJD  Added new struct s3c2410_board handler
+ *     28-Sep-2004 BJD  Updates for new serial port bits
+ *     04-Nov-2004 BJD  Updated UART configuration process
+ *     10-Jan-2005 BJD  Removed s3c2410_clock_tick_rate
+ *     13-Aug-2005 DA   Removed UART from initial I/O mappings
+*/
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/init.h>
+#include <linux/sysdev.h>
+#include <linux/platform_device.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#include <asm/arch/regs-clock.h>
+#include <asm/arch/regs-serial.h>
+#include <asm/arch/regs-gpio.h>
+#include <asm/arch/regs-gpioj.h>
+#include <asm/arch/regs-dsc.h>
+
+#include "s3c2412.h"
+#include "cpu.h"
+#include "devs.h"
+#include "clock.h"
+#include "pm.h"
+
+#ifndef CONFIG_CPU_S3C2412_ONLY
+void __iomem *s3c24xx_va_gpio2 = S3C24XX_VA_GPIO;
+#endif
+
+/* Initial IO mappings */
+
+static struct map_desc s3c2412_iodesc[] __initdata = {
+	IODESC_ENT(CLKPWR),
+	IODESC_ENT(LCD),
+	IODESC_ENT(TIMER),
+	IODESC_ENT(ADC),
+	IODESC_ENT(WATCHDOG),
+};
+
+/* uart registration process */
+
+void __init s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no)
+{
+	s3c24xx_init_uartdevs("s3c2412-uart", s3c2410_uart_resources, cfg, no);
+
+	/* rename devices that are s3c2412/s3c2413 specific */
+	s3c_device_sdi.name  = "s3c2412-sdi";
+	s3c_device_nand.name = "s3c2412-nand";
+}
+
+/* s3c2412_map_io
+ *
+ * register the standard cpu IO areas, and any passed in from the
+ * machine specific initialisation.
+*/
+
+void __init s3c2412_map_io(struct map_desc *mach_desc, int mach_size)
+{
+	/* move base of IO */
+
+	s3c24xx_va_gpio2 = S3C24XX_VA_GPIO + 0x10;
+
+	/* register our io-tables */
+
+	iotable_init(s3c2412_iodesc, ARRAY_SIZE(s3c2412_iodesc));
+	iotable_init(mach_desc, mach_size);
+}
+
+void __init s3c2412_init_clocks(int xtal)
+{
+	unsigned long tmp;
+	unsigned long fclk;
+	unsigned long hclk;
+	unsigned long pclk;
+
+	/* now we've got our machine bits initialised, work out what
+	 * clocks we've got */
+
+	fclk = s3c2410_get_pll(__raw_readl(S3C2410_MPLLCON), xtal*2);
+
+	tmp = __raw_readl(S3C2410_CLKDIVN);
+
+	/* work out clock scalings */
+
+	hclk = fclk / ((tmp & S3C2412_CLKDIVN_HDIVN_MASK) + 1);
+	hclk /= ((tmp & S3C2421_CLKDIVN_ARMDIVN) ? 2 : 1);
+	pclk = hclk / ((tmp & S3C2412_CLKDIVN_PDIVN) ? 2 : 1);
+
+	/* print brieft summary of clocks, etc */
+
+	printk("S3C2412: core %ld.%03ld MHz, memory %ld.%03ld MHz, peripheral %ld.%03ld MHz\n",
+	       print_mhz(fclk), print_mhz(hclk), print_mhz(pclk));
+
+	/* initialise the clocks here, to allow other things like the
+	 * console to use them
+	 */
+
+	s3c24xx_setup_clocks(xtal, fclk, hclk, pclk);
+	s3c2412_baseclk_add();
+}
+
+/* need to register class before we actually register the device, and
+ * we also need to ensure that it has been initialised before any of the
+ * drivers even try to use it (even if not on an s3c2412 based system)
+ * as a driver which may support both 2410 and 2440 may try and use it.
+*/
+
+#ifdef CONFIG_PM
+static struct sleep_save s3c2412_sleep[] = {
+	SAVE_ITEM(S3C2412_DSC0),
+	SAVE_ITEM(S3C2412_DSC1),
+	SAVE_ITEM(S3C2413_GPJDAT),
+	SAVE_ITEM(S3C2413_GPJCON),
+	SAVE_ITEM(S3C2413_GPJUP),
+
+	/* save the sleep configuration anyway, just in case these
+	 * get damaged during wakeup */
+
+	SAVE_ITEM(S3C2412_GPBSLPCON),
+	SAVE_ITEM(S3C2412_GPCSLPCON),
+	SAVE_ITEM(S3C2412_GPDSLPCON),
+	SAVE_ITEM(S3C2412_GPESLPCON),
+	SAVE_ITEM(S3C2412_GPFSLPCON),
+	SAVE_ITEM(S3C2412_GPGSLPCON),
+	SAVE_ITEM(S3C2412_GPHSLPCON),
+	SAVE_ITEM(S3C2413_GPJSLPCON),
+};
+
+static int s3c2412_suspend(struct sys_device *dev, pm_message_t state)
+{
+	s3c2410_pm_do_save(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep));
+	return 0;
+}
+
+static int s3c2412_resume(struct sys_device *dev)
+{
+	s3c2410_pm_do_restore(s3c2412_sleep, ARRAY_SIZE(s3c2412_sleep));
+	return 0;
+}
+
+#else
+#define s3c2412_suspend NULL
+#define s3c2412_resume  NULL
+#endif
+
+struct sysdev_class s3c2412_sysclass = {
+	set_kset_name("s3c2412-core"),
+	.suspend	= s3c2412_suspend,
+	.resume		= s3c2412_resume
+};
+
+static int __init s3c2412_core_init(void)
+{
+	return sysdev_class_register(&s3c2412_sysclass);
+}
+
+core_initcall(s3c2412_core_init);
+
+static struct sys_device s3c2412_sysdev = {
+	.cls		= &s3c2412_sysclass,
+};
+
+int __init s3c2412_init(void)
+{
+	printk("S3C2412: Initialising architecture\n");
+
+	return sysdev_register(&s3c2412_sysdev);
+}

+ 29 - 0
arch/arm/mach-s3c2410/s3c2412.h

@@ -0,0 +1,29 @@
+/* arch/arm/mach-s3c2410/s3c2412.h
+ *
+ * Copyright (c) 2006 Simtec Electronics
+ *	Ben Dooks <ben@simtec.co.uk>
+ *
+ * Header file for s3c2412 cpu support
+ *
+ * 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.
+*/
+
+#ifdef CONFIG_CPU_S3C2412
+
+extern  int s3c2412_init(void);
+
+extern void s3c2412_map_io(struct map_desc *mach_desc, int size);
+
+extern void s3c2412_init_uarts(struct s3c2410_uartcfg *cfg, int no);
+
+extern void s3c2412_init_clocks(int xtal);
+
+extern  int s3c2412_baseclk_add(void);
+#else
+#define s3c2412_init_clocks NULL
+#define s3c2412_init_uarts NULL
+#define s3c2412_map_io NULL
+#define s3c2412_init NULL
+#endif

+ 5 - 5
arch/arm/mm/Kconfig

@@ -61,9 +61,9 @@ config CPU_ARM720T
 
 # ARM920T
 config CPU_ARM920T
-	bool "Support ARM920T processor" if !ARCH_S3C2410
-	depends on ARCH_EP93XX || ARCH_INTEGRATOR || ARCH_S3C2410 || ARCH_IMX || ARCH_AAEC2000 || ARCH_AT91RM9200
-	default y if ARCH_S3C2410 || ARCH_AT91RM9200
+	bool "Support ARM920T processor"
+	depends on ARCH_EP93XX || ARCH_INTEGRATOR || CPU_S3C2410 || CPU_S3C2440 || CPU_S3C2442 || ARCH_IMX || ARCH_AAEC2000 || ARCH_AT91RM9200
+	default y if CPU_S3C2410 || CPU_S3C2440 || CPU_S3C2442 || ARCH_AT91RM9200
 	select CPU_32v4
 	select CPU_ABRT_EV4T
 	select CPU_CACHE_V4WT
@@ -121,8 +121,8 @@ config CPU_ARM925T
 # ARM926T
 config CPU_ARM926T
 	bool "Support ARM926T processor"
-	depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008 || ARCH_NETX
-	default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008 || ARCH_NETX
+	depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412
+	default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412
 	select CPU_32v5
 	select CPU_ABRT_EV5TJ
 	select CPU_CACHE_VIVT

+ 6 - 3
drivers/serial/Kconfig

@@ -354,21 +354,24 @@ config SERIAL_CLPS711X_CONSOLE
 	  kernel at boot time.)
 
 config SERIAL_S3C2410
-	tristate "Samsung S3C2410 Serial port support"
+	tristate "Samsung S3C2410/S3C2440/S3C2442/S3C2412 Serial port support"
 	depends on ARM && ARCH_S3C2410
 	select SERIAL_CORE
 	help
-	  Support for the on-chip UARTs on the Samsung S3C2410X CPU,
+	  Support for the on-chip UARTs on the Samsung S3C24XX series CPUs,
 	  providing /dev/ttySAC0, 1 and 2 (note, some machines may not
 	  provide all of these ports, depending on how the serial port
 	  pins are configured.
 
+	  Currently this driver supports the UARTS on the S3C2410, S3C2440,
+	  S3C2442, S3C2412 and S3C2413 CPUs.
+
 config SERIAL_S3C2410_CONSOLE
 	bool "Support for console on S3C2410 serial port"
 	depends on SERIAL_S3C2410=y
 	select SERIAL_CORE_CONSOLE
 	help
-	  Allow selection of the S3C2410 on-board serial ports for use as
+	  Allow selection of the S3C24XX on-board serial ports for use as
 	  an virtual console.
 
 	  Even if you say Y here, the currently visible virtual console

+ 142 - 1
drivers/serial/s3c2410.c

@@ -872,6 +872,8 @@ static const char *s3c24xx_serial_type(struct uart_port *port)
 		return "S3C2410";
 	case PORT_S3C2440:
 		return "S3C2440";
+	case PORT_S3C2412:
+		return "S3C2412";
 	default:
 		return NULL;
 	}
@@ -1528,6 +1530,141 @@ static inline void s3c2440_serial_exit(void)
 #define s3c2440_uart_inf_at NULL
 #endif /* CONFIG_CPU_S3C2440 */
 
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+
+static int s3c2412_serial_setsource(struct uart_port *port,
+				     struct s3c24xx_uart_clksrc *clk)
+{
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+	ucon &= ~S3C2412_UCON_CLKMASK;
+
+	if (strcmp(clk->name, "uclk") == 0)
+		ucon |= S3C2440_UCON_UCLK;
+	else if (strcmp(clk->name, "pclk") == 0)
+		ucon |= S3C2440_UCON_PCLK;
+	else if (strcmp(clk->name, "usysclk") == 0)
+		ucon |= S3C2412_UCON_USYSCLK;
+	else {
+		printk(KERN_ERR "unknown clock source %s\n", clk->name);
+		return -EINVAL;
+	}
+
+	wr_regl(port, S3C2410_UCON, ucon);
+	return 0;
+}
+
+
+static int s3c2412_serial_getsource(struct uart_port *port,
+				    struct s3c24xx_uart_clksrc *clk)
+{
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+	switch (ucon & S3C2412_UCON_CLKMASK) {
+	case S3C2412_UCON_UCLK:
+		clk->divisor = 1;
+		clk->name = "uclk";
+		break;
+
+	case S3C2412_UCON_PCLK:
+	case S3C2412_UCON_PCLK2:
+		clk->divisor = 1;
+		clk->name = "pclk";
+		break;
+
+	case S3C2412_UCON_USYSCLK:
+		clk->divisor = 1;
+		clk->name = "usysclk";
+		break;
+	}
+
+	return 0;
+}
+
+static int s3c2412_serial_resetport(struct uart_port *port,
+				    struct s3c2410_uartcfg *cfg)
+{
+	unsigned long ucon = rd_regl(port, S3C2410_UCON);
+
+	dbg("%s: port=%p (%08lx), cfg=%p\n",
+	    __FUNCTION__, port, port->mapbase, cfg);
+
+	/* ensure we don't change the clock settings... */
+
+	ucon &= S3C2412_UCON_CLKMASK;
+
+	wr_regl(port, S3C2410_UCON,  ucon | cfg->ucon);
+	wr_regl(port, S3C2410_ULCON, cfg->ulcon);
+
+	/* reset both fifos */
+
+	wr_regl(port, S3C2410_UFCON, cfg->ufcon | S3C2410_UFCON_RESETBOTH);
+	wr_regl(port, S3C2410_UFCON, cfg->ufcon);
+
+	return 0;
+}
+
+static struct s3c24xx_uart_info s3c2412_uart_inf = {
+	.name		= "Samsung S3C2412 UART",
+	.type		= PORT_S3C2412,
+	.fifosize	= 64,
+	.rx_fifomask	= S3C2440_UFSTAT_RXMASK,
+	.rx_fifoshift	= S3C2440_UFSTAT_RXSHIFT,
+	.rx_fifofull	= S3C2440_UFSTAT_RXFULL,
+	.tx_fifofull	= S3C2440_UFSTAT_TXFULL,
+	.tx_fifomask	= S3C2440_UFSTAT_TXMASK,
+	.tx_fifoshift	= S3C2440_UFSTAT_TXSHIFT,
+	.get_clksrc	= s3c2412_serial_getsource,
+	.set_clksrc	= s3c2412_serial_setsource,
+	.reset_port	= s3c2412_serial_resetport,
+};
+
+/* device management */
+
+static int s3c2412_serial_probe(struct platform_device *dev)
+{
+	dbg("s3c2440_serial_probe: dev=%p\n", dev);
+	return s3c24xx_serial_probe(dev, &s3c2440_uart_inf);
+}
+
+static struct platform_driver s3c2412_serial_drv = {
+	.probe		= s3c2412_serial_probe,
+	.remove		= s3c24xx_serial_remove,
+	.suspend	= s3c24xx_serial_suspend,
+	.resume		= s3c24xx_serial_resume,
+	.driver		= {
+		.name	= "s3c2412-uart",
+		.owner	= THIS_MODULE,
+	},
+};
+
+
+static inline int s3c2412_serial_init(void)
+{
+	return s3c24xx_serial_init(&s3c2412_serial_drv, &s3c2412_uart_inf);
+}
+
+static inline void s3c2412_serial_exit(void)
+{
+	platform_driver_unregister(&s3c2412_serial_drv);
+}
+
+#define s3c2412_uart_inf_at &s3c2412_uart_inf
+#else
+
+static inline int s3c2412_serial_init(void)
+{
+	return 0;
+}
+
+static inline void s3c2412_serial_exit(void)
+{
+}
+
+#define s3c2412_uart_inf_at NULL
+#endif /* CONFIG_CPU_S3C2440 */
+
+
 /* module initialisation code */
 
 static int __init s3c24xx_serial_modinit(void)
@@ -1542,6 +1679,7 @@ static int __init s3c24xx_serial_modinit(void)
 
 	s3c2400_serial_init();
 	s3c2410_serial_init();
+	s3c2412_serial_init();
 	s3c2440_serial_init();
 
 	return 0;
@@ -1551,6 +1689,7 @@ static void __exit s3c24xx_serial_modexit(void)
 {
 	s3c2400_serial_exit();
 	s3c2410_serial_exit();
+	s3c2412_serial_exit();
 	s3c2440_serial_exit();
 
 	uart_unregister_driver(&s3c24xx_uart_drv);
@@ -1773,6 +1912,8 @@ static int s3c24xx_serial_initconsole(void)
 		info = s3c2410_uart_inf_at;
 	} else if (strcmp(dev->name, "s3c2440-uart") == 0) {
 		info = s3c2440_uart_inf_at;
+	} else if (strcmp(dev->name, "s3c2412-uart") == 0) {
+		info = s3c2412_uart_inf_at;
 	} else {
 		printk(KERN_ERR "s3c24xx: no driver for %s\n", dev->name);
 		return 0;
@@ -1796,4 +1937,4 @@ console_initcall(s3c24xx_serial_initconsole);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_DESCRIPTION("Samsung S3C2410/S3C2440 Serial port driver");
+MODULE_DESCRIPTION("Samsung S3C2410/S3C2440/S3C2412 Serial port driver");

+ 8 - 2
include/asm-arm/arch-s3c2410/debug-macro.S

@@ -33,7 +33,7 @@
 		.endm
 
 		.macro	senduart,rd,rx
-		str	\rd, [\rx, # S3C2410_UTXH ]
+		strb 	\rd, [\rx, # S3C2410_UTXH ]
 		.endm
 
 		.macro	busyuart, rd, rx
@@ -42,6 +42,12 @@
 		beq	1001f				@
 		@ FIFO enabled...
 1003:
+		@ check for arm920 vs arm926. currently assume all arm926
+		@ devices have an 64 byte FIFO identical to the s3c2440
+		mrc	p15, 0, \rd, c0, c0
+		and	\rd, \rd, #0xff0
+		teq	\rd, #0x260
+		beq	1004f
 		mrc	p15, 0, \rd, c1, c0
 		tst	\rd, #1
 		addeq	\rd, \rx, #(S3C24XX_PA_GPIO - S3C24XX_PA_UART)
@@ -50,7 +56,7 @@
 		ldr	\rd, [ \rd, # S3C2410_GSTATUS1 - S3C2410_GPIOREG(0) ]
 		and	\rd, \rd, #0x00ff0000
 		teq	\rd, #0x00440000		@ is it 2440?
-
+1004:
 		ldr	\rd, [ \rx, # S3C2410_UFSTAT ]
 		moveq	\rd, \rd, lsr #SHIFT_2440TXF
 		tst	\rd, #S3C2410_UFSTAT_TXFULL

+ 7 - 23
include/asm-arm/arch-s3c2410/entry-macro.S

@@ -18,8 +18,6 @@
 
 #define INTPND		(0x10)
 #define INTOFFSET	(0x14)
-#define EXTINTPEND	(0xa8)
-#define EXTINTMASK	(0xa4)
 
 #include <asm/hardware.h>
 #include <asm/arch/irqs.h>
@@ -28,37 +26,23 @@
 
 		mov	\base, #S3C24XX_VA_IRQ
 
-		ldr	\irqstat, [ \base, #INTPND]
-		bics	\irqnr, \irqstat, #3<<4		@@ only an GPIO IRQ
-		beq	2000f
-
 		@@ try the interrupt offset register, since it is there
 
+		ldr	\irqstat, [ \base, #INTPND ]
+		teq	\irqstat, #0
+		beq	1002f
 		ldr	\irqnr, [ \base, #INTOFFSET ]
 		mov	\tmp, #1
 		tst	\irqstat, \tmp, lsl \irqnr
-		addne	\irqnr, \irqnr, #IRQ_EINT0
 		bne	1001f
 
 		@@ the number specified is not a valid irq, so try
 		@@ and work it out for ourselves
 
-		mov	\irqnr, #IRQ_EINT0		@@ start here
-		b	3000f
-
-2000:
-		@@ load the GPIO interrupt register, and check it
-
-		add	\tmp, \base, #S3C24XX_VA_GPIO - S3C24XX_VA_IRQ
-		ldr	\irqstat, [ \tmp, # EXTINTPEND ]
-		ldr	\irqnr,   [ \tmp, # EXTINTMASK ]
-		bics	\irqstat, \irqstat, \irqnr
-		beq	1001f
-
-		mov	\irqnr, #(IRQ_EINT4 - 4)
+		mov	\irqnr, #0		@@ start here
 
 		@@ work out which irq (if any) we got
-3000:
+
 		movs	\tmp, \irqstat, lsl#16
 		addeq	\irqnr, \irqnr, #16
 		moveq	\irqstat, \irqstat, lsr#16
@@ -75,9 +59,9 @@
 		addeq	\irqnr, \irqnr, #1
 
 		@@ we have the value
-		movs	\irqnr, \irqnr
-
 1001:
+		adds	\irqnr, \irqnr, #IRQ_EINT0
+1002:
 		@@ exit here, Z flag unset if IRQ
 
 	.endm

+ 16 - 0
include/asm-arm/arch-s3c2410/map.h

@@ -236,4 +236,20 @@
 #define S3C24XX_PA_SPI      S3C2410_PA_SPI
 #endif
 
+/* deal with the registers that move under the 2412/2413 */
+
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+#ifndef __ASSEMBLY__
+extern void __iomem *s3c24xx_va_gpio2;
+#endif
+#ifdef CONFIG_CPU_S3C2412_ONLY
+#define S3C24XX_VA_GPIO2 (S3C24XX_VA_GPIO + 0x10)
+#else
+#define S3C24XX_VA_GPIO2 s3c24xx_va_gpio2
+#endif
+#else
+#define s3c24xx_va_gpio2 S3C24XX_VA_GPIO
+#define S3C24XX_VA_GPIO2 S3C24XX_VA_GPIO
+#endif
+
 #endif /* __ASM_ARCH_MAP_H */

+ 62 - 1
include/asm-arm/arch-s3c2410/regs-clock.h

@@ -1,6 +1,6 @@
 /* linux/include/asm/arch-s3c2410/regs-clock.h
  *
- * Copyright (c) 2003,2004,2005 Simtec Electronics <linux@simtec.co.uk>
+ * Copyright (c) 2003,2004,2005,2006 Simtec Electronics <linux@simtec.co.uk>
  *		      http://armlinux.simtec.co.uk/
  *
  * This program is free software; you can redistribute it and/or modify
@@ -140,5 +140,66 @@ s3c2410_get_pll(unsigned int pllval, unsigned int baseclk)
 
 #endif /* CONFIG_CPU_S3C2440 or CONFIG_CPU_S3C2442 */
 
+#if defined(CONFIG_CPU_S3C2412) || defined(CONFIG_CPU_S3C2413)
+
+#define S3C2412_OSCSET		S3C2410_CLKREG(0x18)
+#define S3C2412_CLKSRC		S3C2410_CLKREG(0x1C)
+
+#define S3C2412_PLLCON_OFF		(1<<20)
+
+#define S3C2412_CLKDIVN_PDIVN		(1<<2)
+#define S3C2412_CLKDIVN_HDIVN_MASK	(3<<0)
+#define S3C2421_CLKDIVN_ARMDIVN		(1<<3)
+#define S3C2412_CLKDIVN_USB48DIV	(1<<6)
+#define S3C2412_CLKDIVN_UARTDIV_MASK	(15<<8)
+#define S3C2412_CLKDIVN_UARTDIV_SHIFT	(8)
+#define S3C2412_CLKDIVN_I2SDIV_MASK	(15<<12)
+#define S3C2412_CLKDIVN_I2SDIV_SHIFT	(12)
+#define S3C2412_CLKDIVN_CAMDIV_MASK	(15<<16)
+#define S3C2412_CLKDIVN_CAMDIV_SHIFT	(16)
+
+#define S3C2412_CLKCON_WDT		(1<<28)
+#define S3C2412_CLKCON_SPI		(1<<27)
+#define S3C2412_CLKCON_IIS		(1<<26)
+#define S3C2412_CLKCON_IIC		(1<<25)
+#define S3C2412_CLKCON_ADC		(1<<24)
+#define S3C2412_CLKCON_RTC		(1<<23)
+#define S3C2412_CLKCON_GPIO		(1<<22)
+#define S3C2412_CLKCON_UART2		(1<<21)
+#define S3C2412_CLKCON_UART1		(1<<20)
+#define S3C2412_CLKCON_UART0		(1<<19)
+#define S3C2412_CLKCON_SDI		(1<<18)
+#define S3C2412_CLKCON_PWMT		(1<<17)
+#define S3C2412_CLKCON_USBD		(1<<16)
+#define S3C2412_CLKCON_CAMCLK		(1<<15)
+#define S3C2412_CLKCON_UARTCLK		(1<<14)
+/* missing 13 */
+#define S3C2412_CLKCON_USB_HOST48	(1<<12)
+#define S3C2412_CLKCON_USB_DEV48	(1<<11)
+#define S3C2412_CLKCON_HCLKdiv2		(1<<10)
+#define S3C2412_CLKCON_HCLKx2		(1<<9)
+#define S3C2412_CLKCON_SDRAM		(1<<8)
+/* missing 7 */
+#define S3C2412_CLKCON_USBH		S3C2410_CLKCON_USBH
+#define S3C2412_CLKCON_LCDC		S3C2410_CLKCON_LCDC
+#define S3C2412_CLKCON_NAND		S3C2410_CLKCON_NAND
+#define S3C2412_CLKCON_DMA3		(1<<3)
+#define S3C2412_CLKCON_DMA2		(1<<2)
+#define S3C2412_CLKCON_DMA1		(1<<1)
+#define S3C2412_CLKCON_DMA0		(1<<0)
+
+/* clock sourec controls */
+
+#define S3C2412_CLKSRC_EXTCLKDIV_MASK		(7 << 0)
+#define S3C2412_CLKSRC_EXTCLKDIV_SHIFT		(0)
+#define S3C2412_CLKSRC_MDIVCLK_EXTCLKDIV	(1<<3)
+#define S3C2412_CLKSRC_MSYSCLK_MPLL		(1<<4)
+#define S3C2412_CLKSRC_USYSCLK_UPLL		(1<<5)
+#define S3C2412_CLKSRC_UARTCLK_MPLL		(1<<8)
+#define S3C2412_CLKSRC_I2SCLK_MPLL		(1<<9)
+#define S3C2412_CLKSRC_USBCLK_HCLK		(1<<10)
+#define S3C2412_CLKSRC_CAMCLK_HCLK		(1<<11)
+
+#endif /* CONFIG_CPU_S3C2412 | CONFIG_CPU_S3C2413 */
 
 #endif /* __ASM_ARM_REGS_CLOCK */

+ 3 - 0
include/asm-arm/arch-s3c2410/regs-dsc.h

@@ -23,6 +23,9 @@
 #define S3C2440_DSC0	   S3C2410_GPIOREG(0xc4)
 #define S3C2440_DSC1	   S3C2410_GPIOREG(0xc8)
 
+#define S3C2412_DSC0	   S3C2410_GPIOREG(0xdc)
+#define S3C2412_DSC1	   S3C2410_GPIOREG(0xe0)
+
 #define S3C2440_SELECT_DSC0 (0)
 #define S3C2440_SELECT_DSC1 (1<<31)
 

+ 60 - 3
include/asm-arm/arch-s3c2410/regs-gpio.h

@@ -45,7 +45,7 @@
 #define S3C24XX_MISCCR        S3C2400_MISCCR
 #else
 #define S3C24XX_GPIO_BASE(x)  S3C2410_GPIO_BASE(x)
-#define S3C24XX_MISCCR        S3C2410_MISCCR
+#define S3C24XX_MISCCR	      S3C24XX_GPIOREG2(0x80)
 #endif /* CONFIG_CPU_S3C2400 */
 
 
@@ -73,9 +73,15 @@
 #define S3C2410_GPIO_SFN2    (0xFFFFFFF2)	/* not available on A */
 #define S3C2410_GPIO_SFN3    (0xFFFFFFF3)	/* not available on A */
 
-/* configure GPIO ports A..G */
+/* register address for the GPIO registers.
+ * S3C24XX_GPIOREG2 is for the second set of registers in the
+ * GPIO which move between s3c2410 and s3c2412 type systems */
 
 #define S3C2410_GPIOREG(x) ((x) + S3C24XX_VA_GPIO)
+#define S3C24XX_GPIOREG2(x) ((x) + S3C24XX_VA_GPIO2)
+
+
+/* configure GPIO ports A..G */
 
 /* port A - S3C2410: 22bits, zero in bit X makes pin X output
  *          S3C2400: 18bits, zero in bit X makes pin X output
@@ -953,11 +959,18 @@
 #define S3C2410_GPH10_OUTP    (0x01 << 20)
 #define S3C2410_GPH10_CLKOUT1 (0x02 << 20)
 
+/* The S3C2412 and S3C2413 move the GPJ register set to after
+ * GPH, which means all registers after 0x80 are now offset by 0x10
+ * for the 2412/2413 from the 2410/2440/2442
+*/
+
 /* miscellaneous control */
 #define S3C2400_MISCCR	   S3C2410_GPIOREG(0x54)
 #define S3C2410_MISCCR	   S3C2410_GPIOREG(0x80)
 #define S3C2410_DCLKCON	   S3C2410_GPIOREG(0x84)
 
+#define S3C24XX_DCLKCON	   S3C24XX_GPIOREG2(0x84)
+
 /* see clock.h for dclk definitions */
 
 /* pullup control on databus */
@@ -985,6 +998,8 @@
 #define S3C2410_MISCCR_CLK0_DCLK0   (5<<4)
 #define S3C2410_MISCCR_CLK0_MASK    (7<<4)
 
+#define S3C2412_MISCCR_CLK0_RTC	    (2<<4)
+
 #define S3C2410_MISCCR_CLK1_MPLL    (0<<8)
 #define S3C2410_MISCCR_CLK1_UPLL    (1<<8)
 #define S3C2410_MISCCR_CLK1_FCLK    (2<<8)
@@ -993,6 +1008,8 @@
 #define S3C2410_MISCCR_CLK1_DCLK1   (5<<8)
 #define S3C2410_MISCCR_CLK1_MASK    (7<<8)
 
+#define S3C2412_MISCCR_CLK1_CLKsrc  (0<<8)
+
 #define S3C2410_MISCCR_USBSUSPND0   (1<<12)
 #define S3C2410_MISCCR_USBSUSPND1   (1<<13)
 
@@ -1000,7 +1017,7 @@
 
 #define S3C2410_MISCCR_nEN_SCLK0    (1<<17)
 #define S3C2410_MISCCR_nEN_SCLK1    (1<<18)
-#define S3C2410_MISCCR_nEN_SCLKE    (1<<19)
+#define S3C2410_MISCCR_nEN_SCLKE    (1<<19)	/* not 2412 */
 #define S3C2410_MISCCR_SDSLEEP	    (7<<17)
 
 /* external interrupt control... */
@@ -1017,6 +1034,10 @@
 #define S3C2410_EXTINT1	   S3C2410_GPIOREG(0x8C)
 #define S3C2410_EXTINT2	   S3C2410_GPIOREG(0x90)
 
+#define S3C24XX_EXTINT0	   S3C24XX_GPIOREG2(0x88)
+#define S3C24XX_EXTINT1	   S3C24XX_GPIOREG2(0x8C)
+#define S3C24XX_EXTINT2	   S3C24XX_GPIOREG2(0x90)
+
 /* values for S3C2410_EXTINT0/1/2 */
 #define S3C2410_EXTINT_LOWLEV	 (0x00)
 #define S3C2410_EXTINT_HILEV	 (0x01)
@@ -1030,6 +1051,11 @@
 #define S3C2410_EINFLT2	   S3C2410_GPIOREG(0x9C)
 #define S3C2410_EINFLT3	   S3C2410_GPIOREG(0xA0)
 
+#define S3C24XX_EINFLT0	   S3C24XX_GPIOREG2(0x94)
+#define S3C24XX_EINFLT1	   S3C24XX_GPIOREG2(0x98)
+#define S3C24XX_EINFLT2	   S3C24XX_GPIOREG2(0x9C)
+#define S3C24XX_EINFLT3	   S3C24XX_GPIOREG2(0xA0)
+
 /* values for interrupt filtering */
 #define S3C2410_EINTFLT_PCLK		(0x00)
 #define S3C2410_EINTFLT_EXTCLK		(1<<7)
@@ -1039,6 +1065,7 @@
 
 /* GSTATUS have miscellaneous information in them
  *
+ * These move between s3c2410 and s3c2412 style systems.
  */
 
 #define S3C2410_GSTATUS0   S3C2410_GPIOREG(0x0AC)
@@ -1047,6 +1074,18 @@
 #define S3C2410_GSTATUS3   S3C2410_GPIOREG(0x0B8)
 #define S3C2410_GSTATUS4   S3C2410_GPIOREG(0x0BC)
 
+#define S3C2412_GSTATUS0   S3C2410_GPIOREG(0x0BC)
+#define S3C2412_GSTATUS1   S3C2410_GPIOREG(0x0C0)
+#define S3C2412_GSTATUS2   S3C2410_GPIOREG(0x0C4)
+#define S3C2412_GSTATUS3   S3C2410_GPIOREG(0x0C8)
+#define S3C2412_GSTATUS4   S3C2410_GPIOREG(0x0CC)
+
+#define S3C24XX_GSTATUS0   S3C24XX_GPIOREG2(0x0AC)
+#define S3C24XX_GSTATUS1   S3C24XX_GPIOREG2(0x0B0)
+#define S3C24XX_GSTATUS2   S3C24XX_GPIOREG2(0x0B4)
+#define S3C24XX_GSTATUS3   S3C24XX_GPIOREG2(0x0B8)
+#define S3C24XX_GSTATUS4   S3C24XX_GPIOREG2(0x0BC)
+
 #define S3C2410_GSTATUS0_nWAIT	   (1<<3)
 #define S3C2410_GSTATUS0_NCON	   (1<<2)
 #define S3C2410_GSTATUS0_RnB	   (1<<1)
@@ -1054,6 +1093,7 @@
 
 #define S3C2410_GSTATUS1_IDMASK	   (0xffff0000)
 #define S3C2410_GSTATUS1_2410	   (0x32410000)
+#define S3C2410_GSTATUS1_2412	   (0x32412001)
 #define S3C2410_GSTATUS1_2440	   (0x32440000)
 #define S3C2410_GSTATUS1_2442	   (0x32440aaa)
 
@@ -1077,5 +1117,22 @@
 #define S3C2400_OPENCR_OPC_MOSIDIS  (0<<5)
 #define S3C2400_OPENCR_OPC_MOSIEN   (1<<5)
 
+/* 2412/2413 sleep configuration registers */
+
+#define S3C2412_GPBSLPCON	S3C2410_GPIOREG(0x1C)
+#define S3C2412_GPCSLPCON	S3C2410_GPIOREG(0x2C)
+#define S3C2412_GPDSLPCON	S3C2410_GPIOREG(0x3C)
+#define S3C2412_GPESLPCON	S3C2410_GPIOREG(0x4C)
+#define S3C2412_GPFSLPCON	S3C2410_GPIOREG(0x5C)
+#define S3C2412_GPGSLPCON	S3C2410_GPIOREG(0x6C)
+#define S3C2412_GPHSLPCON	S3C2410_GPIOREG(0x7C)
+
+/* definitions for each pin bit */
+#define S3C2412_SLPCON_LOW(x)	( 0x00 << ((x) * 2))
+#define S3C2412_SLPCON_HI(x)	( 0x01 << ((x) * 2))
+#define S3C2412_SLPCON_IN(x)	( 0x02 << ((x) * 2))
+#define S3C2412_SLPCON_PDWN(x)	( 0x03 << ((x) * 2))
+#define S3C2412_SLPCON_MASK(x)	( 0x03 << ((x) * 2))
+
 #endif	/* __ASM_ARCH_REGS_GPIO_H */
 

+ 5 - 0
include/asm-arm/arch-s3c2410/regs-gpioj.h

@@ -32,6 +32,11 @@
 #define S3C2440_GPJDAT	    S3C2410_GPIOREG(0xd4)
 #define S3C2440_GPJUP	    S3C2410_GPIOREG(0xd8)
 
+#define S3C2413_GPJCON		S3C2410_GPIOREG(0x80)
+#define S3C2413_GPJDAT		S3C2410_GPIOREG(0x84)
+#define S3C2413_GPJUP		S3C2410_GPIOREG(0x88)
+#define S3C2413_GPJSLPCON	S3C2410_GPIOREG(0x8C)
+
 #define S3C2440_GPJ0            S3C2410_GPIONO(S3C2440_GPIO_BANKJ, 0)
 #define S3C2440_GPJ0_INP        (0x00 << 0)
 #define S3C2440_GPJ0_OUTP       (0x01 << 0)

+ 6 - 0
include/asm-arm/arch-s3c2410/regs-irq.h

@@ -23,6 +23,7 @@
 
 #define S3C2410_IRQREG(x)   ((x) + S3C24XX_VA_IRQ)
 #define S3C2410_EINTREG(x)  ((x) + S3C24XX_VA_GPIO)
+#define S3C24XX_EINTREG(x)  ((x) + S3C24XX_VA_GPIO2)
 
 #define S3C2410_SRCPND	       S3C2410_IRQREG(0x000)
 #define S3C2410_INTMOD	       S3C2410_IRQREG(0x004)
@@ -40,5 +41,10 @@
 
 #define S3C2410_EINTMASK       S3C2410_EINTREG(0x0A4)
 #define S3C2410_EINTPEND       S3C2410_EINTREG(0X0A8)
+#define S3C2412_EINTMASK       S3C2410_EINTREG(0x0B4)
+#define S3C2412_EINTPEND       S3C2410_EINTREG(0X0B8)
+
+#define S3C24XX_EINTMASK       S3C24XX_EINTREG(0x0A4)
+#define S3C24XX_EINTPEND       S3C24XX_EINTREG(0X0A8)
 
 #endif /* ___ASM_ARCH_REGS_IRQ_H */

+ 15 - 0
include/asm-arm/arch-s3c2410/regs-serial.h

@@ -82,6 +82,12 @@
 #define S3C2440_UCON2_DIVMASK	  (7 << 12)
 #define S3C2440_UCON_DIVSHIFT	  (12)
 
+#define S3C2412_UCON_CLKMASK	(3<<10)
+#define S3C2412_UCON_UCLK	(1<<10)
+#define S3C2412_UCON_USYSCLK	(3<<10)
+#define S3C2412_UCON_PCLK	(0<<10)
+#define S3C2412_UCON_PCLK2	(2<<10)
+
 #define S3C2410_UCON_UCLK	  (1<<10)
 #define S3C2410_UCON_SBREAK	  (1<<4)
 
@@ -124,6 +130,15 @@
 #define	S3C2410_UMCOM_AFC	  (1<<4)
 #define	S3C2410_UMCOM_RTS_LOW	  (1<<0)
 
+#define S3C2412_UMCON_AFC_63	(0<<5)
+#define S3C2412_UMCON_AFC_56	(1<<5)
+#define S3C2412_UMCON_AFC_48	(2<<5)
+#define S3C2412_UMCON_AFC_40	(3<<5)
+#define S3C2412_UMCON_AFC_32	(4<<5)
+#define S3C2412_UMCON_AFC_24	(5<<5)
+#define S3C2412_UMCON_AFC_16	(6<<5)
+#define S3C2412_UMCON_AFC_8	(7<<5)
+
 #define S3C2410_UFSTAT_TXFULL	  (1<<9)
 #define S3C2410_UFSTAT_RXFULL	  (1<<8)
 #define S3C2410_UFSTAT_TXMASK	  (15<<4)

+ 3 - 0
include/linux/serial_core.h

@@ -130,6 +130,9 @@
 /* SUN4V Hypervisor Console */
 #define PORT_SUNHV	72
 
+#define PORT_S3C2412	73
+
+
 #ifdef __KERNEL__
 
 #include <linux/compiler.h>