Browse Source

Merge tag 'mct-exynos-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung into next/drivers

From Kukjin Kim <kgene.kim@samsung.com>:

add support exynos mct device tree and move into drivers/clocksource

* tag 'mct-exynos-for-v3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/kgene/linux-samsung:
  clocksource: mct: Add terminating entry for exynos_mct_ids table
  clocksource: mct: Add missing semicolons in exynos_mct.c
  ARM: EXYNOS: move mct driver to drivers/clocksource
  ARM: EXYNOS: remove static io-remapping of mct registers for Exynos5
  ARM: dts: add mct device tree node for all supported Exynos SoC's
  ARM: EXYNOS: allow dt based discovery of mct controller using clocksource_of_init
  ARM: EXYNOS: add device tree support for MCT controller driver
  ARM: EXYNOS: prepare an array of MCT interrupt numbers and use it
  ARM: EXYNOS: add a register base address variable in mct controller driver

Conflicts:
	drivers/clocksource/Makefile
	drivers/clocksource/exynos_mct.c

[arnd: adapt to CLOCKSOURCE_OF_DECLARE interface change]

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Arnd Bergmann 12 years ago
parent
commit
228e3023eb

+ 68 - 0
Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt

@@ -0,0 +1,68 @@
+Samsung's Multi Core Timer (MCT)
+
+The Samsung's Multi Core Timer (MCT) module includes two main blocks, the
+global timer and CPU local timers. The global timer is a 64-bit free running
+up-counter and can generate 4 interrupts when the counter reaches one of the
+four preset counter values. The CPU local timers are 32-bit free running
+down-counters and generate an interrupt when the counter expires. There is
+one CPU local timer instantiated in MCT for every CPU in the system.
+
+Required properties:
+
+- compatible: should be "samsung,exynos4210-mct".
+  (a) "samsung,exynos4210-mct", for mct compatible with Exynos4210 mct.
+  (b) "samsung,exynos4412-mct", for mct compatible with Exynos4412 mct.
+
+- reg: base address of the mct controller and length of the address space
+  it occupies.
+
+- interrupts: the list of interrupts generated by the controller. The following
+  should be the order of the interrupts specified. The local timer interrupts
+  should be specified after the four global timer interrupts have been
+  specified.
+
+	0: Global Timer Interrupt 0
+	1: Global Timer Interrupt 1
+	2: Global Timer Interrupt 2
+	3: Global Timer Interrupt 3
+	4: Local Timer Interrupt 0
+	5: Local Timer Interrupt 1
+	6: ..
+	7: ..
+	i: Local Timer Interrupt n
+
+Example 1: In this example, the system uses only the first global timer
+	   interrupt generated by MCT and the remaining three global timer
+	   interrupts are unused. Two local timer interrupts have been
+	   specified.
+
+	mct@10050000 {
+		compatible = "samsung,exynos4210-mct";
+		reg = <0x10050000 0x800>;
+		interrupts = <0 57 0>, <0 0 0>, <0 0 0>, <0 0 0>,
+			     <0 42 0>, <0 48 0>;
+	};
+
+Example 2: In this example, the MCT global and local timer interrupts are
+	   connected to two seperate interrupt controllers. Hence, an
+	   interrupt-map is created to map the interrupts to the respective
+	   interrupt controllers.
+
+	mct@101C0000 {
+		compatible = "samsung,exynos4210-mct";
+		reg = <0x101C0000 0x800>;
+		interrupt-controller;
+		#interrups-cells = <2>;
+		interrupt-parent = <&mct_map>;
+		interrupts = <0 0>, <1 0>, <2 0>, <3 0>,
+			     <4 0>, <5 0>;
+
+		mct_map: mct-map {
+			#interrupt-cells = <2>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = <0x0 0 &combiner 23 3>,
+					<0x4 0 &gic 0 120 0>,
+					<0x5 0 &gic 0 121 0>;
+		};
+	};

+ 1 - 1
arch/arm/Kconfig

@@ -1654,7 +1654,7 @@ config LOCAL_TIMERS
 	bool "Use local timer interrupts"
 	bool "Use local timer interrupts"
 	depends on SMP
 	depends on SMP
 	default y
 	default y
-	select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_MCT)
+	select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !CLKSRC_EXYNOS_MCT)
 	help
 	help
 	  Enable support for local timers on SMP platforms, rather then the
 	  Enable support for local timers on SMP platforms, rather then the
 	  legacy IPI broadcast method.  Local timers allows the system
 	  legacy IPI broadcast method.  Local timers allows the system

+ 22 - 0
arch/arm/boot/dts/exynos4210.dtsi

@@ -47,6 +47,28 @@
 			     <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>;
 			     <0 12 0>, <0 13 0>, <0 14 0>, <0 15 0>;
 	};
 	};
 
 
