sh: Add support for SH7206 and SH7619 CPU subtypes.

This implements initial support for the SH7206 (SH-2A) and SH7619
(SH-2) MMU-less CPUs.

Signed-off-by: Yoshinori Sato <ysato@users.sourceforge.jp>
Signed-off-by: Paul Mundt <lethal@linux-sh.org>
diff --git a/arch/sh/kernel/Makefile b/arch/sh/kernel/Makefile
index 5da88a4..50d54c2 100644
--- a/arch/sh/kernel/Makefile
+++ b/arch/sh/kernel/Makefile
@@ -4,7 +4,7 @@
 
 extra-y	:= head.o init_task.o vmlinux.lds
 
-obj-y	:= process.o signal.o entry.o traps.o irq.o \
+obj-y	:= process.o signal.o traps.o irq.o \
 	ptrace.o setup.o time.o sys_sh.o semaphore.o \
 	io.o io_generic.o sh_ksyms.o syscalls.o
 
diff --git a/arch/sh/kernel/cpu/Makefile b/arch/sh/kernel/cpu/Makefile
index fb5dac0..0582e67 100644
--- a/arch/sh/kernel/cpu/Makefile
+++ b/arch/sh/kernel/cpu/Makefile
@@ -2,11 +2,12 @@
 # Makefile for the Linux/SuperH CPU-specifc backends.
 #
 
-obj-y	+= irq/ init.o clock.o
-
-obj-$(CONFIG_CPU_SH2)		+= sh2/
-obj-$(CONFIG_CPU_SH3)		+= sh3/
-obj-$(CONFIG_CPU_SH4)		+= sh4/
+obj-$(CONFIG_CPU_SH2)		= sh2/
+obj-$(CONFIG_CPU_SH2A)		= sh2a/
+obj-$(CONFIG_CPU_SH3)		= sh3/
+obj-$(CONFIG_CPU_SH4)		= sh4/
 
 obj-$(CONFIG_UBC_WAKEUP)	+= ubc.o
 obj-$(CONFIG_SH_ADC)		+= adc.o
+
+obj-y	+= irq/ init.o clock.o
diff --git a/arch/sh/kernel/cpu/init.c b/arch/sh/kernel/cpu/init.c
index bfb90eb..4812176 100644
--- a/arch/sh/kernel/cpu/init.c
+++ b/arch/sh/kernel/cpu/init.c
@@ -68,12 +68,14 @@
 
 		waysize = cpu_data->dcache.sets;
 
+#ifdef CCR_CACHE_ORA
 		/*
 		 * If the OC is already in RAM mode, we only have
 		 * half of the entries to flush..
 		 */
 		if (ccr & CCR_CACHE_ORA)
 			waysize >>= 1;
+#endif
 
 		waysize <<= cpu_data->dcache.entry_shift;
 
