diff options
| author | Arnd Bergmann <arnd@arndb.de> | 2013-04-09 22:07:37 +0200 | 
|---|---|---|
| committer | Arnd Bergmann <arnd@arndb.de> | 2013-04-09 22:18:24 +0200 | 
| commit | 228e3023eb0430b4b9ed0736f8f87c96a6cd9c7a (patch) | |
| tree | 4115d59b698a2a6e1953c5b263cab6d2a113d81b | |
| parent | 894b7382cf20e81d38097d43b8802af6e79d48c4 (diff) | |
| parent | 354599f460ba79c9fb00f220e42de5a7509ceeb4 (diff) | |
| download | olio-linux-3.10-228e3023eb0430b4b9ed0736f8f87c96a6cd9c7a.tar.xz olio-linux-3.10-228e3023eb0430b4b9ed0736f8f87c96a6cd9c7a.zip | |
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>
23 files changed, 315 insertions, 149 deletions
| diff --git a/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt b/Documentation/devicetree/bindings/timer/samsung,exynos4210-mct.txt new file mode 100644 index 00000000000..cb47bfbcaee --- /dev/null +++ b/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>; +		}; +	}; diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 6adf79869f8..03301cb114a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -1654,7 +1654,7 @@ config LOCAL_TIMERS  	bool "Use local timer interrupts"  	depends on SMP  	default y -	select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_MCT) +	select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !CLKSRC_EXYNOS_MCT)  	help  	  Enable support for local timers on SMP platforms, rather then the  	  legacy IPI broadcast method.  Local timers allows the system diff --git a/arch/arm/boot/dts/exynos4210.dtsi b/arch/arm/boot/dts/exynos4210.dtsi index 2feffc70814..49a2786e00b 100644 --- a/arch/arm/boot/dts/exynos4210.dtsi +++ b/arch/arm/boot/dts/exynos4210.dtsi @@ -47,6 +47,28 @@  			     <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 {  		compatible = "samsung,exynos4210-pinctrl";  		reg = <0x11400000 0x1000>; diff --git a/arch/arm/boot/dts/exynos4212.dtsi b/arch/arm/boot/dts/exynos4212.dtsi index c6ae2005961..36d4299789e 100644 --- a/arch/arm/boot/dts/exynos4212.dtsi +++ b/arch/arm/boot/dts/exynos4212.dtsi @@ -25,4 +25,26 @@  	gic:interrupt-controller@10490000 {  		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>; +		}; +	};  }; diff --git a/arch/arm/boot/dts/exynos4412.dtsi b/arch/arm/boot/dts/exynos4412.dtsi index d7dfe312772..821c9fdd1e3 100644 --- a/arch/arm/boot/dts/exynos4412.dtsi +++ b/arch/arm/boot/dts/exynos4412.dtsi @@ -25,4 +25,28 @@  	gic:interrupt-controller@10490000 {  		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>; +		}; +	};  }; diff --git a/arch/arm/boot/dts/exynos5250.dtsi b/arch/arm/boot/dts/exynos5250.dtsi index b1ac73e21c8..c60108e0d27 100644 --- a/arch/arm/boot/dts/exynos5250.dtsi +++ b/arch/arm/boot/dts/exynos5250.dtsi @@ -69,6 +69,28 @@  			     <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 {  		compatible = "samsung,s3c2410-wdt";  		reg = <0x101D0000 0x100>; diff --git a/arch/arm/mach-exynos/Kconfig b/arch/arm/mach-exynos/Kconfig index 2f45906d6ee..faca4326b46 100644 --- a/arch/arm/mach-exynos/Kconfig +++ b/arch/arm/mach-exynos/Kconfig @@ -79,12 +79,6 @@ config SOC_EXYNOS5440  	help  	  Enable EXYNOS5440 SoC support -config EXYNOS4_MCT -	bool -	default y -	help -	  Use MCT (Multi Core Timer) as kernel timers -  config EXYNOS_DEV_DMA  	bool  	help @@ -406,6 +400,7 @@ config MACH_EXYNOS4_DT  	bool "Samsung Exynos4 Machine using device tree"  	depends on ARCH_EXYNOS4  	select ARM_AMBA +	select CLKSRC_OF  	select CPU_EXYNOS4210  	select HAVE_SAMSUNG_KEYPAD if INPUT_KEYBOARD  	select PINCTRL @@ -422,6 +417,7 @@ config MACH_EXYNOS5_DT  	default y  	depends on ARCH_EXYNOS5  	select ARM_AMBA +	select CLKSRC_OF  	select USE_OF  	help  	  Machine support for Samsung EXYNOS5 machine with device tree enabled. diff --git a/arch/arm/mach-exynos/Makefile b/arch/arm/mach-exynos/Makefile index 435757e57bb..daf289b2148 100644 --- a/arch/arm/mach-exynos/Makefile +++ b/arch/arm/mach-exynos/Makefile @@ -26,8 +26,6 @@ obj-$(CONFIG_ARCH_EXYNOS)	+= pmu.o  obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o -obj-$(CONFIG_EXYNOS4_MCT)	+= mct.o -  obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o  # machine support diff --git a/arch/arm/mach-exynos/common.c b/arch/arm/mach-exynos/common.c index bdd957978d9..db7dbd0eb6b 100644 --- a/arch/arm/mach-exynos/common.c +++ b/arch/arm/mach-exynos/common.c @@ -257,11 +257,6 @@ static struct map_desc exynos5_iodesc[] __initdata = {  		.length		= SZ_4K,  		.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,  		.pfn		= __phys_to_pfn(EXYNOS5_PA_SYSRAM),  		.length		= SZ_4K, diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h index 9339bb8954b..3b186eaaaa7 100644 --- a/arch/arm/mach-exynos/common.h +++ b/arch/arm/mach-exynos/common.h @@ -12,7 +12,7 @@  #ifndef __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;  void exynos_init_io(struct map_desc *mach_desc, int size); diff --git a/arch/arm/mach-exynos/include/mach/irqs.h b/arch/arm/mach-exynos/include/mach/irqs.h index 1f4dc35cd4b..c0e75d8dd73 100644 --- a/arch/arm/mach-exynos/include/mach/irqs.h +++ b/arch/arm/mach-exynos/include/mach/irqs.h @@ -30,8 +30,6 @@  /* For EXYNOS4 and EXYNOS5 */ -#define EXYNOS_IRQ_MCT_LOCALTIMER	IRQ_PPI(12) -  #define EXYNOS_IRQ_EINT16_31		IRQ_SPI(32)  /* For EXYNOS4 SoCs */ @@ -323,8 +321,6 @@  #define EXYNOS5_IRQ_CEC			IRQ_SPI(114)  #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_MDMA1		IRQ_SPI(124)  #define EXYNOS5_IRQ_FIMC_LITE0		IRQ_SPI(125) @@ -419,8 +415,6 @@  #define EXYNOS5_IRQ_PMU_CPU1		COMBINER_IRQ(22, 4)  #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_SYSMMU_LITE1_0	COMBINER_IRQ(24, 1) diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h index 1df6abbf53b..7f99b7b187d 100644 --- a/arch/arm/mach-exynos/include/mach/map.h +++ b/arch/arm/mach-exynos/include/mach/map.h @@ -65,7 +65,6 @@  #define EXYNOS5_PA_CMU			0x10010000  #define EXYNOS4_PA_SYSTIMER		0x10050000 -#define EXYNOS5_PA_SYSTIMER		0x101C0000  #define EXYNOS4_PA_WATCHDOG		0x10060000  #define EXYNOS5_PA_WATCHDOG		0x101D0000 diff --git a/arch/arm/mach-exynos/include/mach/regs-mct.h b/arch/arm/mach-exynos/include/mach/regs-mct.h deleted file mode 100644 index 80dd02ad6d6..00000000000 --- a/arch/arm/mach-exynos/include/mach/regs-mct.h +++ /dev/null @@ -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 */ diff --git a/arch/arm/mach-exynos/mach-armlex4210.c b/arch/arm/mach-exynos/mach-armlex4210.c index 685f29173af..3b1a3474267 100644 --- a/arch/arm/mach-exynos/mach-armlex4210.c +++ b/arch/arm/mach-exynos/mach-armlex4210.c @@ -202,6 +202,6 @@ MACHINE_START(ARMLEX4210, "ARMLEX4210")  	.map_io		= armlex4210_map_io,  	.init_machine	= armlex4210_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.restart	= exynos4_restart,  MACHINE_END diff --git a/arch/arm/mach-exynos/mach-exynos4-dt.c b/arch/arm/mach-exynos/mach-exynos4-dt.c index 3358088c822..c4ae108e192 100644 --- a/arch/arm/mach-exynos/mach-exynos4-dt.c +++ b/arch/arm/mach-exynos/mach-exynos4-dt.c @@ -13,6 +13,7 @@  #include <linux/of_platform.h>  #include <linux/serial_core.h> +#include <linux/clocksource.h>  #include <asm/mach/arch.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,  	.init_machine	= exynos4_dt_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= clocksource_of_init,  	.dt_compat	= exynos4_dt_compat,  	.restart        = exynos4_restart,  MACHINE_END diff --git a/arch/arm/mach-exynos/mach-exynos5-dt.c b/arch/arm/mach-exynos/mach-exynos5-dt.c index acaeb14db54..7da4791bfb8 100644 --- a/arch/arm/mach-exynos/mach-exynos5-dt.c +++ b/arch/arm/mach-exynos/mach-exynos5-dt.c @@ -14,6 +14,7 @@  #include <linux/serial_core.h>  #include <linux/memblock.h>  #include <linux/io.h> +#include <linux/clocksource.h>  #include <asm/mach/arch.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,  	.init_machine	= exynos5_dt_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= clocksource_of_init,  	.dt_compat	= exynos5_dt_compat,  	.restart        = exynos5_restart,  	.reserve	= exynos5_reserve, diff --git a/arch/arm/mach-exynos/mach-nuri.c b/arch/arm/mach-exynos/mach-nuri.c index 1ea79730187..da3605d1511 100644 --- a/arch/arm/mach-exynos/mach-nuri.c +++ b/arch/arm/mach-exynos/mach-nuri.c @@ -1380,7 +1380,7 @@ MACHINE_START(NURI, "NURI")  	.map_io		= nuri_map_io,  	.init_machine	= nuri_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.reserve        = &nuri_reserve,  	.restart	= exynos4_restart,  MACHINE_END diff --git a/arch/arm/mach-exynos/mach-origen.c b/arch/arm/mach-exynos/mach-origen.c index 579d2d171da..1772cd284f4 100644 --- a/arch/arm/mach-exynos/mach-origen.c +++ b/arch/arm/mach-exynos/mach-origen.c @@ -815,7 +815,7 @@ MACHINE_START(ORIGEN, "ORIGEN")  	.map_io		= origen_map_io,  	.init_machine	= origen_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.reserve	= &origen_reserve,  	.restart	= exynos4_restart,  MACHINE_END diff --git a/arch/arm/mach-exynos/mach-smdk4x12.c b/arch/arm/mach-exynos/mach-smdk4x12.c index fe6149624b8..34a6356364e 100644 --- a/arch/arm/mach-exynos/mach-smdk4x12.c +++ b/arch/arm/mach-exynos/mach-smdk4x12.c @@ -376,7 +376,7 @@ MACHINE_START(SMDK4212, "SMDK4212")  	.init_irq	= exynos4_init_irq,  	.map_io		= smdk4x12_map_io,  	.init_machine	= smdk4x12_machine_init, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.restart	= exynos4_restart,  	.reserve	= &smdk4x12_reserve,  MACHINE_END @@ -390,7 +390,7 @@ MACHINE_START(SMDK4412, "SMDK4412")  	.map_io		= smdk4x12_map_io,  	.init_machine	= smdk4x12_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.restart	= exynos4_restart,  	.reserve	= &smdk4x12_reserve,  MACHINE_END diff --git a/arch/arm/mach-exynos/mach-smdkv310.c b/arch/arm/mach-exynos/mach-smdkv310.c index d71672922b1..893b14e8c62 100644 --- a/arch/arm/mach-exynos/mach-smdkv310.c +++ b/arch/arm/mach-exynos/mach-smdkv310.c @@ -423,7 +423,7 @@ MACHINE_START(SMDKV310, "SMDKV310")  	.init_irq	= exynos4_init_irq,  	.map_io		= smdkv310_map_io,  	.init_machine	= smdkv310_machine_init, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.reserve	= &smdkv310_reserve,  	.restart	= exynos4_restart,  MACHINE_END @@ -436,7 +436,7 @@ MACHINE_START(SMDKC210, "SMDKC210")  	.map_io		= smdkv310_map_io,  	.init_machine	= smdkv310_machine_init,  	.init_late	= exynos_init_late, -	.init_time	= exynos4_timer_init, +	.init_time	= mct_init,  	.reserve	= &smdkv310_reserve,  	.restart	= exynos4_restart,  MACHINE_END diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index 3167fda9bbb..73fcddb8314 100644 --- a/drivers/clocksource/Kconfig +++ b/drivers/clocksource/Kconfig @@ -70,3 +70,8 @@ config CLKSRC_METAG_GENERIC  	def_bool y if METAG  	help  	  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. diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile index e74c8ce26bf..cd1f09cbd61 100644 --- a/drivers/clocksource/Makefile +++ b/drivers/clocksource/Makefile @@ -19,7 +19,8 @@ obj-$(CONFIG_ARCH_BCM2835)	+= bcm2835_timer.o  obj-$(CONFIG_SUNXI_TIMER)	+= sunxi_timer.o  obj-$(CONFIG_ARCH_TEGRA)	+= tegra20_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_CLKSRC_METAG_GENERIC)	+= metag_generic.o diff --git a/arch/arm/mach-exynos/mct.c b/drivers/clocksource/exynos_mct.c index c9d6650f9b5..957af8636c9 100644 --- a/arch/arm/mach-exynos/mct.c +++ b/drivers/clocksource/exynos_mct.c @@ -20,6 +20,9 @@  #include <linux/delay.h>  #include <linux/percpu.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/localtimer.h> @@ -28,9 +31,36 @@  #include <mach/map.h>  #include <mach/irqs.h> -#include <mach/regs-mct.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  enum { @@ -38,64 +68,75 @@ enum {  	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 int mct_int_type; +static int mct_irqs[MCT_NR_IRQS];  struct mct_clock_event_device {  	struct clock_event_device *evt; -	void __iomem *base; +	unsigned long base;  	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 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 */  			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 */  			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 */  			break;  		default:  			return;  		}  	} else { -		switch ((u32) addr) { -		case (u32) EXYNOS4_MCT_G_TCON: +		switch (offset) { +		case EXYNOS4_MCT_G_TCON:  			stat_addr = EXYNOS4_MCT_G_WSTAT;  			mask = 1 << 16;		/* G_TCON write status */  			break; -		case (u32) EXYNOS4_MCT_G_COMP0_L: +		case EXYNOS4_MCT_G_COMP0_L:  			stat_addr = EXYNOS4_MCT_G_WSTAT;  			mask = 1 << 0;		/* G_COMP0_L write status */  			break; -		case (u32) EXYNOS4_MCT_G_COMP0_U: +		case EXYNOS4_MCT_G_COMP0_U:  			stat_addr = EXYNOS4_MCT_G_WSTAT;  			mask = 1 << 1;		/* G_COMP0_U write status */  			break; -		case (u32) EXYNOS4_MCT_G_COMP0_ADD_INCR: +		case EXYNOS4_MCT_G_COMP0_ADD_INCR:  			stat_addr = EXYNOS4_MCT_G_WSTAT;  			mask = 1 << 2;		/* G_COMP0_ADD_INCR w status */  			break; -		case (u32) EXYNOS4_MCT_G_CNT_L: +		case EXYNOS4_MCT_G_CNT_L:  			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;  			mask = 1 << 0;		/* G_CNT_L write status */  			break; -		case (u32) EXYNOS4_MCT_G_CNT_U: +		case EXYNOS4_MCT_G_CNT_U:  			stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;  			mask = 1 << 1;		/* G_CNT_U write status */  			break; @@ -106,12 +147,12 @@ static void exynos4_mct_write(unsigned int value, void *addr)  	/* Wait maximum 1 ms until written values are applied */  	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;  		} -	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 */ @@ -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(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;  	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)  {  	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 {  		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);  	return ((cycle_t)hi << 32) | lo; @@ -167,7 +208,7 @@ static void exynos4_mct_comp0_stop(void)  {  	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);  	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;  	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) {  		tcon |= MCT_G_TCON_COMP0_AUTO_INC; @@ -257,11 +298,7 @@ static void exynos4_clockevent_init(void)  	mct_comp_device.cpumask = cpumask_of(0);  	clockevents_config_and_register(&mct_comp_device, clk_rate,  					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 @@ -273,12 +310,12 @@ static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)  {  	unsigned long tmp;  	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) {  		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 */  	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 |  	       MCT_L_TCON_INTERVAL_MODE;  	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);  	/* 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);  		return 1;  	} else { @@ -385,7 +422,6 @@ static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)  {  	struct mct_clock_event_device *mevt;  	unsigned int cpu = smp_processor_id(); -	int mct_lx_irq;  	mevt = this_cpu_ptr(&percpu_mct_tick);  	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 (cpu == 0) { -			mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L0 : -						EXYNOS5_IRQ_MCT_L0;  			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 { -			mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L1 : -						EXYNOS5_IRQ_MCT_L1;  			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 { -		enable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER, 0); +		enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);  	}  	return 0; @@ -436,7 +468,7 @@ static void exynos4_local_timer_stop(struct clock_event_device *evt)  		else  			remove_irq(evt->irq, &mct_tick1_event_irq);  	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 = { @@ -445,41 +477,80 @@ static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {  };  #endif /* CONFIG_LOCAL_TIMERS */ -static void __init exynos4_timer_resources(void) +static void __init exynos4_timer_resources(void __iomem *base)  {  	struct clk *mct_clk;  	mct_clk = clk_get(NULL, "xtal");  	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  	if (mct_int_type == MCT_INT_PPI) {  		int err; -		err = request_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER, +		err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],  					 exynos4_mct_tick_isr, "MCT",  					 &percpu_mct_tick);  		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);  #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_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); |