+	mct@10050000 {
+		compatible = "samsung,exynos4210-mct";
+		reg = <0x10050000 0x800>;
+		interrupt-controller;
+		#interrups-cells = <2>;
+		interrupt-parent = <&mct_map>;
+		interrupts = <0 0>, <1 0>, <2 0>, <3 0>,
+			     <4 0>, <5 0>;
+
+		mct_map: mct-map {
+			#interrupt-cells = <2>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = <0x0 0 &gic 0 57 0>,
+					<0x1 0 &gic 0 69 0>,
+					<0x2 0 &combiner 12 6>,
+					<0x3 0 &combiner 12 7>,
+					<0x4 0 &gic 0 42 0>,
+					<0x5 0 &gic 0 48 0>;
+		};
+	};
+
 	pinctrl_0: pinctrl@11400000 {
 	pinctrl_0: pinctrl@11400000 {
 		compatible = "samsung,exynos4210-pinctrl";
 		compatible = "samsung,exynos4210-pinctrl";
 		reg = <0x11400000 0x1000>;
 		reg = <0x11400000 0x1000>;

+ 22 - 0
arch/arm/boot/dts/exynos4212.dtsi

@@ -25,4 +25,26 @@
 	gic:interrupt-controller@10490000 {
 	gic:interrupt-controller@10490000 {
 		cpu-offset = <0x8000>;
 		cpu-offset = <0x8000>;
 	};
 	};
+
+	mct@10050000 {
+		compatible = "samsung,exynos4412-mct";
+		reg = <0x10050000 0x800>;
+		interrupt-controller;
+		#interrups-cells = <2>;
+		interrupt-parent = <&mct_map>;
+		interrupts = <0 0>, <1 0>, <2 0>, <3 0>,
+			     <4 0>, <5 0>;
+
+		mct_map: mct-map {
+			#interrupt-cells = <2>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = <0x0 0 &gic 0 57 0>,
+					<0x1 0 &combiner 12 5>,
+					<0x2 0 &combiner 12 6>,
+					<0x3 0 &combiner 12 7>,
+					<0x4 0 &gic 1 12 0>,
+					<0x5 0 &gic 1 12 0>;
+		};
+	};
 };
 };

+ 24 - 0
arch/arm/boot/dts/exynos4412.dtsi

@@ -25,4 +25,28 @@
 	gic:interrupt-controller@10490000 {
 	gic:interrupt-controller@10490000 {
 		cpu-offset = <0x4000>;
 		cpu-offset = <0x4000>;
 	};
 	};
+
+	mct@10050000 {
+		compatible = "samsung,exynos4412-mct";
+		reg = <0x10050000 0x800>;
+		interrupt-controller;
+		#interrups-cells = <2>;
+		interrupt-parent = <&mct_map>;
+		interrupts = <0 0>, <1 0>, <2 0>, <3 0>,
+			     <4 0>, <5 0>, <6 0>, <7 0>;
+
+		mct_map: mct-map {
+			#interrupt-cells = <2>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = <0x0 0 &gic 0 57 0>,
+					<0x1 0 &combiner 12 5>,
+					<0x2 0 &combiner 12 6>,
+					<0x3 0 &combiner 12 7>,
+					<0x4 0 &gic 1 12 0>,
+					<0x5 0 &gic 1 12 0>,
+					<0x6 0 &gic 1 12 0>,
+					<0x7 0 &gic 1 12 0>;
+		};
+	};
 };
 };

+ 22 - 0
arch/arm/boot/dts/exynos5250.dtsi

@@ -69,6 +69,28 @@
 			     <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
 			     <0 28 0>, <0 29 0>, <0 30 0>, <0 31 0>;
 	};
 	};
 
 