diff --git a/arch/sh/kernel/cpu/irq/imask.c b/arch/sh/kernel/cpu/irq/imask.c
index a33ae3e..301b505 100644
--- a/arch/sh/kernel/cpu/irq/imask.c
+++ b/arch/sh/kernel/cpu/irq/imask.c
@@ -53,7 +53,10 @@
 {
 	unsigned long __dummy;
 
-	asm volatile("ldc	%2, r6_bank\n\t"
+	asm volatile(
+#ifdef CONFIG_CPU_HAS_SR_RB
+		     "ldc	%2, r6_bank\n\t"
+#endif
 		     "stc	sr, %0\n\t"
 		     "and	#0xf0, %0\n\t"
 		     "shlr2	%0\n\t"
diff --git a/arch/sh/kernel/cpu/irq/ipr.c b/arch/sh/kernel/cpu/irq/ipr.c
index a008956..f7a2bae1 100644
--- a/arch/sh/kernel/cpu/irq/ipr.c
+++ b/arch/sh/kernel/cpu/irq/ipr.c
@@ -62,6 +62,10 @@
 }
 EXPORT_SYMBOL(make_ipr_irq);
 
+/*
+ * XXX: Move this garbage in to the drivers, and kill off the ridiculous CPU
+ * subtype checks.
+ */
 static struct ipr_data sys_ipr_map[] = {
 #ifndef CONFIG_CPU_SUBTYPE_SH7780
 	{ TIMER_IRQ, TIMER_IPR_ADDR, TIMER_IPR_POS, TIMER_PRIORITY },
@@ -80,6 +84,18 @@
 	{ SCIF1_BRI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY },
 	{ SCIF1_TXI_IRQ, SCIF1_IPR_ADDR, SCIF1_IPR_POS, SCIF1_PRIORITY },
 #endif
+#ifdef SCIF2_ERI_IRQ
+	{ SCIF2_ERI_IRQ, SCIF2_IPR_ADDR, SCIF2_IPR_POS, SCIF2_PRIORITY },
+	{ SCIF2_RXI_IRQ, SCIF2_IPR_ADDR, SCIF2_IPR_POS, SCIF2_PRIORITY },
+	{ SCIF2_BRI_IRQ, SCIF2_IPR_ADDR, SCIF2_IPR_POS, SCIF2_PRIORITY },
+	{ SCIF2_TXI_IRQ, SCIF2_IPR_ADDR, SCIF2_IPR_POS, SCIF2_PRIORITY },
+#endif
+#ifdef SCIF3_ERI_IRQ
+	{ SCIF3_ERI_IRQ, SCIF3_IPR_ADDR, SCIF3_IPR_POS, SCIF3_PRIORITY },
+	{ SCIF3_RXI_IRQ, SCIF3_IPR_ADDR, SCIF3_IPR_POS, SCIF3_PRIORITY },
+	{ SCIF3_BRI_IRQ, SCIF3_IPR_ADDR, SCIF3_IPR_POS, SCIF3_PRIORITY },
+	{ SCIF3_TXI_IRQ, SCIF3_IPR_ADDR, SCIF3_IPR_POS, SCIF3_PRIORITY },
+#endif
 #if defined(CONFIG_CPU_SUBTYPE_SH7300)
 	{ SCIF0_IRQ, SCIF0_IPR_ADDR, SCIF0_IPR_POS, SCIF0_PRIORITY },
 	{ DMTE2_IRQ, DMA1_IPR_ADDR, DMA1_IPR_POS, DMA1_PRIORITY },
diff --git a/arch/sh/kernel/cpu/sh2/Makefile b/arch/sh/kernel/cpu/sh2/Makefile
index 389353f..f0f059a 100644
--- a/arch/sh/kernel/cpu/sh2/Makefile
+++ b/arch/sh/kernel/cpu/sh2/Makefile
@@ -2,5 +2,6 @@
 # Makefile for the Linux/SuperH SH-2 backends.
 #
 
-obj-y	:= probe.o
+obj-y	:= ex.o probe.o entry.o
 
+obj-$(CONFIG_CPU_SUBTYPE_SH7619) += setup-sh7619.o clock-sh7619.o
diff --git a/arch/sh/kernel/cpu/sh2/clock-sh7619.c b/arch/sh/kernel/cpu/sh2/clock-sh7619.c
new file mode 100644
index 0000000..d0440b2
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2/clock-sh7619.c
@@ -0,0 +1,81 @@
+/*
+ * arch/sh/kernel/cpu/sh2/clock-sh7619.c
+ *
+ * SH7619 support for the clock framework
+ *
+ *  Copyright (C) 2006  Yoshinori Sato
+ *
+ * Based on clock-sh4.c
+ *  Copyright (C) 2005  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+const static int pll1rate[]={1,2};
+const static int pfc_divisors[]={1,2,0,4};
+
+#if (CONFIG_SH_CLK_MD == 1) || (CONFIG_SH_CLK_MD == 2)
+#define PLL2 (4)
+#elif (CONFIG_SH_CLK_MD == 5) || (CONFIG_SH_CLK_MD == 6)
+#define PLL2 (2)
+#else
+#error "Illigal Clock Mode!"
+#endif
+
+static void master_clk_init(struct clk *clk)
+{
+	clk->rate *= PLL2 * pll1rate[(ctrl_inw(FREQCR) >> 8) & 7];
+}
+
+static struct clk_ops sh7619_master_clk_ops = {
+	.init		= master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+	int idx = (ctrl_inw(FREQCR) & 0x0007);
+	clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7619_module_clk_ops = {
+	.recalc		= module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+	clk->rate = clk->parent->rate / pll1rate[(ctrl_inw(FREQCR) >> 8) & 7];
+}
+
+static struct clk_ops sh7619_bus_clk_ops = {
+	.recalc		= bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+	clk->rate = clk->parent->rate;
+}
+
+static struct clk_ops sh7619_cpu_clk_ops = {
+	.recalc		= cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7619_clk_ops[] = {
+	&sh7619_master_clk_ops,
+	&sh7619_module_clk_ops,
+	&sh7619_bus_clk_ops,
+	&sh7619_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+	if (idx < ARRAY_SIZE(sh7619_clk_ops))
+		*ops = sh7619_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh2/probe.c b/arch/sh/kernel/cpu/sh2/probe.c
index f17a2a0..ba527d9 100644
--- a/arch/sh/kernel/cpu/sh2/probe.c
+++ b/arch/sh/kernel/cpu/sh2/probe.c
@@ -17,17 +17,23 @@
 
 int __init detect_cpu_and_cache_system(void)
 {
-	/*
-	 * For now, assume SH7604 .. fix this later.
-	 */
+#if defined(CONFIG_CPU_SUBTYPE_SH7604)
 	cpu_data->type			= CPU_SH7604;
 	cpu_data->dcache.ways		= 4;
-	cpu_data->dcache.way_shift	= 6;
+	cpu_data->dcache.way_incr	= (1<<10);
 	cpu_data->dcache.sets		= 64;
 	cpu_data->dcache.entry_shift	= 4;
 	cpu_data->dcache.linesz		= L1_CACHE_BYTES;
 	cpu_data->dcache.flags		= 0;
-
+#elif defined(CONFIG_CPU_SUBTYPE_SH7619)
+	cpu_data->type			= CPU_SH7619;
+	cpu_data->dcache.ways		= 4;
+	cpu_data->dcache.way_incr	= (1<<12);
+	cpu_data->dcache.sets		= 256;
+	cpu_data->dcache.entry_shift	= 4;
+	cpu_data->dcache.linesz		= L1_CACHE_BYTES;
+	cpu_data->dcache.flags		= 0;
+#endif
 	/*
 	 * SH-2 doesn't have separate caches
 	 */
diff --git a/arch/sh/kernel/cpu/sh2/setup-sh7619.c b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
new file mode 100644
index 0000000..82c2d90
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2/setup-sh7619.c
@@ -0,0 +1,53 @@
+/*
+ * SH7619 Setup
+ *
+ *  Copyright (C) 2006  Yoshinori Sato
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <asm/sci.h>
+
+static struct plat_sci_port sci_platform_data[] = {
+	{
+		.mapbase	= 0xf8400000,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 88, 89, 91, 90},
+	}, {
+		.mapbase	= 0xf8410000,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 92, 93, 95, 94},
+	}, {
+		.mapbase	= 0xf8420000,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 96, 97, 99, 98},
+	}, {
+		.flags = 0,
+	}
+};
+
+static struct platform_device sci_device = {
+	.name		= "sh-sci",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= sci_platform_data,
+	},
+};
+
+static struct platform_device *sh7619_devices[] __initdata = {
+	&sci_device,
+};
+
+static int __init sh7619_devices_setup(void)
+{
+	return platform_add_devices(sh7619_devices,
+				    ARRAY_SIZE(sh7619_devices));
+}
+__initcall(sh7619_devices_setup);
diff --git a/arch/sh/kernel/cpu/sh2a/Makefile b/arch/sh/kernel/cpu/sh2a/Makefile
new file mode 100644
index 0000000..350972a
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/Makefile
@@ -0,0 +1,10 @@
+#
+# Makefile for the Linux/SuperH SH-2A backends.
+#
+
+obj-y	:= common.o probe.o
+
+common-y	+= $(addprefix ../sh2/, ex.o)
+common-y	+= $(addprefix ../sh2/, entry.o)
+
+obj-$(CONFIG_CPU_SUBTYPE_SH7206) += setup-sh7206.o clock-sh7206.o
diff --git a/arch/sh/kernel/cpu/sh2a/clock-sh7206.c b/arch/sh/kernel/cpu/sh2a/clock-sh7206.c
new file mode 100644
index 0000000..a9ad309
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/clock-sh7206.c
@@ -0,0 +1,85 @@
+/*
+ * arch/sh/kernel/cpu/sh2a/clock-sh7206.c
+ *
+ * SH7206 support for the clock framework
+ *
+ *  Copyright (C) 2006  Yoshinori Sato
+ *
+ * Based on clock-sh4.c
+ *  Copyright (C) 2005  Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <asm/clock.h>
+#include <asm/freq.h>
+#include <asm/io.h>
+
+const static int pll1rate[]={1,2,3,4,6,8};
+const static int pfc_divisors[]={1,2,3,4,6,8,12};
+#define ifc_divisors pfc_divisors
+
+#if (CONFIG_SH_CLK_MD == 2)
+#define PLL2 (4)
+#elif (CONFIG_SH_CLK_MD == 6)
+#define PLL2 (2)
+#elif (CONFIG_SH_CLK_MD == 7)
+#define PLL2 (1)
+#else
+#error "Illigal Clock Mode!"
+#endif
+
+static void master_clk_init(struct clk *clk)
+{
+	clk->rate *= PLL2 * pll1rate[(ctrl_inw(FREQCR) >> 8) & 0x0007];
+}
+
+static struct clk_ops sh7206_master_clk_ops = {
+	.init		= master_clk_init,
+};
+
+static void module_clk_recalc(struct clk *clk)
+{
+	int idx = (ctrl_inw(FREQCR) & 0x0007);
+	clk->rate = clk->parent->rate / pfc_divisors[idx];
+}
+
+static struct clk_ops sh7206_module_clk_ops = {
+	.recalc		= module_clk_recalc,
+};
+
+static void bus_clk_recalc(struct clk *clk)
+{
+	clk->rate = clk->parent->rate / pll1rate[(ctrl_inw(FREQCR) >> 8) & 0x0007];
+}
+
+static struct clk_ops sh7206_bus_clk_ops = {
+	.recalc		= bus_clk_recalc,
+};
+
+static void cpu_clk_recalc(struct clk *clk)
+{
+	int idx = (ctrl_inw(FREQCR) & 0x0007);
+	clk->rate = clk->parent->rate / ifc_divisors[idx];
+}
+
+static struct clk_ops sh7206_cpu_clk_ops = {
+	.recalc		= cpu_clk_recalc,
+};
+
+static struct clk_ops *sh7206_clk_ops[] = {
+	&sh7206_master_clk_ops,
+	&sh7206_module_clk_ops,
+	&sh7206_bus_clk_ops,
+	&sh7206_cpu_clk_ops,
+};
+
+void __init arch_init_clk_ops(struct clk_ops **ops, int idx)
+{
+	if (idx < ARRAY_SIZE(sh7206_clk_ops))
+		*ops = sh7206_clk_ops[idx];
+}
+
diff --git a/arch/sh/kernel/cpu/sh2a/probe.c b/arch/sh/kernel/cpu/sh2a/probe.c
new file mode 100644
index 0000000..87c6c05
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/probe.c
@@ -0,0 +1,39 @@
+/*
+ * arch/sh/kernel/cpu/sh2a/probe.c
+ *
+ * CPU Subtype Probing for SH-2A.
+ *
+ * Copyright (C) 2004, 2005 Paul Mundt
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <asm/processor.h>
+#include <asm/cache.h>
+
+int __init detect_cpu_and_cache_system(void)
+{
+	/* Just SH7206 for now .. */
+	cpu_data->type			= CPU_SH7206;
+
+	cpu_data->dcache.ways		= 4;
+	cpu_data->dcache.way_incr	= (1 << 11);
+	cpu_data->dcache.sets		= 128;
+	cpu_data->dcache.entry_shift	= 4;
+	cpu_data->dcache.linesz		= L1_CACHE_BYTES;
+	cpu_data->dcache.flags		= 0;
+
+	/*
+	 * The icache is the same as the dcache as far as this setup is
+	 * concerned. The only real difference in hardware is that the icache
+	 * lacks the U bit that the dcache has, none of this has any bearing
+	 * on the cache info.
+	 */
+	cpu_data->icache		= cpu_data->dcache;
+
+	return 0;
+}
+
diff --git a/arch/sh/kernel/cpu/sh2a/setup-sh7206.c b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
new file mode 100644
index 0000000..cdfeef49
--- /dev/null
+++ b/arch/sh/kernel/cpu/sh2a/setup-sh7206.c
@@ -0,0 +1,58 @@
+/*
+ * SH7206 Setup
+ *
+ *  Copyright (C) 2006  Yoshinori Sato
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/serial.h>
+#include <asm/sci.h>
+
+static struct plat_sci_port sci_platform_data[] = {
+	{
+		.mapbase	= 0xfffe8000,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 240, 241, 242, 243},
+	}, {
+		.mapbase	= 0xfffe8800,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 244, 245, 246, 247},
+	}, {
+		.mapbase	= 0xfffe9000,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 248, 249, 250, 251},
+	}, {
+		.mapbase	= 0xfffe9800,
+		.flags		= UPF_BOOT_AUTOCONF,
+		.type		= PORT_SCIF,
+		.irqs		=  { 252, 253, 254, 255},
+	}, {
+		.flags = 0,
+	}
+};
+
+static struct platform_device sci_device = {
+	.name		= "sh-sci",
+	.id		= -1,
+	.dev		= {
+		.platform_data	= sci_platform_data,
+	},
+};
+
+static struct platform_device *sh7206_devices[] __initdata = {
+	&sci_device,
+};
+
+static int __init sh7206_devices_setup(void)
+{
+	return platform_add_devices(sh7206_devices,
+				    ARRAY_SIZE(sh7206_devices));
+}
+__initcall(sh7206_devices_setup);
diff --git a/arch/sh/kernel/setup.c b/arch/sh/kernel/setup.c
index 36d86f9..c24f639 100644
--- a/arch/sh/kernel/setup.c
+++ b/arch/sh/kernel/setup.c
@@ -404,6 +404,7 @@
 	[CPU_SH4_202]	= "SH4-202",	[CPU_SH4_501]	= "SH4-501",
 	[CPU_SH7770]	= "SH7770",	[CPU_SH7780]	= "SH7780",
 	[CPU_SH7781]	= "SH7781",	[CPU_SH7343]	= "SH7343",
+	[CPU_SH7206]	= "SH7206",	[CPU_SH7619]	= "SH7619",
 	[CPU_SH_NONE]	= "Unknown"
 };
 
diff --git a/arch/sh/kernel/signal.c b/arch/sh/kernel/signal.c
index 5213f5b..764886b 100644
--- a/arch/sh/kernel/signal.c
+++ b/arch/sh/kernel/signal.c
@@ -98,7 +98,11 @@
  */
 
 #define MOVW(n)	 (0x9300|((n)-2))	/* Move mem word at PC+n to R3 */
-#define TRAP16	 0xc310			/* Syscall w/no args (NR in R3) */
+#if defined(CONFIG_CPU_SH2) || defined(CONFIG_CPU_SH2A)
+#define TRAP_NOARG 0xc320		/* Syscall w/no args (NR in R3) */
+#else
+#define TRAP_NOARG 0xc310		/* Syscall w/no args (NR in R3) */
+#endif
 #define OR_R0_R0 0x200b			/* or r0,r0 (insert to avoid hardware bug) */
 
 struct sigframe
@@ -350,7 +354,7 @@
 	} else {
 		/* Generate return code (system call to sigreturn) */
 		err |= __put_user(MOVW(7), &frame->retcode[0]);
-		err |= __put_user(TRAP16, &frame->retcode[1]);
+		err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
 		err |= __put_user(OR_R0_R0, &frame->retcode[2]);
 		err |= __put_user(OR_R0_R0, &frame->retcode[3]);
 		err |= __put_user(OR_R0_R0, &frame->retcode[4]);
@@ -430,7 +434,7 @@
 	} else {
 		/* Generate return code (system call to rt_sigreturn) */
 		err |= __put_user(MOVW(7), &frame->retcode[0]);
-		err |= __put_user(TRAP16, &frame->retcode[1]);
+		err |= __put_user(TRAP_NOARG, &frame->retcode[1]);
 		err |= __put_user(OR_R0_R0, &frame->retcode[2]);
 		err |= __put_user(OR_R0_R0, &frame->retcode[3]);
 		err |= __put_user(OR_R0_R0, &frame->retcode[4]);
diff --git a/arch/sh/kernel/timers/Makefile b/arch/sh/kernel/timers/Makefile
index 151a6a3..bcf244f 100644
--- a/arch/sh/kernel/timers/Makefile
+++ b/arch/sh/kernel/timers/Makefile
@@ -5,4 +5,6 @@
 obj-y	:= timer.o
 
 obj-$(CONFIG_SH_TMU)		+= timer-tmu.o
+obj-$(CONFIG_SH_MTU2)		+= timer-mtu2.o
+obj-$(CONFIG_SH_CMT)		+= timer-cmt.o
 
diff --git a/arch/sh/kernel/timers/timer-cmt.c b/arch/sh/kernel/timers/timer-cmt.c
new file mode 100644
index 0000000..9eab395
--- /dev/null
+++ b/arch/sh/kernel/timers/timer-cmt.c
@@ -0,0 +1,256 @@
+/*
+ * arch/sh/kernel/timers/timer-cmt.c - CMT Timer Support
+ *
+ *  Copyright (C) 2005  Yoshinori Sato
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/seqlock.h>
+#include <asm/timer.h>
+#include <asm/rtc.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/clock.h>
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7619)
+#define CMT_CMSTR	0xf84a0070
+#define CMT_CMCSR_0	0xf84a0072
+#define CMT_CMCNT_0	0xf84a0074
+#define CMT_CMCOR_0	0xf84a0076
+#define CMT_CMCSR_1	0xf84a0078
+#define CMT_CMCNT_1	0xf84a007a
+#define CMT_CMCOR_1	0xf84a007c
+
+#define STBCR3		0xf80a0000
+#define cmt_clock_enable() do {	ctrl_outb(ctrl_inb(STBCR3) & ~0x10, STBCR3); } while(0)
+#define CMT_CMCSR_INIT	0x0040
+#define CMT_CMCSR_CALIB	0x0000
+#elif defined(CONFIG_CPU_SUBTYPE_SH7206)
+#define CMT_CMSTR	0xfffec000
+#define CMT_CMCSR_0	0xfffec002
+#define CMT_CMCNT_0	0xfffec004
+#define CMT_CMCOR_0	0xfffec006
+
+#define STBCR4		0xfffe040c
+#define cmt_clock_enable() do {	ctrl_outb(ctrl_inb(STBCR4) & ~0x04, STBCR4); } while(0)
+#define CMT_CMCSR_INIT	0x0040
+#define CMT_CMCSR_CALIB	0x0000
+#else
+#error "Unknown CPU SUBTYPE"
+#endif
+
+static DEFINE_SPINLOCK(cmt0_lock);
+
+static unsigned long cmt_timer_get_offset(void)
+{
+	int count;
+	unsigned long flags;
+
+	static unsigned short count_p = 0xffff;    /* for the first call after boot */
+	static unsigned long jiffies_p = 0;
+
+	/*
+	 * cache volatile jiffies temporarily; we have IRQs turned off.
+	 */
+	unsigned long jiffies_t;
+
+	spin_lock_irqsave(&cmt0_lock, flags);
+	/* timer count may underflow right here */
+	count =  ctrl_inw(CMT_CMCOR_0);
+	count -= ctrl_inw(CMT_CMCNT_0);
+
+	jiffies_t = jiffies;
+
+	/*
+	 * avoiding timer inconsistencies (they are rare, but they happen)...
+	 * there is one kind of problem that must be avoided here:
+	 *  1. the timer counter underflows
+	 */
+
+	if (jiffies_t == jiffies_p) {
+		if (count > count_p) {
+			/* the nutcase */
+			if (ctrl_inw(CMT_CMCSR_0) & 0x80) { /* Check CMF bit */
+				count -= LATCH;
+			} else {
+				printk("%s (): hardware timer problem?\n",
+				       __FUNCTION__);
+			}
+		}
+	} else
+		jiffies_p = jiffies_t;
+
+	count_p = count;
+	spin_unlock_irqrestore(&cmt0_lock, flags);
+
+	count = ((LATCH-1) - count) * TICK_SIZE;
+	count = (count + LATCH/2) / LATCH;
+
+	return count;
+}
+
+static irqreturn_t cmt_timer_interrupt(int irq, void *dev_id,
+				       struct pt_regs *regs)
+{
+	unsigned long timer_status;
+
+	/* Clear CMF bit */
+	timer_status = ctrl_inw(CMT_CMCSR_0);
+	timer_status &= ~0x80;
+	ctrl_outw(timer_status, CMT_CMCSR_0);
+
+	/*
+	 * Here we are in the timer irq handler. We just have irqs locally
+	 * disabled but we don't know if the timer_bh is running on the other
+	 * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+	 * the irq version of write_lock because as just said we have irq
+	 * locally disabled. -arca
+	 */
+	write_seqlock(&xtime_lock);
+	handle_timer_tick(regs);
+	write_sequnlock(&xtime_lock);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction cmt_irq = {
+	.name		= "timer",
+	.handler	= cmt_timer_interrupt,
+	.flags		= SA_INTERRUPT,
+	.mask		= CPU_MASK_NONE,
+};
+
+/*
+ * Hah!  We'll see if this works (switching from usecs to nsecs).
+ */
+static unsigned long cmt_timer_get_frequency(void)
+{
+	u32 freq;
+	struct timespec ts1, ts2;
+	unsigned long diff_nsec;
+	unsigned long factor;
+
+	/* Setup the timer:  We don't want to generate interrupts, just
+	 * have it count down at its natural rate.
+	 */
+	
+	ctrl_outw(ctrl_inw(CMT_CMSTR) & ~0x01, CMT_CMSTR);
+	ctrl_outw(CMT_CMCSR_CALIB, CMT_CMCSR_0);
+	ctrl_outw(0xffff, CMT_CMCOR_0);
+	ctrl_outw(0xffff, CMT_CMCNT_0);
+
+	rtc_sh_get_time(&ts2);
+
+	do {
+		rtc_sh_get_time(&ts1);
+	} while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+	/* actually start the timer */
+	ctrl_outw(ctrl_inw(CMT_CMSTR) | 0x01, CMT_CMSTR);
+
+	do {
+		rtc_sh_get_time(&ts2);
+	} while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+	freq = 0xffff - ctrl_inw(CMT_CMCNT_0);
+	if (ts2.tv_nsec < ts1.tv_nsec) {
+		ts2.tv_nsec += 1000000000;
+		ts2.tv_sec--;
+	}
+
+	diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
+
+	/* this should work well if the RTC has a precision of n Hz, where
+	 * n is an integer.  I don't think we have to worry about the other
+	 * cases. */
+	factor = (1000000000 + diff_nsec/2) / diff_nsec;
+
+	if (factor * diff_nsec > 1100000000 ||
+	    factor * diff_nsec <  900000000)
+		panic("weird RTC (diff_nsec %ld)", diff_nsec);
+
+	return freq * factor;
+}
+
+static void cmt_clk_init(struct clk *clk)
+{
+	u8 divisor = CMT_CMCSR_INIT & 0x3;
+	ctrl_inw(CMT_CMCSR_0);
+	ctrl_outw(CMT_CMCSR_INIT, CMT_CMCSR_0);
+	clk->parent = clk_get("module_clk");
+	clk->rate = clk->parent->rate / (8 << (divisor << 1));
+}
+
+static void cmt_clk_recalc(struct clk *clk)
+{
+	u8 divisor = ctrl_inw(CMT_CMCSR_0) & 0x3;
+	clk->rate = clk->parent->rate / (8 << (divisor << 1));
+}
+
+static struct clk_ops cmt_clk_ops = {
+	.init		= cmt_clk_init,
+	.recalc		= cmt_clk_recalc,
+};
+
+static struct clk cmt0_clk = {
+	.name		= "cmt0_clk",
+	.ops		= &cmt_clk_ops,
+};
+
+static int cmt_timer_start(void)
+{
+	ctrl_outw(ctrl_inw(CMT_CMSTR) | 0x01, CMT_CMSTR);
+	return 0;
+}
+
+static int cmt_timer_stop(void)
+{
+	ctrl_outw(ctrl_inw(CMT_CMSTR) & ~0x01, CMT_CMSTR);
+	return 0;
+}
+
+static int cmt_timer_init(void)
+{
+	unsigned long interval;
+
+	cmt_clock_enable();
+
+	setup_irq(TIMER_IRQ, &cmt_irq);
+
+	cmt0_clk.parent = clk_get("module_clk");
+
+	cmt_timer_stop();
+
+	interval = cmt0_clk.parent->rate / 8 / HZ;
+	printk(KERN_INFO "Interval = %ld\n", interval);
+
+	ctrl_outw(interval, CMT_CMCOR_0);
+
+	clk_register(&cmt0_clk);
+	clk_enable(&cmt0_clk);
+
+	cmt_timer_start();
+
+	return 0;
+}
+
+struct sys_timer_ops cmt_timer_ops = {
+	.init		= cmt_timer_init,
+	.start		= cmt_timer_start,
+	.stop		= cmt_timer_stop,
+	.get_frequency	= cmt_timer_get_frequency,
+	.get_offset	= cmt_timer_get_offset,
+};
+
+struct sys_timer cmt_timer = {
+	.name	= "cmt",
+	.ops	= &cmt_timer_ops,
+};
+
diff --git a/arch/sh/kernel/timers/timer-mtu2.c b/arch/sh/kernel/timers/timer-mtu2.c
new file mode 100644
index 0000000..73a5ef3
--- /dev/null
+++ b/arch/sh/kernel/timers/timer-mtu2.c
@@ -0,0 +1,260 @@
+/*
+ * arch/sh/kernel/timers/timer-mtu2.c - MTU2 Timer Support
+ *
+ *  Copyright (C) 2005  Paul Mundt
+ *
+ * Based off of arch/sh/kernel/timers/timer-tmu.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/seqlock.h>
+#include <asm/timer.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/clock.h>
+
+/*
+ * We use channel 1 for our lowly system timer. Channel 2 would be the other
+ * likely candidate, but we leave it alone as it has higher divisors that
+ * would be of more use to other more interesting applications.
+ *
+ * TODO: Presently we only implement a 16-bit single-channel system timer.
+ * However, we can implement channel cascade if we go the overflow route and
+ * get away with using 2 MTU2 channels as a 32-bit timer.
+ */
+
+static DEFINE_SPINLOCK(mtu2_lock);
+
+#define MTU2_TSTR	0xfffe4280
+#define MTU2_TCR_1	0xfffe4380
+#define MTU2_TMDR_1	0xfffe4381
+#define MTU2_TIOR_1	0xfffe4382
+#define MTU2_TIER_1	0xfffe4384
+#define MTU2_TSR_1	0xfffe4385
+#define MTU2_TCNT_1	0xfffe4386	/* 16-bit counter */
+#define MTU2_TGRA_1	0xfffe438a
+
+#define STBCR3		0xfffe0408
+
+#define MTU2_TSTR_CST1	(1 << 1)	/* Counter Start 1 */
+
+#define MTU2_TSR_TGFA	(1 << 0)	/* GRA compare match */
+
+#define MTU2_TIER_TGIEA	(1 << 0)	/* GRA compare match  interrupt enable */
+
+#define MTU2_TCR_INIT	0x22
+
+#define MTU2_TCR_CALIB  0x00
+
+static unsigned long mtu2_timer_get_offset(void)
+{
+	int count;
+	unsigned long flags;
+
+	static int count_p = 0x7fff;	/* for the first call after boot */
+	static unsigned long jiffies_p = 0;
+
+	/*
+	 * cache volatile jiffies temporarily; we have IRQs turned off.
+	 */
+	unsigned long jiffies_t;
+
+	spin_lock_irqsave(&mtu2_lock, flags);
+	/* timer count may underflow right here */
+	count = ctrl_inw(MTU2_TCNT_1);	/* read the latched count */
+
+	jiffies_t = jiffies;
+
+	/*
+	 * avoiding timer inconsistencies (they are rare, but they happen)...
+	 * there is one kind of problem that must be avoided here:
+	 *  1. the timer counter underflows
+	 */
+
+	if (jiffies_t == jiffies_p) {
+		if (count > count_p) {
+			if (ctrl_inb(MTU2_TSR_1) & MTU2_TSR_TGFA) {
+				count -= LATCH;
+			} else {
+				printk("%s (): hardware timer problem?\n",
+				       __FUNCTION__);
+			}
+		}
+	} else
+		jiffies_p = jiffies_t;
+
+	count_p = count;
+	spin_unlock_irqrestore(&mtu2_lock, flags);
+
+	count = ((LATCH-1) - count) * TICK_SIZE;
+	count = (count + LATCH/2) / LATCH;
+
+	return count;
+}
+
+static irqreturn_t mtu2_timer_interrupt(int irq, void *dev_id,
+				       struct pt_regs *regs)
+{
+	unsigned long timer_status;
+
+	/* Clear TGFA bit */
+	timer_status = ctrl_inb(MTU2_TSR_1);
+	timer_status &= ~MTU2_TSR_TGFA;
+	ctrl_outb(timer_status, MTU2_TSR_1);
+
+	/* Do timer tick */
+	write_seqlock(&xtime_lock);
+	handle_timer_tick(regs);
+	write_sequnlock(&xtime_lock);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction mtu2_irq = {
+	.name		= "timer",
+	.handler	= mtu2_timer_interrupt,
+	.flags		= SA_INTERRUPT,
+	.mask		= CPU_MASK_NONE,
+};
+
+/*
+ * Hah!  We'll see if this works (switching from usecs to nsecs).
+ */
+static unsigned long mtu2_timer_get_frequency(void)
+{
+	u32 freq;
+	struct timespec ts1, ts2;
+	unsigned long diff_nsec;
+	unsigned long factor;
+
+	/* Setup the timer:  We don't want to generate interrupts, just
+	 * have it count down at its natural rate.
+	 */
+	
+	ctrl_outb(ctrl_inb(MTU2_TSTR) & ~MTU2_TSTR_CST1, MTU2_TSTR);
+	ctrl_outb(MTU2_TCR_CALIB, MTU2_TCR_1);
+	ctrl_outb(ctrl_inb(MTU2_TIER_1) & ~MTU2_TIER_TGIEA, MTU2_TIER_1);
+	ctrl_outw(0, MTU2_TCNT_1);
+
+	rtc_get_time(&ts2);
+
+	do {
+		rtc_get_time(&ts1);
+	} while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+	/* actually start the timer */
+	ctrl_outw(ctrl_inw(CMT_CMSTR) | 0x01, CMT_CMSTR);
+
+	do {
+		rtc_get_time(&ts2);
+	} while (ts1.tv_nsec == ts2.tv_nsec && ts1.tv_sec == ts2.tv_sec);
+
+	freq = ctrl_inw(MTU2_TCNT_0);
+	if (ts2.tv_nsec < ts1.tv_nsec) {
+		ts2.tv_nsec += 1000000000;
+		ts2.tv_sec--;
+	}
+
+	diff_nsec = (ts2.tv_sec - ts1.tv_sec) * 1000000000 + (ts2.tv_nsec - ts1.tv_nsec);
+
+	/* this should work well if the RTC has a precision of n Hz, where
+	 * n is an integer.  I don't think we have to worry about the other
+	 * cases. */
+	factor = (1000000000 + diff_nsec/2) / diff_nsec;
+
+	if (factor * diff_nsec > 1100000000 ||
+	    factor * diff_nsec <  900000000)
+		panic("weird RTC (diff_nsec %ld)", diff_nsec);
+
+	return freq * factor;
+}
+
+static unsigned int divisors[] = { 1, 4, 16, 64, 1, 1, 256 };
+
+static void mtu2_clk_init(struct clk *clk)
+{
+	u8 idx = MTU2_TCR_INIT & 0x7;
+
+	clk->rate = clk->parent->rate / divisors[idx];
+	/* Start TCNT counting */
+	ctrl_outb(ctrl_inb(MTU2_TSTR) | MTU2_TSTR_CST1, MTU2_TSTR);
+
+}
+
+static void mtu2_clk_recalc(struct clk *clk)
+{
+	u8 idx = ctrl_inb(MTU2_TCR_1) & 0x7;
+	clk->rate = clk->parent->rate / divisors[idx];
+}
+
+static struct clk_ops mtu2_clk_ops = {
+	.init		= mtu2_clk_init,
+	.recalc		= mtu2_clk_recalc,
+};
+
+static struct clk mtu2_clk1 = {
+	.name		= "mtu2_clk1",
+	.ops		= &mtu2_clk_ops,
+};
+
+static int mtu2_timer_start(void)
+{
+	ctrl_outb(ctrl_inb(MTU2_TSTR) | MTU2_TSTR_CST1, MTU2_TSTR);
+	return 0;
+}
+
+static int mtu2_timer_stop(void)
+{
+	ctrl_outb(ctrl_inb(MTU2_TSTR) & ~MTU2_TSTR_CST1, MTU2_TSTR);
+	return 0;
+}
+
+static int mtu2_timer_init(void)
+{
+	u8 tmp;
+	unsigned long interval;
+
+	setup_irq(TIMER_IRQ, &mtu2_irq);
+
+	mtu2_clk1.parent = clk_get("module_clk");
+
+	ctrl_outb(ctrl_inb(STBCR3) & (~0x20), STBCR3);
+
+	/* Normal operation */
+	ctrl_outb(0, MTU2_TMDR_1);
+	ctrl_outb(MTU2_TCR_INIT, MTU2_TCR_1);
+	ctrl_outb(0x01, MTU2_TIOR_1);
+
+	/* Enable underflow interrupt */
+	ctrl_outb(ctrl_inb(MTU2_TIER_1) | MTU2_TIER_TGIEA, MTU2_TIER_1);
+
+	interval = CONFIG_SH_PCLK_FREQ / 16 / HZ;
+	printk(KERN_INFO "Interval = %ld\n", interval);
+
+	ctrl_outw(interval, MTU2_TGRA_1);
+	ctrl_outw(0, MTU2_TCNT_1);
+
+	clk_register(&mtu2_clk1);
+	clk_enable(&mtu2_clk1);
+
+	return 0;
+}
+
+struct sys_timer_ops mtu2_timer_ops = {
+	.init		= mtu2_timer_init,
+	.start		= mtu2_timer_start,
+	.stop		= mtu2_timer_stop,
+	.get_frequency	= mtu2_timer_get_frequency,
+	.get_offset	= mtu2_timer_get_offset,
+};
+
+struct sys_timer mtu2_timer = {
+	.name	= "mtu2",
+	.ops	= &mtu2_timer_ops,
+};
diff --git a/arch/sh/kernel/timers/timer.c b/arch/sh/kernel/timers/timer.c
index dc1f631..a6bcc91 100644
--- a/arch/sh/kernel/timers/timer.c
+++ b/arch/sh/kernel/timers/timer.c
@@ -17,6 +17,12 @@
 #ifdef CONFIG_SH_TMU
 	&tmu_timer,
 #endif
+#ifdef CONFIG_SH_MTU2
+	&mtu2_timer,
+#endif
+#ifdef CONFIG_SH_CMT
+	&cmt_timer,
+#endif
 	NULL,
 };