+	mct@101C0000 {
+		compatible = "samsung,exynos4210-mct";
+		reg = <0x101C0000 0x800>;
+		interrupt-controller;
+		#interrups-cells = <2>;
+		interrupt-parent = <&mct_map>;
+		interrupts = <0 0>, <1 0>, <2 0>, <3 0>,
+			     <4 0>, <5 0>;
+
+		mct_map: mct-map {
+			#interrupt-cells = <2>;
+			#address-cells = <0>;
+			#size-cells = <0>;
+			interrupt-map = <0x0 0 &combiner 23 3>,
+					<0x1 0 &combiner 23 4>,
+					<0x2 0 &combiner 25 2>,
+					<0x3 0 &combiner 25 3>,
+					<0x4 0 &gic 0 120 0>,
+					<0x5 0 &gic 0 121 0>;
+		};
+	};
+
 	watchdog {
 	watchdog {
 		compatible = "samsung,s3c2410-wdt";
 		compatible = "samsung,s3c2410-wdt";
 		reg = <0x101D0000 0x100>;
 		reg = <0x101D0000 0x100>;

+ 2 - 6
arch/arm/mach-exynos/Kconfig

@@ -79,12 +79,6 @@ config SOC_EXYNOS5440
 	help
 	help
 	  Enable EXYNOS5440 SoC support
 	  Enable EXYNOS5440 SoC support
 
 
-config EXYNOS4_MCT
-	bool
-	default y
-	help
-	  Use MCT (Multi Core Timer) as kernel timers
-
 config EXYNOS_DEV_DMA
 config EXYNOS_DEV_DMA
 	bool
 	bool
 	help
 	help
@@ -406,6 +400,7 @@ config MACH_EXYNOS4_DT
 	bool "Samsung Exynos4 Machine using device tree"
 	bool "Samsung Exynos4 Machine using device tree"
 	depends on ARCH_EXYNOS4
 	depends on ARCH_EXYNOS4
 	select ARM_AMBA
 	select ARM_AMBA
+	select CLKSRC_OF
 	select CPU_EXYNOS4210
 	select CPU_EXYNOS4210
 	select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD
 	select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD
 	select PINCTRL
 	select PINCTRL
@@ -422,6 +417,7 @@ config MACH_EXYNOS5_DT
 	default y
 	default y
 	depends on ARCH_EXYNOS5
 	depends on ARCH_EXYNOS5
 	select ARM_AMBA
 	select ARM_AMBA
+	select CLKSRC_OF
 	select USE_OF
 	select USE_OF
 	help
 	help
 	  Machine support for Samsung EXYNOS5 machine with device tree enabled.
 	  Machine support for Samsung EXYNOS5 machine with device tree enabled.

+ 0 - 2
arch/arm/mach-exynos/Makefile

@@ -26,8 +26,6 @@ obj-$(CONFIG_ARCH_EXYNOS)	+= pmu.o
 
 
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 
 
-obj-$(CONFIG_EXYNOS4_MCT)	+= mct.o
-
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 
 
 # machine support
 # machine support

+ 0 - 5
arch/arm/mach-exynos/common.c

@@ -256,11 +256,6 @@ static struct map_desc exynos5_iodesc[] __initdata = {
 		.pfn		= __phys_to_pfn(EXYNOS5_PA_SROMC),
 		.pfn		= __phys_to_pfn(EXYNOS5_PA_SROMC),
 		.length		= SZ_4K,
 		.length		= SZ_4K,
 		.type		= MT_DEVICE,
 		.type		= MT_DEVICE,
-	}, {
-		.virtual	= (unsigned long)S5P_VA_SYSTIMER,
-		.pfn		= __phys_to_pfn(EXYNOS5_PA_SYSTIMER),
-		.length		= SZ_4K,
-		.type		= MT_DEVICE,
 	}, {
 	}, {
 		.virtual	= (unsigned long)S5P_VA_SYSRAM,
 		.virtual	= (unsigned long)S5P_VA_SYSRAM,
 		.pfn		= __phys_to_pfn(EXYNOS5_PA_SYSRAM),
 		.pfn		= __phys_to_pfn(EXYNOS5_PA_SYSRAM),

+ 1 - 1
arch/arm/mach-exynos/common.h

@@ -12,7 +12,7 @@
 #ifndef __ARCH_ARM_MACH_EXYNOS_COMMON_H
 #ifndef __ARCH_ARM_MACH_EXYNOS_COMMON_H
 #define __ARCH_ARM_MACH_EXYNOS_COMMON_H
 #define __ARCH_ARM_MACH_EXYNOS_COMMON_H
 
 
-extern void exynos4_timer_init(void);
+extern void mct_init(void);
 
 
 struct map_desc;
 struct map_desc;
 void exynos_init_io(struct map_desc *mach_desc, int size);
 void exynos_init_io(struct map_desc *mach_desc, int size);

+ 0 - 6
arch/arm/mach-exynos/include/mach/irqs.h

@@ -30,8 +30,6 @@
 
 
 /* For EXYNOS4 and EXYNOS5 */
 /* For EXYNOS4 and EXYNOS5 */
 
 
-#define EXYNOS_IRQ_MCT_LOCALTIMER	IRQ_PPI(12)
-
 #define EXYNOS_IRQ_EINT16_31		IRQ_SPI(32)
 #define EXYNOS_IRQ_EINT16_31		IRQ_SPI(32)
 
 
 /* For EXYNOS4 SoCs */
 /* For EXYNOS4 SoCs */
@@ -323,8 +321,6 @@
 #define EXYNOS5_IRQ_CEC			IRQ_SPI(114)
 #define EXYNOS5_IRQ_CEC			IRQ_SPI(114)
 #define EXYNOS5_IRQ_SATA		IRQ_SPI(115)
 #define EXYNOS5_IRQ_SATA		IRQ_SPI(115)
 
 
-#define EXYNOS5_IRQ_MCT_L0		IRQ_SPI(120)
-#define EXYNOS5_IRQ_MCT_L1		IRQ_SPI(121)
 #define EXYNOS5_IRQ_MMC44		IRQ_SPI(123)
 #define EXYNOS5_IRQ_MMC44		IRQ_SPI(123)
 #define EXYNOS5_IRQ_MDMA1		IRQ_SPI(124)
 #define EXYNOS5_IRQ_MDMA1		IRQ_SPI(124)
 #define EXYNOS5_IRQ_FIMC_LITE0		IRQ_SPI(125)
 #define EXYNOS5_IRQ_FIMC_LITE0		IRQ_SPI(125)
@@ -419,8 +415,6 @@
 #define EXYNOS5_IRQ_PMU_CPU1		COMBINER_IRQ(22, 4)
 #define EXYNOS5_IRQ_PMU_CPU1		COMBINER_IRQ(22, 4)
 
 
 #define EXYNOS5_IRQ_EINT0		COMBINER_IRQ(23, 0)
 #define EXYNOS5_IRQ_EINT0		COMBINER_IRQ(23, 0)
-#define EXYNOS5_IRQ_MCT_G0		COMBINER_IRQ(23, 3)
-#define EXYNOS5_IRQ_MCT_G1		COMBINER_IRQ(23, 4)
 
 
 #define EXYNOS5_IRQ_EINT1		COMBINER_IRQ(24, 0)
 #define EXYNOS5_IRQ_EINT1		COMBINER_IRQ(24, 0)
 #define EXYNOS5_IRQ_SYSMMU_LITE1_0	COMBINER_IRQ(24, 1)
 #define EXYNOS5_IRQ_SYSMMU_LITE1_0	COMBINER_IRQ(24, 1)

+ 0 - 1
arch/arm/mach-exynos/include/mach/map.h

@@ -65,7 +65,6 @@
 #define EXYNOS5_PA_CMU			0x10010000
 #define EXYNOS5_PA_CMU			0x10010000
 
 
 #define EXYNOS4_PA_SYSTIMER		0x10050000
 #define EXYNOS4_PA_SYSTIMER		0x10050000
-#define EXYNOS5_PA_SYSTIMER		0x101C0000
 
 
 #define EXYNOS4_PA_WATCHDOG		0x10060000
 #define EXYNOS4_PA_WATCHDOG		0x10060000
 #define EXYNOS5_PA_WATCHDOG		0x101D0000
 #define EXYNOS5_PA_WATCHDOG		0x101D0000

+ 0 - 53
arch/arm/mach-exynos/include/mach/regs-mct.h

@@ -1,53 +0,0 @@
-/* arch/arm/mach-exynos4/include/mach/regs-mct.h
- *
- * Copyright (c) 2011 Samsung Electronics Co., Ltd.
- *		http://www.samsung.com
- *
- * EXYNOS4 MCT configutation
- *
- * 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.
-*/
-
-#ifndef __ASM_ARCH_REGS_MCT_H
-#define __ASM_ARCH_REGS_MCT_H __FILE__
-
-#include <mach/map.h>
-
-#define EXYNOS4_MCTREG(x)		(S5P_VA_SYSTIMER + (x))
-
-#define EXYNOS4_MCT_G_CNT_L		EXYNOS4_MCTREG(0x100)
-#define EXYNOS4_MCT_G_CNT_U		EXYNOS4_MCTREG(0x104)
-#define EXYNOS4_MCT_G_CNT_WSTAT		EXYNOS4_MCTREG(0x110)
-
-#define EXYNOS4_MCT_G_COMP0_L		EXYNOS4_MCTREG(0x200)
-#define EXYNOS4_MCT_G_COMP0_U		EXYNOS4_MCTREG(0x204)
-#define EXYNOS4_MCT_G_COMP0_ADD_INCR	EXYNOS4_MCTREG(0x208)
-
-#define EXYNOS4_MCT_G_TCON		EXYNOS4_MCTREG(0x240)
-
-#define EXYNOS4_MCT_G_INT_CSTAT		EXYNOS4_MCTREG(0x244)
-#define EXYNOS4_MCT_G_INT_ENB		EXYNOS4_MCTREG(0x248)
-#define EXYNOS4_MCT_G_WSTAT		EXYNOS4_MCTREG(0x24C)
-
-#define _EXYNOS4_MCT_L_BASE		EXYNOS4_MCTREG(0x300)
-#define EXYNOS4_MCT_L_BASE(x)		(_EXYNOS4_MCT_L_BASE + (0x100 * x))
-#define EXYNOS4_MCT_L_MASK		(0xffffff00)
-
-#define MCT_L_TCNTB_OFFSET		(0x00)
-#define MCT_L_ICNTB_OFFSET		(0x08)
-#define MCT_L_TCON_OFFSET		(0x20)
-#define MCT_L_INT_CSTAT_OFFSET		(0x30)
-#define MCT_L_INT_ENB_OFFSET		(0x34)
-#define MCT_L_WSTAT_OFFSET		(0x40)
-
-#define MCT_G_TCON_START		(1 << 8)
-#define MCT_G_TCON_COMP0_AUTO_INC	(1 << 1)
-#define MCT_G_TCON_COMP0_ENABLE		(1 << 0)
-
-#define MCT_L_TCON_INTERVAL_MODE	(1 << 2)
-#define MCT_L_TCON_INT_START		(1 << 1)
-#define MCT_L_TCON_TIMER_START		(1 << 0)
-
-#endif /* __ASM_ARCH_REGS_MCT_H */

+ 1 - 1
arch/arm/mach-exynos/mach-armlex4210.c

@@ -202,6 +202,6 @@ MACHINE_START(ARMLEX4210, "ARMLEX4210")
 	.map_io		= armlex4210_map_io,
 	.map_io		= armlex4210_map_io,
 	.init_machine	= armlex4210_machine_init,
 	.init_machine	= armlex4210_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 MACHINE_END
 MACHINE_END

+ 2 - 1
arch/arm/mach-exynos/mach-exynos4-dt.c

@@ -13,6 +13,7 @@
 
 
 #include <linux/of_platform.h>
 #include <linux/of_platform.h>
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
+#include <linux/clocksource.h>
 
 
 #include <asm/mach/arch.h>
 #include <asm/mach/arch.h>
 #include <mach/map.h>
 #include <mach/map.h>
@@ -142,7 +143,7 @@ DT_MACHINE_START(EXYNOS4210_DT, "Samsung Exynos4 (Flattened Device Tree)")
 	.map_io		= exynos4_dt_map_io,
 	.map_io		= exynos4_dt_map_io,
 	.init_machine	= exynos4_dt_machine_init,
 	.init_machine	= exynos4_dt_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= clocksource_of_init,
 	.dt_compat	= exynos4_dt_compat,
 	.dt_compat	= exynos4_dt_compat,
 	.restart        = exynos4_restart,
 	.restart        = exynos4_restart,
 MACHINE_END
 MACHINE_END

+ 2 - 1
arch/arm/mach-exynos/mach-exynos5-dt.c

@@ -14,6 +14,7 @@
 #include <linux/serial_core.h>
 #include <linux/serial_core.h>
 #include <linux/memblock.h>
 #include <linux/memblock.h>
 #include <linux/io.h>
 #include <linux/io.h>
+#include <linux/clocksource.h>
 
 
 #include <asm/mach/arch.h>
 #include <asm/mach/arch.h>
 #include <mach/map.h>
 #include <mach/map.h>
@@ -216,7 +217,7 @@ DT_MACHINE_START(EXYNOS5_DT, "SAMSUNG EXYNOS5 (Flattened Device Tree)")
 	.map_io		= exynos5_dt_map_io,
 	.map_io		= exynos5_dt_map_io,
 	.init_machine	= exynos5_dt_machine_init,
 	.init_machine	= exynos5_dt_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= clocksource_of_init,
 	.dt_compat	= exynos5_dt_compat,
 	.dt_compat	= exynos5_dt_compat,
 	.restart        = exynos5_restart,
 	.restart        = exynos5_restart,
 	.reserve	= exynos5_reserve,
 	.reserve	= exynos5_reserve,

+ 1 - 1
arch/arm/mach-exynos/mach-nuri.c

@@ -1380,7 +1380,7 @@ MACHINE_START(NURI, "NURI")
 	.map_io		= nuri_map_io,
 	.map_io		= nuri_map_io,
 	.init_machine	= nuri_machine_init,
 	.init_machine	= nuri_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.reserve        = &nuri_reserve,
 	.reserve        = &nuri_reserve,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 MACHINE_END
 MACHINE_END

+ 1 - 1
arch/arm/mach-exynos/mach-origen.c

@@ -815,7 +815,7 @@ MACHINE_START(ORIGEN, "ORIGEN")
 	.map_io		= origen_map_io,
 	.map_io		= origen_map_io,
 	.init_machine	= origen_machine_init,
 	.init_machine	= origen_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.reserve	= &origen_reserve,
 	.reserve	= &origen_reserve,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 MACHINE_END
 MACHINE_END

+ 2 - 2
arch/arm/mach-exynos/mach-smdk4x12.c

@@ -376,7 +376,7 @@ MACHINE_START(SMDK4212, "SMDK4212")
 	.init_irq	= exynos4_init_irq,
 	.init_irq	= exynos4_init_irq,
 	.map_io		= smdk4x12_map_io,
 	.map_io		= smdk4x12_map_io,
 	.init_machine	= smdk4x12_machine_init,
 	.init_machine	= smdk4x12_machine_init,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 	.reserve	= &smdk4x12_reserve,
 	.reserve	= &smdk4x12_reserve,
 MACHINE_END
 MACHINE_END
@@ -390,7 +390,7 @@ MACHINE_START(SMDK4412, "SMDK4412")
 	.map_io		= smdk4x12_map_io,
 	.map_io		= smdk4x12_map_io,
 	.init_machine	= smdk4x12_machine_init,
 	.init_machine	= smdk4x12_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 	.reserve	= &smdk4x12_reserve,
 	.reserve	= &smdk4x12_reserve,
 MACHINE_END
 MACHINE_END

+ 2 - 2
arch/arm/mach-exynos/mach-smdkv310.c

@@ -423,7 +423,7 @@ MACHINE_START(SMDKV310, "SMDKV310")
 	.init_irq	= exynos4_init_irq,
 	.init_irq	= exynos4_init_irq,
 	.map_io		= smdkv310_map_io,
 	.map_io		= smdkv310_map_io,
 	.init_machine	= smdkv310_machine_init,
 	.init_machine	= smdkv310_machine_init,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.reserve	= &smdkv310_reserve,
 	.reserve	= &smdkv310_reserve,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 MACHINE_END
 MACHINE_END
@@ -436,7 +436,7 @@ MACHINE_START(SMDKC210, "SMDKC210")
 	.map_io		= smdkv310_map_io,
 	.map_io		= smdkv310_map_io,
 	.init_machine	= smdkv310_machine_init,
 	.init_machine	= smdkv310_machine_init,
 	.init_late	= exynos_init_late,
 	.init_late	= exynos_init_late,
-	.init_time	= exynos4_timer_init,
+	.init_time	= mct_init,
 	.reserve	= &smdkv310_reserve,
 	.reserve	= &smdkv310_reserve,
 	.restart	= exynos4_restart,
 	.restart	= exynos4_restart,
 MACHINE_END
 MACHINE_END

+ 5 - 0
drivers/clocksource/Kconfig

@@ -70,3 +70,8 @@ config CLKSRC_METAG_GENERIC
 	def_bool y if METAG
 	def_bool y if METAG
 	help
 	help
 	  This option enables support for the Meta per-thread timers.
 	  This option enables support for the Meta per-thread timers.
+
+config CLKSRC_EXYNOS_MCT
+	def_bool y if ARCH_EXYNOS
+	help
+	  Support for Multi Core Timer controller on Exynos SoCs.

+ 2 - 1
drivers/clocksource/Makefile

@@ -19,7 +19,8 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o
 obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o
 obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_timer.o
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_timer.o
 obj-$(CONFIG_VT8500_TIMER)	+= vt8500_timer.o
 obj-$(CONFIG_VT8500_TIMER)	+= vt8500_timer.o
-obj-$(CONFIG_CADENCE_TTC_TIMER)		+= cadence_ttc_timer.o
+obj-$(CONFIG_CADENCE_TTC_TIMER)	+= cadence_ttc_timer.o
+obj-$(CONFIG_CLKSRC_EXYNOS_MCT)	+= exynos_mct.o
 
 
 obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o
 obj-$(CONFIG_ARM_ARCH_TIMER)		+= arm_arch_timer.o
 obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o
 obj-$(CONFIG_CLKSRC_METAG_GENERIC)	+= metag_generic.o

+ 135 - 64
arch/arm/mach-exynos/mct.c → drivers/clocksource/exynos_mct.c

@@ -20,6 +20,9 @@
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/percpu.h>
 #include <linux/percpu.h>
 #include <linux/of.h>
 #include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/clocksource.h>
 
 
 #include <asm/arch_timer.h>
 #include <asm/arch_timer.h>
 #include <asm/localtimer.h>
 #include <asm/localtimer.h>
@@ -28,9 +31,36 @@
 
 
 #include <mach/map.h>
 #include <mach/map.h>
 #include <mach/irqs.h>
 #include <mach/irqs.h>
-#include <mach/regs-mct.h>
 #include <asm/mach/time.h>
 #include <asm/mach/time.h>
 
 
+#define EXYNOS4_MCTREG(x)		(x)
+#define EXYNOS4_MCT_G_CNT_L		EXYNOS4_MCTREG(0x100)
+#define EXYNOS4_MCT_G_CNT_U		EXYNOS4_MCTREG(0x104)
+#define EXYNOS4_MCT_G_CNT_WSTAT		EXYNOS4_MCTREG(0x110)
+#define EXYNOS4_MCT_G_COMP0_L		EXYNOS4_MCTREG(0x200)
+#define EXYNOS4_MCT_G_COMP0_U		EXYNOS4_MCTREG(0x204)
+#define EXYNOS4_MCT_G_COMP0_ADD_INCR	EXYNOS4_MCTREG(0x208)
+#define EXYNOS4_MCT_G_TCON		EXYNOS4_MCTREG(0x240)
+#define EXYNOS4_MCT_G_INT_CSTAT		EXYNOS4_MCTREG(0x244)
+#define EXYNOS4_MCT_G_INT_ENB		EXYNOS4_MCTREG(0x248)
+#define EXYNOS4_MCT_G_WSTAT		EXYNOS4_MCTREG(0x24C)
+#define _EXYNOS4_MCT_L_BASE		EXYNOS4_MCTREG(0x300)
+#define EXYNOS4_MCT_L_BASE(x)		(_EXYNOS4_MCT_L_BASE + (0x100 * x))
+#define EXYNOS4_MCT_L_MASK		(0xffffff00)
+
+#define MCT_L_TCNTB_OFFSET		(0x00)
+#define MCT_L_ICNTB_OFFSET		(0x08)
+#define MCT_L_TCON_OFFSET		(0x20)
+#define MCT_L_INT_CSTAT_OFFSET		(0x30)
+#define MCT_L_INT_ENB_OFFSET		(0x34)
+#define MCT_L_WSTAT_OFFSET		(0x40)
+#define MCT_G_TCON_START		(1 << 8)
+#define MCT_G_TCON_COMP0_AUTO_INC	(1 << 1)
+#define MCT_G_TCON_COMP0_ENABLE		(1 << 0)
+#define MCT_L_TCON_INTERVAL_MODE	(1 << 2)
+#define MCT_L_TCON_INT_START		(1 << 1)
+#define MCT_L_TCON_TIMER_START		(1 << 0)
+
 #define TICK_BASE_CNT	1
 #define TICK_BASE_CNT	1
 
 
 enum {
 enum {
@@ -38,64 +68,75 @@ enum {
 	MCT_INT_PPI
 	MCT_INT_PPI
 };
 };
 
 
+enum {
+	MCT_G0_IRQ,
+	MCT_G1_IRQ,
+	MCT_G2_IRQ,
+	MCT_G3_IRQ,
+	MCT_L0_IRQ,
+	MCT_L1_IRQ,
+	MCT_L2_IRQ,
+	MCT_L3_IRQ,
+	MCT_NR_IRQS,
+};
+
+static void __iomem *reg_base;
 static unsigned long clk_rate;
 static unsigned long clk_rate;
 static unsigned int mct_int_type;
 static unsigned int mct_int_type;
+static int mct_irqs[MCT_NR_IRQS];
 
 
 struct mct_clock_event_device {
 struct mct_clock_event_device {
 	struct clock_event_device *evt;
 	struct clock_event_device *evt;
-	void __iomem *base;
+	unsigned long base;
 	char name[10];
 	char name[10];
 };
 };
 
 
-static void exynos4_mct_write(unsigned int value, void *addr)
+static void exynos4_mct_write(unsigned int value, unsigned long offset)
 {
 {
-	void __iomem *stat_addr;
+	unsigned long stat_addr;
 	u32 mask;
 	u32 mask;
 	u32 i;
 	u32 i;
 
 
-	__raw_writel(value, addr);
+	__raw_writel(value, reg_base + offset);
 
 
-	if (likely(addr >= EXYNOS4_MCT_L_BASE(0))) {
-		u32 base = (u32) addr & EXYNOS4_MCT_L_MASK;
-		switch ((u32) addr & ~EXYNOS4_MCT_L_MASK) {
-		case (u32) MCT_L_TCON_OFFSET:
-			stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET;
+	if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
+		stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
+		switch (offset & EXYNOS4_MCT_L_MASK) {
+		case MCT_L_TCON_OFFSET:
 			mask = 1 << 3;		/* L_TCON write status */
 			mask = 1 << 3;		/* L_TCON write status */
 			break;
 			break;
-		case (u32) MCT_L_ICNTB_OFFSET:
-			stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET;
+		case MCT_L_ICNTB_OFFSET:
 			mask = 1 << 1;		/* L_ICNTB write status */
 			mask = 1 << 1;		/* L_ICNTB write status */
 			break;
 			break;
-		case (u32) MCT_L_TCNTB_OFFSET:
-			stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET;
+		case MCT_L_TCNTB_OFFSET:
 			mask = 1 << 0;		/* L_TCNTB write status */
 			mask = 1 << 0;		/* L_TCNTB write status */
 			break;
 			break;
 		default:
 		default:
 			return;
 			return;
 		}
 		}
 	} else {
 	} else {
-		switch ((u32) addr) {
-		case (u32) EXYNOS4_MCT_G_TCON:
+		switch (offset) {
+		case EXYNOS4_MCT_G_TCON:
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			mask = 1 << 16;		/* G_TCON write status */
 			mask = 1 << 16;		/* G_TCON write status */
 			break;
 			break;
-		case (u32) EXYNOS4_MCT_G_COMP0_L:
+		case EXYNOS4_MCT_G_COMP0_L:
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			mask = 1 << 0;		/* G_COMP0_L write status */
 			mask = 1 << 0;		/* G_COMP0_L write status */
 			break;
 			break;
-		case (u32) EXYNOS4_MCT_G_COMP0_U:
+		case EXYNOS4_MCT_G_COMP0_U:
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			mask = 1 << 1;		/* G_COMP0_U write status */
 			mask = 1 << 1;		/* G_COMP0_U write status */
 			break;
 			break;
-		case (u32) EXYNOS4_MCT_G_COMP0_ADD_INCR:
+		case EXYNOS4_MCT_G_COMP0_ADD_INCR:
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			stat_addr = EXYNOS4_MCT_G_WSTAT;
 			mask = 1 << 2;		/* G_COMP0_ADD_INCR w status */
 			mask = 1 << 2;		/* G_COMP0_ADD_INCR w status */
 			break;
 			break;
-		case (u32) EXYNOS4_MCT_G_CNT_L:
+		case EXYNOS4_MCT_G_CNT_L:
 			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
 			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
 			mask = 1 << 0;		/* G_CNT_L write status */
 			mask = 1 << 0;		/* G_CNT_L write status */
 			break;
 			break;
-		case (u32) EXYNOS4_MCT_G_CNT_U:
+		case EXYNOS4_MCT_G_CNT_U:
 			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
 			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
 			mask = 1 << 1;		/* G_CNT_U write status */
 			mask = 1 << 1;		/* G_CNT_U write status */
 			break;
 			break;
@@ -106,12 +147,12 @@ static void exynos4_mct_write(unsigned int value, void *addr)
 
 
 	/* Wait maximum 1 ms until written values are applied */
 	/* Wait maximum 1 ms until written values are applied */
 	for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
 	for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
-		if (__raw_readl(stat_addr) & mask) {
-			__raw_writel(mask, stat_addr);
+		if (__raw_readl(reg_base + stat_addr) & mask) {
+			__raw_writel(mask, reg_base + stat_addr);
 			return;
 			return;
 		}
 		}
 
 
-	panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
+	panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
 }
 }
 
 
 /* Clocksource handling */
 /* Clocksource handling */
@@ -122,7 +163,7 @@ static void exynos4_mct_frc_start(u32 hi, u32 lo)
 	exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
 	exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
 	exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
 	exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
 
 
-	reg = __raw_readl(EXYNOS4_MCT_G_TCON);
+	reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
 	reg |= MCT_G_TCON_START;
 	reg |= MCT_G_TCON_START;
 	exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
 	exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
 }
 }
@@ -130,12 +171,12 @@ static void exynos4_mct_frc_start(u32 hi, u32 lo)
 static cycle_t exynos4_frc_read(struct clocksource *cs)
 static cycle_t exynos4_frc_read(struct clocksource *cs)
 {
 {
 	unsigned int lo, hi;
 	unsigned int lo, hi;
-	u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
+	u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
 
 
 	do {
 	do {
 		hi = hi2;
 		hi = hi2;
-		lo = __raw_readl(EXYNOS4_MCT_G_CNT_L);
-		hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
+		lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L);
+		hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
 	} while (hi != hi2);
 	} while (hi != hi2);
 
 
 	return ((cycle_t)hi << 32) | lo;
 	return ((cycle_t)hi << 32) | lo;
@@ -167,7 +208,7 @@ static void exynos4_mct_comp0_stop(void)
 {
 {
 	unsigned int tcon;
 	unsigned int tcon;
 
 
-	tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
+	tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
 	tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
 	tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
 
 
 	exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
 	exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
@@ -180,7 +221,7 @@ static void exynos4_mct_comp0_start(enum clock_event_mode mode,
 	unsigned int tcon;
 	unsigned int tcon;
 	cycle_t comp_cycle;
 	cycle_t comp_cycle;
 
 
-	tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
+	tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
 
 
 	if (mode == CLOCK_EVT_MODE_PERIODIC) {
 	if (mode == CLOCK_EVT_MODE_PERIODIC) {
 		tcon |= MCT_G_TCON_COMP0_AUTO_INC;
 		tcon |= MCT_G_TCON_COMP0_AUTO_INC;
@@ -257,11 +298,7 @@ static void exynos4_clockevent_init(void)
 	mct_comp_device.cpumask = cpumask_of(0);
 	mct_comp_device.cpumask = cpumask_of(0);
 	clockevents_config_and_register(&mct_comp_device, clk_rate,
 	clockevents_config_and_register(&mct_comp_device, clk_rate,
 					0xf, 0xffffffff);
 					0xf, 0xffffffff);
-
-	if (soc_is_exynos5250())
-		setup_irq(EXYNOS5_IRQ_MCT_G0, &mct_comp_event_irq);
-	else
-		setup_irq(EXYNOS4_IRQ_MCT_G0, &mct_comp_event_irq);
+	setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq);
 }
 }
 
 
 #ifdef CONFIG_LOCAL_TIMERS
 #ifdef CONFIG_LOCAL_TIMERS
@@ -273,12 +310,12 @@ static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
 {
 {
 	unsigned long tmp;
 	unsigned long tmp;
 	unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
 	unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
-	void __iomem *addr = mevt->base + MCT_L_TCON_OFFSET;
+	unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
 
 
-	tmp = __raw_readl(addr);
+	tmp = __raw_readl(reg_base + offset);
 	if (tmp & mask) {
 	if (tmp & mask) {
 		tmp &= ~mask;
 		tmp &= ~mask;
-		exynos4_mct_write(tmp, addr);
+		exynos4_mct_write(tmp, offset);
 	}
 	}
 }
 }
 
 
@@ -297,7 +334,7 @@ static void exynos4_mct_tick_start(unsigned long cycles,
 	/* enable MCT tick interrupt */
 	/* enable MCT tick interrupt */
 	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
 	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
 
 
-	tmp = __raw_readl(mevt->base + MCT_L_TCON_OFFSET);
+	tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET);
 	tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
 	tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
 	       MCT_L_TCON_INTERVAL_MODE;
 	       MCT_L_TCON_INTERVAL_MODE;
 	exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
 	exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
@@ -349,7 +386,7 @@ static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
 		exynos4_mct_tick_stop(mevt);
 		exynos4_mct_tick_stop(mevt);
 
 
 	/* Clear the MCT tick interrupt */
 	/* Clear the MCT tick interrupt */
-	if (__raw_readl(mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
+	if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
 		exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
 		exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
 		return 1;
 		return 1;
 	} else {
 	} else {
@@ -385,7 +422,6 @@ static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
 {
 {
 	struct mct_clock_event_device *mevt;
 	struct mct_clock_event_device *mevt;
 	unsigned int cpu = smp_processor_id();
 	unsigned int cpu = smp_processor_id();
-	int mct_lx_irq;
 
 
 	mevt = this_cpu_ptr(&percpu_mct_tick);
 	mevt = this_cpu_ptr(&percpu_mct_tick);
 	mevt->evt = evt;
 	mevt->evt = evt;
@@ -406,21 +442,17 @@ static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
 
 
 	if (mct_int_type == MCT_INT_SPI) {
 	if (mct_int_type == MCT_INT_SPI) {
 		if (cpu == 0) {
 		if (cpu == 0) {
-			mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L0 :
-						EXYNOS5_IRQ_MCT_L0;
 			mct_tick0_event_irq.dev_id = mevt;
 			mct_tick0_event_irq.dev_id = mevt;
-			evt->irq = mct_lx_irq;
-			setup_irq(mct_lx_irq, &mct_tick0_event_irq);
+			evt->irq = mct_irqs[MCT_L0_IRQ];
+			setup_irq(evt->irq, &mct_tick0_event_irq);
 		} else {
 		} else {
-			mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L1 :
-						EXYNOS5_IRQ_MCT_L1;
 			mct_tick1_event_irq.dev_id = mevt;
 			mct_tick1_event_irq.dev_id = mevt;
-			evt->irq = mct_lx_irq;
-			setup_irq(mct_lx_irq, &mct_tick1_event_irq);
-			irq_set_affinity(mct_lx_irq, cpumask_of(1));
+			evt->irq = mct_irqs[MCT_L1_IRQ];
+			setup_irq(evt->irq, &mct_tick1_event_irq);
+			irq_set_affinity(evt->irq, cpumask_of(1));
 		}
 		}
 	} else {
 	} else {
-		enable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER, 0);
+		enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -436,7 +468,7 @@ static void exynos4_local_timer_stop(struct clock_event_device *evt)
 		else
 		else
 			remove_irq(evt->irq, &mct_tick1_event_irq);
 			remove_irq(evt->irq, &mct_tick1_event_irq);
 	else
 	else
-		disable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER);
+		disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
 }
 }
 
 
 static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
 static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
@@ -445,41 +477,80 @@ static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
 };
 };
 #endif /* CONFIG_LOCAL_TIMERS */
 #endif /* CONFIG_LOCAL_TIMERS */
 
 
-static void __init exynos4_timer_resources(void)
+static void __init exynos4_timer_resources(void __iomem *base)
 {
 {
 	struct clk *mct_clk;
 	struct clk *mct_clk;
 	mct_clk = clk_get(NULL, "xtal");
 	mct_clk = clk_get(NULL, "xtal");
 
 
 	clk_rate = clk_get_rate(mct_clk);
 	clk_rate = clk_get_rate(mct_clk);
 
 
+	reg_base = base;
+	if (!reg_base)
+		panic("%s: unable to ioremap mct address space\n", __func__);
+
 #ifdef CONFIG_LOCAL_TIMERS
 #ifdef CONFIG_LOCAL_TIMERS
 	if (mct_int_type == MCT_INT_PPI) {
 	if (mct_int_type == MCT_INT_PPI) {
 		int err;
 		int err;
 
 
-		err = request_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER,
+		err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
 					 exynos4_mct_tick_isr, "MCT",
 					 exynos4_mct_tick_isr, "MCT",
 					 &percpu_mct_tick);
 					 &percpu_mct_tick);
 		WARN(err, "MCT: can't request IRQ %d (%d)\n",
 		WARN(err, "MCT: can't request IRQ %d (%d)\n",
-		     EXYNOS_IRQ_MCT_LOCALTIMER, err);
+		     mct_irqs[MCT_L0_IRQ], err);
 	}
 	}
 
 
 	local_timer_register(&exynos4_mct_tick_ops);
 	local_timer_register(&exynos4_mct_tick_ops);
 #endif /* CONFIG_LOCAL_TIMERS */
 #endif /* CONFIG_LOCAL_TIMERS */
 }
 }
 
 
-void __init exynos4_timer_init(void)
+void __init mct_init(void)
 {
 {
-	if (soc_is_exynos5440()) {
-		arch_timer_of_register();
-		return;
+	if (soc_is_exynos4210()) {
+		mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0;
+		mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0;
+		mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1;
+		mct_int_type = MCT_INT_SPI;
+	} else {
+		panic("unable to determine mct controller type\n");
 	}
 	}
 
 
-	if ((soc_is_exynos4210()) || (soc_is_exynos5250()))
-		mct_int_type = MCT_INT_SPI;
-	else
-		mct_int_type = MCT_INT_PPI;
+	exynos4_timer_resources(S5P_VA_SYSTIMER);
+	exynos4_clocksource_init();
+	exynos4_clockevent_init();
+}
 
 
-	exynos4_timer_resources();
+static void __init mct_init_dt(struct device_node *np, unsigned int int_type)
+{
+	u32 nr_irqs, i;
+
+	mct_int_type = int_type;
+
+	/* This driver uses only one global timer interrupt */
+	mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
+
+	/*
+	 * Find out the number of local irqs specified. The local
+	 * timer irqs are specified after the four global timer
+	 * irqs are specified.
+	 */
+	nr_irqs = of_irq_count(np);
+	for (i = MCT_L0_IRQ; i < nr_irqs; i++)
+		mct_irqs[i] = irq_of_parse_and_map(np, i);
+
+	exynos4_timer_resources(of_iomap(np, 0));
 	exynos4_clocksource_init();
 	exynos4_clocksource_init();
 	exynos4_clockevent_init();
 	exynos4_clockevent_init();
 }
 }
+
+
+static void __init mct_init_spi(struct device_node *np)
+{
+	return mct_init_dt(np, MCT_INT_SPI);
+}
+
+static void __init mct_init_ppi(struct device_node *np)
+{
+	return mct_init_dt(np, MCT_INT_PPI);
+}
+CLOCKSOURCE_OF_DECLARE(exynos4210, "samsung,exynos4210-mct", mct_init_spi);
+CLOCKSOURCE_OF_DECLARE(exynos4412, "samsung,exynos4412-mct", mct_init_ppi);