All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/3] ARM: EXYNOS4: Add SYSTIMER IO Address mapping for MCT
@ 2011-02-28  5:16 ` Kukjin Kim
  0 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-02-28  5:16 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc; +Cc: ben-linux, Changhwan Youn, Kukjin Kim

From: Changhwan Youn <chaos.youn@samsung.com>

The MCT(Multi-Core Timer) is used for implementing kernel timers for
EXYNOS4210.

Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
 arch/arm/mach-exynos4/cpu.c              |    5 +++++
 arch/arm/mach-exynos4/include/mach/map.h |    1 +
 2 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c
index b0ec6d3..479dfa1 100644
--- a/arch/arm/mach-exynos4/cpu.c
+++ b/arch/arm/mach-exynos4/cpu.c
@@ -31,6 +31,11 @@ extern void combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq);
 /* Initial IO mappings */
 static struct map_desc exynos4_iodesc[] __initdata = {
 	{
+		.virtual	= (unsigned long)S5P_VA_SYSTIMER,
+		.pfn		= __phys_to_pfn(EXYNOS4_PA_SYSTIMER),
+		.length		= SZ_4K,
+		.type	 	= MT_DEVICE,
+	}, {
 		.virtual	= (unsigned long)S5P_VA_SYSRAM,
 		.pfn		= __phys_to_pfn(EXYNOS4_PA_SYSRAM),
 		.length		= SZ_4K,
diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h
index 80a41e0..d678f08 100644
--- a/arch/arm/mach-exynos4/include/mach/map.h
+++ b/arch/arm/mach-exynos4/include/mach/map.h
@@ -44,6 +44,7 @@
 #define EXYNOS4_PA_PMU			0x10020000
 #define EXYNOS4_PA_CMU			0x10030000
 
+#define EXYNOS4_PA_SYSTIMER		0x10050000
 #define EXYNOS4_PA_WATCHDOG		0x10060000
 #define EXYNOS4_PA_RTC			0x10070000
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 1/3] ARM: EXYNOS4: Add SYSTIMER IO Address mapping for MCT
@ 2011-02-28  5:16 ` Kukjin Kim
  0 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-02-28  5:16 UTC (permalink / raw)
  To: linux-arm-kernel

From: Changhwan Youn <chaos.youn@samsung.com>

The MCT(Multi-Core Timer) is used for implementing kernel timers for
EXYNOS4210.

Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
 arch/arm/mach-exynos4/cpu.c              |    5 +++++
 arch/arm/mach-exynos4/include/mach/map.h |    1 +
 2 files changed, 6 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/cpu.c b/arch/arm/mach-exynos4/cpu.c
index b0ec6d3..479dfa1 100644
--- a/arch/arm/mach-exynos4/cpu.c
+++ b/arch/arm/mach-exynos4/cpu.c
@@ -31,6 +31,11 @@ extern void combiner_cascade_irq(unsigned int combiner_nr, unsigned int irq);
 /* Initial IO mappings */
 static struct map_desc exynos4_iodesc[] __initdata = {
 	{
+		.virtual	= (unsigned long)S5P_VA_SYSTIMER,
+		.pfn		= __phys_to_pfn(EXYNOS4_PA_SYSTIMER),
+		.length		= SZ_4K,
+		.type	 	= MT_DEVICE,
+	}, {
 		.virtual	= (unsigned long)S5P_VA_SYSRAM,
 		.pfn		= __phys_to_pfn(EXYNOS4_PA_SYSRAM),
 		.length		= SZ_4K,
diff --git a/arch/arm/mach-exynos4/include/mach/map.h b/arch/arm/mach-exynos4/include/mach/map.h
index 80a41e0..d678f08 100644
--- a/arch/arm/mach-exynos4/include/mach/map.h
+++ b/arch/arm/mach-exynos4/include/mach/map.h
@@ -44,6 +44,7 @@
 #define EXYNOS4_PA_PMU			0x10020000
 #define EXYNOS4_PA_CMU			0x10030000
 
+#define EXYNOS4_PA_SYSTIMER		0x10050000
 #define EXYNOS4_PA_WATCHDOG		0x10060000
 #define EXYNOS4_PA_RTC			0x10070000
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/3] ARM: EXYNOS4: Add irq definition for kernel global timer
  2011-02-28  5:16 ` Kukjin Kim
@ 2011-02-28  5:16   ` Kukjin Kim
  -1 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-02-28  5:16 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc; +Cc: ben-linux, Changhwan Youn, Kukjin Kim

From: Changhwan Youn <chaos.youn@samsung.com>

This patch adds IRQ_MCT_G0 to irq map. IRQ_MCT_G0 is an interrupt of MCT
comparator and used for kernel global timer.

Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/irqs.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/include/mach/irqs.h b/arch/arm/mach-exynos4/include/mach/irqs.h
index 2dc5900..e3556d4 100644
--- a/arch/arm/mach-exynos4/include/mach/irqs.h
+++ b/arch/arm/mach-exynos4/include/mach/irqs.h
@@ -131,6 +131,7 @@
 #define IRQ_MCT_L0		COMBINER_IRQ(51, 0)
 
 #define IRQ_WDT			COMBINER_IRQ(53, 0)
+#define IRQ_MCT_G0		COMBINER_IRQ(53, 4)
 
 #define MAX_COMBINER_NR		54
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH 2/3] ARM: EXYNOS4: Add irq definition for kernel global timer
@ 2011-02-28  5:16   ` Kukjin Kim
  0 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-02-28  5:16 UTC (permalink / raw)
  To: linux-arm-kernel

From: Changhwan Youn <chaos.youn@samsung.com>

This patch adds IRQ_MCT_G0 to irq map. IRQ_MCT_G0 is an interrupt of MCT
comparator and used for kernel global timer.

Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
 arch/arm/mach-exynos4/include/mach/irqs.h |    1 +
 1 files changed, 1 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-exynos4/include/mach/irqs.h b/arch/arm/mach-exynos4/include/mach/irqs.h
index 2dc5900..e3556d4 100644
--- a/arch/arm/mach-exynos4/include/mach/irqs.h
+++ b/arch/arm/mach-exynos4/include/mach/irqs.h
@@ -131,6 +131,7 @@
 #define IRQ_MCT_L0		COMBINER_IRQ(51, 0)
 
 #define IRQ_WDT			COMBINER_IRQ(53, 0)
+#define IRQ_MCT_G0		COMBINER_IRQ(53, 4)
 
 #define MAX_COMBINER_NR		54
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
  2011-02-28  5:16 ` Kukjin Kim
@ 2011-02-28  5:16   ` Kukjin Kim
  -1 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-02-28  5:16 UTC (permalink / raw)
  To: linux-arm-kernel, linux-samsung-soc
  Cc: ben-linux, Changhwan Youn, Russell King, Kukjin Kim

From: Changhwan Youn <chaos.youn@samsung.com>

The Multi-Core Timer(MCT) of EXYNOS4 is designed for implementing
clock source timer and clock event timers. This patch implements
1 clock source timer with 64 bit free running counter of MCT and
2 clock event timers with two of 31-bit tick counters.

Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
Changes since v3:
- Re-worked based on exynos4
- Changed member of clock_evetn_device like following
  struct mct_clock_event_device {
	struct clock_event_device *evt;
  ...
- Address comments from Russell King
  Added static into "irqreturn_t exynos4_mct_comp_isr()"
  Added .dev_id member into "struct irqaction mct_comp_event_irq"

Changes since v2:
- Addressed comments from Russell King
  Added mct_clock_event_device structure
- Removed suspend()/resume() which are not used now

Changes since v1:
- Addressed comments from Russell King
  (implemented global timer, used local timer and so on)
- Fixed small things

 arch/arm/Kconfig                              |    2 +-
 arch/arm/mach-exynos4/Kconfig                 |    5 +
 arch/arm/mach-exynos4/Makefile                |   11 +-
 arch/arm/mach-exynos4/include/mach/regs-mct.h |   52 +++
 arch/arm/mach-exynos4/mct.c                   |  421 +++++++++++++++++++++++++
 5 files changed, 488 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/mach-exynos4/include/mach/regs-mct.h
 create mode 100644 arch/arm/mach-exynos4/mct.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ec3bf98..b4db99b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1366,7 +1366,7 @@ config LOCAL_TIMERS
 	bool "Use local timer interrupts"
 	depends on SMP
 	default y
-	select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP
+	select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_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/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
index ad03840..77b5d93 100644
--- a/arch/arm/mach-exynos4/Kconfig
+++ b/arch/arm/mach-exynos4/Kconfig
@@ -15,6 +15,11 @@ config CPU_EXYNOS4210
 	help
 	  Enable EXYNOS4210 CPU support
 
+config EXYNOS4_MCT
+	bool "Kernel timer support by MCT"
+	help
+	  Use MCT (Multi Core Timer) as kernel timers
+
 config EXYNOS4_DEV_PD
 	bool
 	help
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
index 0558235..de20b91 100644
--- a/arch/arm/mach-exynos4/Makefile
+++ b/arch/arm/mach-exynos4/Makefile
@@ -13,11 +13,18 @@ obj-				:=
 # Core support for EXYNOS4 system
 
 obj-$(CONFIG_CPU_EXYNOS4210)	+= cpu.o init.o clock.o irq-combiner.o
-obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o time.o gpiolib.o irq-eint.o dma.o
+obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o gpiolib.o irq-eint.o dma.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
 
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
-obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
+
+ifeq ($(CONFIG_EXYNOS4_MCT),y)
+obj-y				+= mct.o
+else
+obj-y				+= time.o
+obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
+endif
+
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 
 # machine support
diff --git a/arch/arm/mach-exynos4/include/mach/regs-mct.h b/arch/arm/mach-exynos4/include/mach/regs-mct.h
new file mode 100644
index 0000000..ca9c843
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/regs-mct.h
@@ -0,0 +1,52 @@
+/* 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_L0_BASE		EXYNOS4_MCTREG(0x300)
+#define EXYNOS4_MCT_L1_BASE		EXYNOS4_MCTREG(0x400)
+
+#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-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c
new file mode 100644
index 0000000..6bb4b50
--- /dev/null
+++ b/arch/arm/mach-exynos4/mct.c
@@ -0,0 +1,421 @@
+/* linux/arch/arm/mach-exynos4/mct.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * EXYNOS4 MCT(Multi-Core Timer) support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/percpu.h>
+
+#include <mach/map.h>
+#include <mach/regs-mct.h>
+#include <asm/mach/time.h>
+
+static unsigned long clk_cnt_per_tick;
+static unsigned long clk_rate;
+
+struct mct_clock_event_device {
+	struct clock_event_device *evt;
+	void __iomem *base;
+};
+
+struct mct_clock_event_device mct_tick[2];
+
+static void exynos4_mct_write(unsigned int value, void *addr)
+{
+	void __iomem *stat_addr;
+	u32 mask;
+	u32 i;
+
+	__raw_writel(value, addr);
+
+	switch ((u32) addr) {
+	case (u32) 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:
+		stat_addr = EXYNOS4_MCT_G_WSTAT;
+		mask = 1 << 0;		/* G_COMP0_L write status */
+		break;
+	case (u32) 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:
+		stat_addr = EXYNOS4_MCT_G_WSTAT;
+		mask = 1 << 2;		/* G_COMP0_ADD_INCR write status */
+		break;
+	case (u32) 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:
+		stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
+		mask = 1 << 1;		/* G_CNT_U write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCON_OFFSET):
+		stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 3;		/* L0_TCON write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCON_OFFSET):
+		stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 3;		/* L1_TCON write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 0;		/* L0_TCNTB write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 0;		/* L1_TCNTB write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_ICNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 1;		/* L0_ICNTB write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_ICNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 1;		/* L1_ICNTB write status */
+		break;
+	default:
+		return;
+	}
+
+	/* 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);
+			return;
+		}
+
+	panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
+}
+
+/* Clocksource handling */
+static void exynos4_mct_frc_start(u32 hi, u32 lo)
+{
+	u32 reg;
+
+	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 |= MCT_G_TCON_START;
+	exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
+}
+
+static cycle_t exynos4_frc_read(struct clocksource *cs)
+{
+	unsigned int lo, hi;
+	u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
+
+	do {
+		hi = hi2;
+		lo = __raw_readl(EXYNOS4_MCT_G_CNT_L);
+		hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
+	} while (hi != hi2);
+
+	return ((cycle_t)hi << 32) | lo;
+}
+
+struct clocksource mct_frc = {
+	.name		= "mct-frc",
+	.rating		= 400,
+	.read		= exynos4_frc_read,
+	.mask		= CLOCKSOURCE_MASK(64),
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void __init exynos4_clocksource_init(void)
+{
+	exynos4_mct_frc_start(0, 0);
+
+	if (clocksource_register_hz(&mct_frc, clk_rate))
+		panic("%s: can't register clocksource\n", mct_frc.name);
+}
+
+static void exynos4_mct_comp0_stop(void)
+{
+	unsigned int tcon;
+
+	tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
+	tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
+
+	exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
+	exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
+}
+
+static void exynos4_mct_comp0_start(enum clock_event_mode mode,
+				    unsigned long cycles)
+{
+	unsigned int tcon;
+	cycle_t comp_cycle;
+
+	tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
+
+	if (mode == CLOCK_EVT_MODE_PERIODIC) {
+		tcon |= MCT_G_TCON_COMP0_AUTO_INC;
+		exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
+	}
+
+	comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
+	exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
+	exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
+
+	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
+
+	tcon |= MCT_G_TCON_COMP0_ENABLE;
+	exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
+}
+
+static int exynos4_comp_set_next_event(unsigned long cycles,
+				       struct clock_event_device *evt)
+{
+	exynos4_mct_comp0_start(evt->mode, cycles);
+
+	return 0;
+}
+
+static void exynos4_comp_set_mode(enum clock_event_mode mode,
+				  struct clock_event_device *evt)
+{
+	exynos4_mct_comp0_stop();
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		exynos4_mct_comp0_start(mode, clk_cnt_per_tick);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+	}
+}
+
+static struct clock_event_device mct_comp_device = {
+	.name		= "mct-comp",
+	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+	.rating		= 250,
+	.set_next_event	= exynos4_comp_set_next_event,
+	.set_mode	= exynos4_comp_set_mode,
+};
+
+static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+
+	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
+
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction mct_comp_event_irq = {
+	.name		= "mct_comp_irq",
+	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
+	.handler	= exynos4_mct_comp_isr,
+	.dev_id		= &mct_comp_device,
+};
+
+static void exynos4_clockevent_init(void)
+{
+	clk_cnt_per_tick = clk_rate / 2	/ HZ;
+
+	clockevents_calc_mult_shift(&mct_comp_device, clk_rate / 2, 5);
+	mct_comp_device.max_delta_ns =
+		clockevent_delta2ns(0xffffffff, &mct_comp_device);
+	mct_comp_device.min_delta_ns =
+		clockevent_delta2ns(0xf, &mct_comp_device);
+	mct_comp_device.cpumask = cpumask_of(0);
+	clockevents_register_device(&mct_comp_device);
+
+	setup_irq(IRQ_MCT_G0, &mct_comp_event_irq);
+}
+
+#ifdef CONFIG_LOCAL_TIMERS
+/* Clock event handling */
+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;
+
+	tmp = __raw_readl(addr);
+	if (tmp & mask) {
+		tmp &= ~mask;
+		exynos4_mct_write(tmp, addr);
+	}
+}
+
+static void exynos4_mct_tick_start(unsigned long cycles,
+				   struct mct_clock_event_device *mevt)
+{
+	unsigned long tmp;
+
+	exynos4_mct_tick_stop(mevt);
+
+	tmp = (1 << 31) | cycles;	/* MCT_L_UPDATE_ICNTB */
+
+	/* update interrupt count buffer */
+	exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
+
+	/* enable MCT tick interupt */
+	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
+
+	tmp = __raw_readl(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);
+}
+
+static int exynos4_tick_set_next_event(unsigned long cycles,
+				       struct clock_event_device *evt)
+{
+	struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
+
+	exynos4_mct_tick_start(cycles, mevt);
+
+	return 0;
+}
+
+static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
+					 struct clock_event_device *evt)
+{
+	struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
+
+	exynos4_mct_tick_stop(mevt);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		exynos4_mct_tick_start(clk_cnt_per_tick, mevt);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+	}
+}
+
+static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
+{
+	struct mct_clock_event_device *mevt = dev_id;
+	struct clock_event_device *evt = mevt->evt;
+
+	/*
+	 * This is for supporting oneshot mode.
+	 * Mct would generate interrupt periodically
+	 * without explicit stopping.
+	 */
+	if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
+		exynos4_mct_tick_stop(mevt);
+
+	/* Clear the MCT tick interrupt */
+	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
+
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction mct_tick0_event_irq = {
+	.name		= "mct_tick0_irq",
+	.flags		= IRQF_TIMER | IRQF_NOBALANCING,
+	.handler	= exynos4_mct_tick_isr,
+};
+
+static struct irqaction mct_tick1_event_irq = {
+	.name		= "mct_tick1_irq",
+	.flags		= IRQF_TIMER | IRQF_NOBALANCING,
+	.handler	= exynos4_mct_tick_isr,
+};
+
+static void exynos4_mct_tick_init(struct clock_event_device *evt)
+{
+	unsigned int cpu = smp_processor_id();
+
+	mct_tick[cpu].evt = evt;
+
+	if (cpu == 0) {
+		mct_tick[cpu].base = EXYNOS4_MCT_L0_BASE;
+		evt->name = "mct_tick0";
+	} else {
+		mct_tick[cpu].base = EXYNOS4_MCT_L1_BASE;
+		evt->name = "mct_tick1";
+	}
+
+	evt->cpumask = cpumask_of(cpu);
+	evt->set_next_event = exynos4_tick_set_next_event;
+	evt->set_mode = exynos4_tick_set_mode;
+	evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	evt->rating = 450;
+
+	clockevents_calc_mult_shift(evt, clk_rate / 2, 5);
+	evt->max_delta_ns =
+		clockevent_delta2ns(0x7fffffff, evt);
+	evt->min_delta_ns =
+		clockevent_delta2ns(0xf, evt);
+
+	clockevents_register_device(evt);
+
+	exynos4_mct_write(0x1, mct_tick[cpu].base + MCT_L_TCNTB_OFFSET);
+
+	if (cpu == 0) {
+		mct_tick0_event_irq.dev_id = &mct_tick[cpu];
+		setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
+	} else {
+		mct_tick1_event_irq.dev_id = &mct_tick[cpu];
+		irq_set_affinity(IRQ_MCT1, cpumask_of(1));
+		setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
+	}
+}
+
+/* Setup the local clock events for a CPU */
+void __cpuinit local_timer_setup(struct clock_event_device *evt)
+{
+	exynos4_mct_tick_init(evt);
+}
+
+int local_timer_ack(void)
+{
+	return 0;
+}
+
+#endif /* CONFIG_LOCAL_TIMERS */
+
+static void __init exynos4_timer_resources(void)
+{
+	struct clk *mct_clk;
+	mct_clk = clk_get(NULL, "xtal");
+
+	clk_rate = clk_get_rate(mct_clk);
+}
+
+static void __init exynos4_timer_init(void)
+{
+	exynos4_timer_resources();
+	exynos4_clocksource_init();
+	exynos4_clockevent_init();
+}
+
+struct sys_timer exynos4_timer = {
+	.init		= exynos4_timer_init,
+};
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
@ 2011-02-28  5:16   ` Kukjin Kim
  0 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-02-28  5:16 UTC (permalink / raw)
  To: linux-arm-kernel

From: Changhwan Youn <chaos.youn@samsung.com>

The Multi-Core Timer(MCT) of EXYNOS4 is designed for implementing
clock source timer and clock event timers. This patch implements
1 clock source timer with 64 bit free running counter of MCT and
2 clock event timers with two of 31-bit tick counters.

Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
Cc: Ben Dooks <ben-linux@fluff.org>
Cc: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
Changes since v3:
- Re-worked based on exynos4
- Changed member of clock_evetn_device like following
  struct mct_clock_event_device {
	struct clock_event_device *evt;
  ...
- Address comments from Russell King
  Added static into "irqreturn_t exynos4_mct_comp_isr()"
  Added .dev_id member into "struct irqaction mct_comp_event_irq"

Changes since v2:
- Addressed comments from Russell King
  Added mct_clock_event_device structure
- Removed suspend()/resume() which are not used now

Changes since v1:
- Addressed comments from Russell King
  (implemented global timer, used local timer and so on)
- Fixed small things

 arch/arm/Kconfig                              |    2 +-
 arch/arm/mach-exynos4/Kconfig                 |    5 +
 arch/arm/mach-exynos4/Makefile                |   11 +-
 arch/arm/mach-exynos4/include/mach/regs-mct.h |   52 +++
 arch/arm/mach-exynos4/mct.c                   |  421 +++++++++++++++++++++++++
 5 files changed, 488 insertions(+), 3 deletions(-)
 create mode 100644 arch/arm/mach-exynos4/include/mach/regs-mct.h
 create mode 100644 arch/arm/mach-exynos4/mct.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index ec3bf98..b4db99b 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1366,7 +1366,7 @@ config LOCAL_TIMERS
 	bool "Use local timer interrupts"
 	depends on SMP
 	default y
-	select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP
+	select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_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/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
index ad03840..77b5d93 100644
--- a/arch/arm/mach-exynos4/Kconfig
+++ b/arch/arm/mach-exynos4/Kconfig
@@ -15,6 +15,11 @@ config CPU_EXYNOS4210
 	help
 	  Enable EXYNOS4210 CPU support
 
+config EXYNOS4_MCT
+	bool "Kernel timer support by MCT"
+	help
+	  Use MCT (Multi Core Timer) as kernel timers
+
 config EXYNOS4_DEV_PD
 	bool
 	help
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
index 0558235..de20b91 100644
--- a/arch/arm/mach-exynos4/Makefile
+++ b/arch/arm/mach-exynos4/Makefile
@@ -13,11 +13,18 @@ obj-				:=
 # Core support for EXYNOS4 system
 
 obj-$(CONFIG_CPU_EXYNOS4210)	+= cpu.o init.o clock.o irq-combiner.o
-obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o time.o gpiolib.o irq-eint.o dma.o
+obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o gpiolib.o irq-eint.o dma.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
 
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
-obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
+
+ifeq ($(CONFIG_EXYNOS4_MCT),y)
+obj-y				+= mct.o
+else
+obj-y				+= time.o
+obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
+endif
+
 obj-$(CONFIG_HOTPLUG_CPU)	+= hotplug.o
 
 # machine support
diff --git a/arch/arm/mach-exynos4/include/mach/regs-mct.h b/arch/arm/mach-exynos4/include/mach/regs-mct.h
new file mode 100644
index 0000000..ca9c843
--- /dev/null
+++ b/arch/arm/mach-exynos4/include/mach/regs-mct.h
@@ -0,0 +1,52 @@
+/* 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_L0_BASE		EXYNOS4_MCTREG(0x300)
+#define EXYNOS4_MCT_L1_BASE		EXYNOS4_MCTREG(0x400)
+
+#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-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c
new file mode 100644
index 0000000..6bb4b50
--- /dev/null
+++ b/arch/arm/mach-exynos4/mct.c
@@ -0,0 +1,421 @@
+/* linux/arch/arm/mach-exynos4/mct.c
+ *
+ * Copyright (c) 2011 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * EXYNOS4 MCT(Multi-Core Timer) support
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/percpu.h>
+
+#include <mach/map.h>
+#include <mach/regs-mct.h>
+#include <asm/mach/time.h>
+
+static unsigned long clk_cnt_per_tick;
+static unsigned long clk_rate;
+
+struct mct_clock_event_device {
+	struct clock_event_device *evt;
+	void __iomem *base;
+};
+
+struct mct_clock_event_device mct_tick[2];
+
+static void exynos4_mct_write(unsigned int value, void *addr)
+{
+	void __iomem *stat_addr;
+	u32 mask;
+	u32 i;
+
+	__raw_writel(value, addr);
+
+	switch ((u32) addr) {
+	case (u32) 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:
+		stat_addr = EXYNOS4_MCT_G_WSTAT;
+		mask = 1 << 0;		/* G_COMP0_L write status */
+		break;
+	case (u32) 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:
+		stat_addr = EXYNOS4_MCT_G_WSTAT;
+		mask = 1 << 2;		/* G_COMP0_ADD_INCR write status */
+		break;
+	case (u32) 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:
+		stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
+		mask = 1 << 1;		/* G_CNT_U write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCON_OFFSET):
+		stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 3;		/* L0_TCON write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCON_OFFSET):
+		stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 3;		/* L1_TCON write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 0;		/* L0_TCNTB write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 0;		/* L1_TCNTB write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_ICNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 1;		/* L0_ICNTB write status */
+		break;
+	case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_ICNTB_OFFSET):
+		stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
+		mask = 1 << 1;		/* L1_ICNTB write status */
+		break;
+	default:
+		return;
+	}
+
+	/* 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);
+			return;
+		}
+
+	panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
+}
+
+/* Clocksource handling */
+static void exynos4_mct_frc_start(u32 hi, u32 lo)
+{
+	u32 reg;
+
+	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 |= MCT_G_TCON_START;
+	exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
+}
+
+static cycle_t exynos4_frc_read(struct clocksource *cs)
+{
+	unsigned int lo, hi;
+	u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
+
+	do {
+		hi = hi2;
+		lo = __raw_readl(EXYNOS4_MCT_G_CNT_L);
+		hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
+	} while (hi != hi2);
+
+	return ((cycle_t)hi << 32) | lo;
+}
+
+struct clocksource mct_frc = {
+	.name		= "mct-frc",
+	.rating		= 400,
+	.read		= exynos4_frc_read,
+	.mask		= CLOCKSOURCE_MASK(64),
+	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,
+};
+
+static void __init exynos4_clocksource_init(void)
+{
+	exynos4_mct_frc_start(0, 0);
+
+	if (clocksource_register_hz(&mct_frc, clk_rate))
+		panic("%s: can't register clocksource\n", mct_frc.name);
+}
+
+static void exynos4_mct_comp0_stop(void)
+{
+	unsigned int tcon;
+
+	tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
+	tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
+
+	exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
+	exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
+}
+
+static void exynos4_mct_comp0_start(enum clock_event_mode mode,
+				    unsigned long cycles)
+{
+	unsigned int tcon;
+	cycle_t comp_cycle;
+
+	tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
+
+	if (mode == CLOCK_EVT_MODE_PERIODIC) {
+		tcon |= MCT_G_TCON_COMP0_AUTO_INC;
+		exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
+	}
+
+	comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
+	exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
+	exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
+
+	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
+
+	tcon |= MCT_G_TCON_COMP0_ENABLE;
+	exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
+}
+
+static int exynos4_comp_set_next_event(unsigned long cycles,
+				       struct clock_event_device *evt)
+{
+	exynos4_mct_comp0_start(evt->mode, cycles);
+
+	return 0;
+}
+
+static void exynos4_comp_set_mode(enum clock_event_mode mode,
+				  struct clock_event_device *evt)
+{
+	exynos4_mct_comp0_stop();
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		exynos4_mct_comp0_start(mode, clk_cnt_per_tick);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+	}
+}
+
+static struct clock_event_device mct_comp_device = {
+	.name		= "mct-comp",
+	.features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
+	.rating		= 250,
+	.set_next_event	= exynos4_comp_set_next_event,
+	.set_mode	= exynos4_comp_set_mode,
+};
+
+static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
+{
+	struct clock_event_device *evt = dev_id;
+
+	exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
+
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction mct_comp_event_irq = {
+	.name		= "mct_comp_irq",
+	.flags		= IRQF_TIMER | IRQF_IRQPOLL,
+	.handler	= exynos4_mct_comp_isr,
+	.dev_id		= &mct_comp_device,
+};
+
+static void exynos4_clockevent_init(void)
+{
+	clk_cnt_per_tick = clk_rate / 2	/ HZ;
+
+	clockevents_calc_mult_shift(&mct_comp_device, clk_rate / 2, 5);
+	mct_comp_device.max_delta_ns =
+		clockevent_delta2ns(0xffffffff, &mct_comp_device);
+	mct_comp_device.min_delta_ns =
+		clockevent_delta2ns(0xf, &mct_comp_device);
+	mct_comp_device.cpumask = cpumask_of(0);
+	clockevents_register_device(&mct_comp_device);
+
+	setup_irq(IRQ_MCT_G0, &mct_comp_event_irq);
+}
+
+#ifdef CONFIG_LOCAL_TIMERS
+/* Clock event handling */
+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;
+
+	tmp = __raw_readl(addr);
+	if (tmp & mask) {
+		tmp &= ~mask;
+		exynos4_mct_write(tmp, addr);
+	}
+}
+
+static void exynos4_mct_tick_start(unsigned long cycles,
+				   struct mct_clock_event_device *mevt)
+{
+	unsigned long tmp;
+
+	exynos4_mct_tick_stop(mevt);
+
+	tmp = (1 << 31) | cycles;	/* MCT_L_UPDATE_ICNTB */
+
+	/* update interrupt count buffer */
+	exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
+
+	/* enable MCT tick interupt */
+	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
+
+	tmp = __raw_readl(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);
+}
+
+static int exynos4_tick_set_next_event(unsigned long cycles,
+				       struct clock_event_device *evt)
+{
+	struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
+
+	exynos4_mct_tick_start(cycles, mevt);
+
+	return 0;
+}
+
+static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
+					 struct clock_event_device *evt)
+{
+	struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
+
+	exynos4_mct_tick_stop(mevt);
+
+	switch (mode) {
+	case CLOCK_EVT_MODE_PERIODIC:
+		exynos4_mct_tick_start(clk_cnt_per_tick, mevt);
+		break;
+
+	case CLOCK_EVT_MODE_ONESHOT:
+	case CLOCK_EVT_MODE_UNUSED:
+	case CLOCK_EVT_MODE_SHUTDOWN:
+	case CLOCK_EVT_MODE_RESUME:
+		break;
+	}
+}
+
+static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
+{
+	struct mct_clock_event_device *mevt = dev_id;
+	struct clock_event_device *evt = mevt->evt;
+
+	/*
+	 * This is for supporting oneshot mode.
+	 * Mct would generate interrupt periodically
+	 * without explicit stopping.
+	 */
+	if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
+		exynos4_mct_tick_stop(mevt);
+
+	/* Clear the MCT tick interrupt */
+	exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
+
+	evt->event_handler(evt);
+
+	return IRQ_HANDLED;
+}
+
+static struct irqaction mct_tick0_event_irq = {
+	.name		= "mct_tick0_irq",
+	.flags		= IRQF_TIMER | IRQF_NOBALANCING,
+	.handler	= exynos4_mct_tick_isr,
+};
+
+static struct irqaction mct_tick1_event_irq = {
+	.name		= "mct_tick1_irq",
+	.flags		= IRQF_TIMER | IRQF_NOBALANCING,
+	.handler	= exynos4_mct_tick_isr,
+};
+
+static void exynos4_mct_tick_init(struct clock_event_device *evt)
+{
+	unsigned int cpu = smp_processor_id();
+
+	mct_tick[cpu].evt = evt;
+
+	if (cpu == 0) {
+		mct_tick[cpu].base = EXYNOS4_MCT_L0_BASE;
+		evt->name = "mct_tick0";
+	} else {
+		mct_tick[cpu].base = EXYNOS4_MCT_L1_BASE;
+		evt->name = "mct_tick1";
+	}
+
+	evt->cpumask = cpumask_of(cpu);
+	evt->set_next_event = exynos4_tick_set_next_event;
+	evt->set_mode = exynos4_tick_set_mode;
+	evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+	evt->rating = 450;
+
+	clockevents_calc_mult_shift(evt, clk_rate / 2, 5);
+	evt->max_delta_ns =
+		clockevent_delta2ns(0x7fffffff, evt);
+	evt->min_delta_ns =
+		clockevent_delta2ns(0xf, evt);
+
+	clockevents_register_device(evt);
+
+	exynos4_mct_write(0x1, mct_tick[cpu].base + MCT_L_TCNTB_OFFSET);
+
+	if (cpu == 0) {
+		mct_tick0_event_irq.dev_id = &mct_tick[cpu];
+		setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
+	} else {
+		mct_tick1_event_irq.dev_id = &mct_tick[cpu];
+		irq_set_affinity(IRQ_MCT1, cpumask_of(1));
+		setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
+	}
+}
+
+/* Setup the local clock events for a CPU */
+void __cpuinit local_timer_setup(struct clock_event_device *evt)
+{
+	exynos4_mct_tick_init(evt);
+}
+
+int local_timer_ack(void)
+{
+	return 0;
+}
+
+#endif /* CONFIG_LOCAL_TIMERS */
+
+static void __init exynos4_timer_resources(void)
+{
+	struct clk *mct_clk;
+	mct_clk = clk_get(NULL, "xtal");
+
+	clk_rate = clk_get_rate(mct_clk);
+}
+
+static void __init exynos4_timer_init(void)
+{
+	exynos4_timer_resources();
+	exynos4_clocksource_init();
+	exynos4_clockevent_init();
+}
+
+struct sys_timer exynos4_timer = {
+	.init		= exynos4_timer_init,
+};
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
  2011-02-28  5:16   ` Kukjin Kim
@ 2011-03-01  3:14     ` Kyungmin Park
  -1 siblings, 0 replies; 12+ messages in thread
From: Kyungmin Park @ 2011-03-01  3:14 UTC (permalink / raw)
  To: Kukjin Kim
  Cc: linux-arm-kernel, linux-samsung-soc, ben-linux, Changhwan Youn,
	Russell King, Marek Szyprowski

Hi,

MCT is only possible from EVT1.0 or later. The current universal_c210
used the EVT0 version.
So can you remove the ifdef and board can select which timer is used?
Please refer the omap implementation and how to use it.

If we use the same configuration, It will be boot failed at
universal_c210 board.

Thank you,
Kyungmin Park

On Mon, Feb 28, 2011 at 2:16 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> From: Changhwan Youn <chaos.youn@samsung.com>
>
> The Multi-Core Timer(MCT) of EXYNOS4 is designed for implementing
> clock source timer and clock event timers. This patch implements
> 1 clock source timer with 64 bit free running counter of MCT and
> 2 clock event timers with two of 31-bit tick counters.
>
> Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
> Cc: Ben Dooks <ben-linux@fluff.org>
> Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> Changes since v3:
> - Re-worked based on exynos4
> - Changed member of clock_evetn_device like following
>  struct mct_clock_event_device {
>        struct clock_event_device *evt;
>  ...
> - Address comments from Russell King
>  Added static into "irqreturn_t exynos4_mct_comp_isr()"
>  Added .dev_id member into "struct irqaction mct_comp_event_irq"
>
> Changes since v2:
> - Addressed comments from Russell King
>  Added mct_clock_event_device structure
> - Removed suspend()/resume() which are not used now
>
> Changes since v1:
> - Addressed comments from Russell King
>  (implemented global timer, used local timer and so on)
> - Fixed small things
>
>  arch/arm/Kconfig                              |    2 +-
>  arch/arm/mach-exynos4/Kconfig                 |    5 +
>  arch/arm/mach-exynos4/Makefile                |   11 +-
>  arch/arm/mach-exynos4/include/mach/regs-mct.h |   52 +++
>  arch/arm/mach-exynos4/mct.c                   |  421 +++++++++++++++++++++++++
>  5 files changed, 488 insertions(+), 3 deletions(-)
>  create mode 100644 arch/arm/mach-exynos4/include/mach/regs-mct.h
>  create mode 100644 arch/arm/mach-exynos4/mct.c
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index ec3bf98..b4db99b 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1366,7 +1366,7 @@ config LOCAL_TIMERS
>        bool "Use local timer interrupts"
>        depends on SMP
>        default y
> -       select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP
> +       select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_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/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
> index ad03840..77b5d93 100644
> --- a/arch/arm/mach-exynos4/Kconfig
> +++ b/arch/arm/mach-exynos4/Kconfig
> @@ -15,6 +15,11 @@ config CPU_EXYNOS4210
>        help
>          Enable EXYNOS4210 CPU support
>
> +config EXYNOS4_MCT
> +       bool "Kernel timer support by MCT"
> +       help
> +         Use MCT (Multi Core Timer) as kernel timers
> +
>  config EXYNOS4_DEV_PD
>        bool
>        help
> diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
> index 0558235..de20b91 100644
> --- a/arch/arm/mach-exynos4/Makefile
> +++ b/arch/arm/mach-exynos4/Makefile
> @@ -13,11 +13,18 @@ obj-                                :=
>  # Core support for EXYNOS4 system
>
>  obj-$(CONFIG_CPU_EXYNOS4210)   += cpu.o init.o clock.o irq-combiner.o
> -obj-$(CONFIG_CPU_EXYNOS4210)   += setup-i2c0.o time.o gpiolib.o irq-eint.o dma.o
> +obj-$(CONFIG_CPU_EXYNOS4210)   += setup-i2c0.o gpiolib.o irq-eint.o dma.o
>  obj-$(CONFIG_CPU_FREQ)         += cpufreq.o
>
>  obj-$(CONFIG_SMP)              += platsmp.o headsmp.o
> -obj-$(CONFIG_LOCAL_TIMERS)     += localtimer.o
> +
> +ifeq ($(CONFIG_EXYNOS4_MCT),y)
> +obj-y                          += mct.o
> +else
> +obj-y                          += time.o
> +obj-$(CONFIG_LOCAL_TIMERS)     += localtimer.o
> +endif
> +
>  obj-$(CONFIG_HOTPLUG_CPU)      += hotplug.o
>
>  # machine support
> diff --git a/arch/arm/mach-exynos4/include/mach/regs-mct.h b/arch/arm/mach-exynos4/include/mach/regs-mct.h
> new file mode 100644
> index 0000000..ca9c843
> --- /dev/null
> +++ b/arch/arm/mach-exynos4/include/mach/regs-mct.h
> @@ -0,0 +1,52 @@
> +/* 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_L0_BASE            EXYNOS4_MCTREG(0x300)
> +#define EXYNOS4_MCT_L1_BASE            EXYNOS4_MCTREG(0x400)
> +
> +#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-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c
> new file mode 100644
> index 0000000..6bb4b50
> --- /dev/null
> +++ b/arch/arm/mach-exynos4/mct.c
> @@ -0,0 +1,421 @@
> +/* linux/arch/arm/mach-exynos4/mct.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + *             http://www.samsung.com
> + *
> + * EXYNOS4 MCT(Multi-Core Timer) support
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/sched.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/percpu.h>
> +
> +#include <mach/map.h>
> +#include <mach/regs-mct.h>
> +#include <asm/mach/time.h>
> +
> +static unsigned long clk_cnt_per_tick;
> +static unsigned long clk_rate;
> +
> +struct mct_clock_event_device {
> +       struct clock_event_device *evt;
> +       void __iomem *base;
> +};
> +
> +struct mct_clock_event_device mct_tick[2];
> +
> +static void exynos4_mct_write(unsigned int value, void *addr)
> +{
> +       void __iomem *stat_addr;
> +       u32 mask;
> +       u32 i;
> +
> +       __raw_writel(value, addr);
> +
> +       switch ((u32) addr) {
> +       case (u32) 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:
> +               stat_addr = EXYNOS4_MCT_G_WSTAT;
> +               mask = 1 << 0;          /* G_COMP0_L write status */
> +               break;
> +       case (u32) 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:
> +               stat_addr = EXYNOS4_MCT_G_WSTAT;
> +               mask = 1 << 2;          /* G_COMP0_ADD_INCR write status */
> +               break;
> +       case (u32) 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:
> +               stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
> +               mask = 1 << 1;          /* G_CNT_U write status */
> +               break;
> +       case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCON_OFFSET):
> +               stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
> +               mask = 1 << 3;          /* L0_TCON write status */
> +               break;
> +       case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCON_OFFSET):
> +               stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
> +               mask = 1 << 3;          /* L1_TCON write status */
> +               break;
> +       case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCNTB_OFFSET):
> +               stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
> +               mask = 1 << 0;          /* L0_TCNTB write status */
> +               break;
> +       case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCNTB_OFFSET):
> +               stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
> +               mask = 1 << 0;          /* L1_TCNTB write status */
> +               break;
> +       case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_ICNTB_OFFSET):
> +               stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
> +               mask = 1 << 1;          /* L0_ICNTB write status */
> +               break;
> +       case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_ICNTB_OFFSET):
> +               stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
> +               mask = 1 << 1;          /* L1_ICNTB write status */
> +               break;
> +       default:
> +               return;
> +       }
> +
> +       /* 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);
> +                       return;
> +               }
> +
> +       panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
> +}
> +
> +/* Clocksource handling */
> +static void exynos4_mct_frc_start(u32 hi, u32 lo)
> +{
> +       u32 reg;
> +
> +       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 |= MCT_G_TCON_START;
> +       exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
> +}
> +
> +static cycle_t exynos4_frc_read(struct clocksource *cs)
> +{
> +       unsigned int lo, hi;
> +       u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
> +
> +       do {
> +               hi = hi2;
> +               lo = __raw_readl(EXYNOS4_MCT_G_CNT_L);
> +               hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
> +       } while (hi != hi2);
> +
> +       return ((cycle_t)hi << 32) | lo;
> +}
> +
> +struct clocksource mct_frc = {
> +       .name           = "mct-frc",
> +       .rating         = 400,
> +       .read           = exynos4_frc_read,
> +       .mask           = CLOCKSOURCE_MASK(64),
> +       .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
> +};
> +
> +static void __init exynos4_clocksource_init(void)
> +{
> +       exynos4_mct_frc_start(0, 0);
> +
> +       if (clocksource_register_hz(&mct_frc, clk_rate))
> +               panic("%s: can't register clocksource\n", mct_frc.name);
> +}
> +
> +static void exynos4_mct_comp0_stop(void)
> +{
> +       unsigned int tcon;
> +
> +       tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
> +       tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
> +
> +       exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
> +       exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
> +}
> +
> +static void exynos4_mct_comp0_start(enum clock_event_mode mode,
> +                                   unsigned long cycles)
> +{
> +       unsigned int tcon;
> +       cycle_t comp_cycle;
> +
> +       tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
> +
> +       if (mode == CLOCK_EVT_MODE_PERIODIC) {
> +               tcon |= MCT_G_TCON_COMP0_AUTO_INC;
> +               exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
> +       }
> +
> +       comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
> +       exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
> +       exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
> +
> +       exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
> +
> +       tcon |= MCT_G_TCON_COMP0_ENABLE;
> +       exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
> +}
> +
> +static int exynos4_comp_set_next_event(unsigned long cycles,
> +                                      struct clock_event_device *evt)
> +{
> +       exynos4_mct_comp0_start(evt->mode, cycles);
> +
> +       return 0;
> +}
> +
> +static void exynos4_comp_set_mode(enum clock_event_mode mode,
> +                                 struct clock_event_device *evt)
> +{
> +       exynos4_mct_comp0_stop();
> +
> +       switch (mode) {
> +       case CLOCK_EVT_MODE_PERIODIC:
> +               exynos4_mct_comp0_start(mode, clk_cnt_per_tick);
> +               break;
> +
> +       case CLOCK_EVT_MODE_ONESHOT:
> +       case CLOCK_EVT_MODE_UNUSED:
> +       case CLOCK_EVT_MODE_SHUTDOWN:
> +       case CLOCK_EVT_MODE_RESUME:
> +               break;
> +       }
> +}
> +
> +static struct clock_event_device mct_comp_device = {
> +       .name           = "mct-comp",
> +       .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
> +       .rating         = 250,
> +       .set_next_event = exynos4_comp_set_next_event,
> +       .set_mode       = exynos4_comp_set_mode,
> +};
> +
> +static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
> +{
> +       struct clock_event_device *evt = dev_id;
> +
> +       exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
> +
> +       evt->event_handler(evt);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static struct irqaction mct_comp_event_irq = {
> +       .name           = "mct_comp_irq",
> +       .flags          = IRQF_TIMER | IRQF_IRQPOLL,
> +       .handler        = exynos4_mct_comp_isr,
> +       .dev_id         = &mct_comp_device,
> +};
> +
> +static void exynos4_clockevent_init(void)
> +{
> +       clk_cnt_per_tick = clk_rate / 2 / HZ;
> +
> +       clockevents_calc_mult_shift(&mct_comp_device, clk_rate / 2, 5);
> +       mct_comp_device.max_delta_ns =
> +               clockevent_delta2ns(0xffffffff, &mct_comp_device);
> +       mct_comp_device.min_delta_ns =
> +               clockevent_delta2ns(0xf, &mct_comp_device);
> +       mct_comp_device.cpumask = cpumask_of(0);
> +       clockevents_register_device(&mct_comp_device);
> +
> +       setup_irq(IRQ_MCT_G0, &mct_comp_event_irq);
> +}
> +
> +#ifdef CONFIG_LOCAL_TIMERS
> +/* Clock event handling */
> +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;
> +
> +       tmp = __raw_readl(addr);
> +       if (tmp & mask) {
> +               tmp &= ~mask;
> +               exynos4_mct_write(tmp, addr);
> +       }
> +}
> +
> +static void exynos4_mct_tick_start(unsigned long cycles,
> +                                  struct mct_clock_event_device *mevt)
> +{
> +       unsigned long tmp;
> +
> +       exynos4_mct_tick_stop(mevt);
> +
> +       tmp = (1 << 31) | cycles;       /* MCT_L_UPDATE_ICNTB */
> +
> +       /* update interrupt count buffer */
> +       exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
> +
> +       /* enable MCT tick interupt */
> +       exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
> +
> +       tmp = __raw_readl(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);
> +}
> +
> +static int exynos4_tick_set_next_event(unsigned long cycles,
> +                                      struct clock_event_device *evt)
> +{
> +       struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
> +
> +       exynos4_mct_tick_start(cycles, mevt);
> +
> +       return 0;
> +}
> +
> +static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
> +                                        struct clock_event_device *evt)
> +{
> +       struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
> +
> +       exynos4_mct_tick_stop(mevt);
> +
> +       switch (mode) {
> +       case CLOCK_EVT_MODE_PERIODIC:
> +               exynos4_mct_tick_start(clk_cnt_per_tick, mevt);
> +               break;
> +
> +       case CLOCK_EVT_MODE_ONESHOT:
> +       case CLOCK_EVT_MODE_UNUSED:
> +       case CLOCK_EVT_MODE_SHUTDOWN:
> +       case CLOCK_EVT_MODE_RESUME:
> +               break;
> +       }
> +}
> +
> +static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
> +{
> +       struct mct_clock_event_device *mevt = dev_id;
> +       struct clock_event_device *evt = mevt->evt;
> +
> +       /*
> +        * This is for supporting oneshot mode.
> +        * Mct would generate interrupt periodically
> +        * without explicit stopping.
> +        */
> +       if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
> +               exynos4_mct_tick_stop(mevt);
> +
> +       /* Clear the MCT tick interrupt */
> +       exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
> +
> +       evt->event_handler(evt);
> +
> +       return IRQ_HANDLED;
> +}
> +
> +static struct irqaction mct_tick0_event_irq = {
> +       .name           = "mct_tick0_irq",
> +       .flags          = IRQF_TIMER | IRQF_NOBALANCING,
> +       .handler        = exynos4_mct_tick_isr,
> +};
> +
> +static struct irqaction mct_tick1_event_irq = {
> +       .name           = "mct_tick1_irq",
> +       .flags          = IRQF_TIMER | IRQF_NOBALANCING,
> +       .handler        = exynos4_mct_tick_isr,
> +};
> +
> +static void exynos4_mct_tick_init(struct clock_event_device *evt)
> +{
> +       unsigned int cpu = smp_processor_id();
> +
> +       mct_tick[cpu].evt = evt;
> +
> +       if (cpu == 0) {
> +               mct_tick[cpu].base = EXYNOS4_MCT_L0_BASE;
> +               evt->name = "mct_tick0";
> +       } else {
> +               mct_tick[cpu].base = EXYNOS4_MCT_L1_BASE;
> +               evt->name = "mct_tick1";
> +       }
> +
> +       evt->cpumask = cpumask_of(cpu);
> +       evt->set_next_event = exynos4_tick_set_next_event;
> +       evt->set_mode = exynos4_tick_set_mode;
> +       evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> +       evt->rating = 450;
> +
> +       clockevents_calc_mult_shift(evt, clk_rate / 2, 5);
> +       evt->max_delta_ns =
> +               clockevent_delta2ns(0x7fffffff, evt);
> +       evt->min_delta_ns =
> +               clockevent_delta2ns(0xf, evt);
> +
> +       clockevents_register_device(evt);
> +
> +       exynos4_mct_write(0x1, mct_tick[cpu].base + MCT_L_TCNTB_OFFSET);
> +
> +       if (cpu == 0) {
> +               mct_tick0_event_irq.dev_id = &mct_tick[cpu];
> +               setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
> +       } else {
> +               mct_tick1_event_irq.dev_id = &mct_tick[cpu];
> +               irq_set_affinity(IRQ_MCT1, cpumask_of(1));
> +               setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
> +       }
> +}
> +
> +/* Setup the local clock events for a CPU */
> +void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +{
> +       exynos4_mct_tick_init(evt);
> +}
> +
> +int local_timer_ack(void)
> +{
> +       return 0;
> +}
> +
> +#endif /* CONFIG_LOCAL_TIMERS */
> +
> +static void __init exynos4_timer_resources(void)
> +{
> +       struct clk *mct_clk;
> +       mct_clk = clk_get(NULL, "xtal");
> +
> +       clk_rate = clk_get_rate(mct_clk);
> +}
> +
> +static void __init exynos4_timer_init(void)
> +{
> +       exynos4_timer_resources();
> +       exynos4_clocksource_init();
> +       exynos4_clockevent_init();
> +}
> +
> +struct sys_timer exynos4_timer = {
> +       .init           = exynos4_timer_init,
> +};
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
@ 2011-03-01  3:14     ` Kyungmin Park
  0 siblings, 0 replies; 12+ messages in thread
From: Kyungmin Park @ 2011-03-01  3:14 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

MCT is only possible from EVT1.0 or later. The current universal_c210
used the EVT0 version.
So can you remove the ifdef and board can select which timer is used?
Please refer the omap implementation and how to use it.

If we use the same configuration, It will be boot failed at
universal_c210 board.

Thank you,
Kyungmin Park

On Mon, Feb 28, 2011 at 2:16 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> From: Changhwan Youn <chaos.youn@samsung.com>
>
> The Multi-Core Timer(MCT) of EXYNOS4 is designed for implementing
> clock source timer and clock event timers. This patch implements
> 1 clock source timer with 64 bit free running counter of MCT and
> 2 clock event timers with two of 31-bit tick counters.
>
> Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
> Cc: Ben Dooks <ben-linux@fluff.org>
> Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> Changes since v3:
> - Re-worked based on exynos4
> - Changed member of clock_evetn_device like following
> ?struct mct_clock_event_device {
> ? ? ? ?struct clock_event_device *evt;
> ?...
> - Address comments from Russell King
> ?Added static into "irqreturn_t exynos4_mct_comp_isr()"
> ?Added .dev_id member into "struct irqaction mct_comp_event_irq"
>
> Changes since v2:
> - Addressed comments from Russell King
> ?Added mct_clock_event_device structure
> - Removed suspend()/resume() which are not used now
>
> Changes since v1:
> - Addressed comments from Russell King
> ?(implemented global timer, used local timer and so on)
> - Fixed small things
>
> ?arch/arm/Kconfig ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?2 +-
> ?arch/arm/mach-exynos4/Kconfig ? ? ? ? ? ? ? ? | ? ?5 +
> ?arch/arm/mach-exynos4/Makefile ? ? ? ? ? ? ? ?| ? 11 +-
> ?arch/arm/mach-exynos4/include/mach/regs-mct.h | ? 52 +++
> ?arch/arm/mach-exynos4/mct.c ? ? ? ? ? ? ? ? ? | ?421 +++++++++++++++++++++++++
> ?5 files changed, 488 insertions(+), 3 deletions(-)
> ?create mode 100644 arch/arm/mach-exynos4/include/mach/regs-mct.h
> ?create mode 100644 arch/arm/mach-exynos4/mct.c
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index ec3bf98..b4db99b 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1366,7 +1366,7 @@ config LOCAL_TIMERS
> ? ? ? ?bool "Use local timer interrupts"
> ? ? ? ?depends on SMP
> ? ? ? ?default y
> - ? ? ? select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP
> + ? ? ? select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_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/mach-exynos4/Kconfig b/arch/arm/mach-exynos4/Kconfig
> index ad03840..77b5d93 100644
> --- a/arch/arm/mach-exynos4/Kconfig
> +++ b/arch/arm/mach-exynos4/Kconfig
> @@ -15,6 +15,11 @@ config CPU_EXYNOS4210
> ? ? ? ?help
> ? ? ? ? ?Enable EXYNOS4210 CPU support
>
> +config EXYNOS4_MCT
> + ? ? ? bool "Kernel timer support by MCT"
> + ? ? ? help
> + ? ? ? ? Use MCT (Multi Core Timer) as kernel timers
> +
> ?config EXYNOS4_DEV_PD
> ? ? ? ?bool
> ? ? ? ?help
> diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
> index 0558235..de20b91 100644
> --- a/arch/arm/mach-exynos4/Makefile
> +++ b/arch/arm/mach-exynos4/Makefile
> @@ -13,11 +13,18 @@ obj- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?:=
> ?# Core support for EXYNOS4 system
>
> ?obj-$(CONFIG_CPU_EXYNOS4210) ? += cpu.o init.o clock.o irq-combiner.o
> -obj-$(CONFIG_CPU_EXYNOS4210) ? += setup-i2c0.o time.o gpiolib.o irq-eint.o dma.o
> +obj-$(CONFIG_CPU_EXYNOS4210) ? += setup-i2c0.o gpiolib.o irq-eint.o dma.o
> ?obj-$(CONFIG_CPU_FREQ) ? ? ? ? += cpufreq.o
>
> ?obj-$(CONFIG_SMP) ? ? ? ? ? ? ?+= platsmp.o headsmp.o
> -obj-$(CONFIG_LOCAL_TIMERS) ? ? += localtimer.o
> +
> +ifeq ($(CONFIG_EXYNOS4_MCT),y)
> +obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?+= mct.o
> +else
> +obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?+= time.o
> +obj-$(CONFIG_LOCAL_TIMERS) ? ? += localtimer.o
> +endif
> +
> ?obj-$(CONFIG_HOTPLUG_CPU) ? ? ?+= hotplug.o
>
> ?# machine support
> diff --git a/arch/arm/mach-exynos4/include/mach/regs-mct.h b/arch/arm/mach-exynos4/include/mach/regs-mct.h
> new file mode 100644
> index 0000000..ca9c843
> --- /dev/null
> +++ b/arch/arm/mach-exynos4/include/mach/regs-mct.h
> @@ -0,0 +1,52 @@
> +/* 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_L0_BASE ? ? ? ? ? ?EXYNOS4_MCTREG(0x300)
> +#define EXYNOS4_MCT_L1_BASE ? ? ? ? ? ?EXYNOS4_MCTREG(0x400)
> +
> +#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-exynos4/mct.c b/arch/arm/mach-exynos4/mct.c
> new file mode 100644
> index 0000000..6bb4b50
> --- /dev/null
> +++ b/arch/arm/mach-exynos4/mct.c
> @@ -0,0 +1,421 @@
> +/* linux/arch/arm/mach-exynos4/mct.c
> + *
> + * Copyright (c) 2011 Samsung Electronics Co., Ltd.
> + * ? ? ? ? ? ? http://www.samsung.com
> + *
> + * EXYNOS4 MCT(Multi-Core Timer) support
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> +*/
> +
> +#include <linux/sched.h>
> +#include <linux/interrupt.h>
> +#include <linux/irq.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/clockchips.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/percpu.h>
> +
> +#include <mach/map.h>
> +#include <mach/regs-mct.h>
> +#include <asm/mach/time.h>
> +
> +static unsigned long clk_cnt_per_tick;
> +static unsigned long clk_rate;
> +
> +struct mct_clock_event_device {
> + ? ? ? struct clock_event_device *evt;
> + ? ? ? void __iomem *base;
> +};
> +
> +struct mct_clock_event_device mct_tick[2];
> +
> +static void exynos4_mct_write(unsigned int value, void *addr)
> +{
> + ? ? ? void __iomem *stat_addr;
> + ? ? ? u32 mask;
> + ? ? ? u32 i;
> +
> + ? ? ? __raw_writel(value, addr);
> +
> + ? ? ? switch ((u32) addr) {
> + ? ? ? case (u32) 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:
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_G_WSTAT;
> + ? ? ? ? ? ? ? mask = 1 << 0; ? ? ? ? ?/* G_COMP0_L write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32) 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:
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_G_WSTAT;
> + ? ? ? ? ? ? ? mask = 1 << 2; ? ? ? ? ?/* G_COMP0_ADD_INCR write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32) 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:
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
> + ? ? ? ? ? ? ? mask = 1 << 1; ? ? ? ? ?/* G_CNT_U write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCON_OFFSET):
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
> + ? ? ? ? ? ? ? mask = 1 << 3; ? ? ? ? ?/* L0_TCON write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCON_OFFSET):
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
> + ? ? ? ? ? ? ? mask = 1 << 3; ? ? ? ? ?/* L1_TCON write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_TCNTB_OFFSET):
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
> + ? ? ? ? ? ? ? mask = 1 << 0; ? ? ? ? ?/* L0_TCNTB write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_TCNTB_OFFSET):
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
> + ? ? ? ? ? ? ? mask = 1 << 0; ? ? ? ? ?/* L1_TCNTB write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32)(EXYNOS4_MCT_L0_BASE + MCT_L_ICNTB_OFFSET):
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_L0_BASE + MCT_L_WSTAT_OFFSET;
> + ? ? ? ? ? ? ? mask = 1 << 1; ? ? ? ? ?/* L0_ICNTB write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? case (u32)(EXYNOS4_MCT_L1_BASE + MCT_L_ICNTB_OFFSET):
> + ? ? ? ? ? ? ? stat_addr = EXYNOS4_MCT_L1_BASE + MCT_L_WSTAT_OFFSET;
> + ? ? ? ? ? ? ? mask = 1 << 1; ? ? ? ? ?/* L1_ICNTB write status */
> + ? ? ? ? ? ? ? break;
> + ? ? ? default:
> + ? ? ? ? ? ? ? return;
> + ? ? ? }
> +
> + ? ? ? /* 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);
> + ? ? ? ? ? ? ? ? ? ? ? return;
> + ? ? ? ? ? ? ? }
> +
> + ? ? ? panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
> +}
> +
> +/* Clocksource handling */
> +static void exynos4_mct_frc_start(u32 hi, u32 lo)
> +{
> + ? ? ? u32 reg;
> +
> + ? ? ? 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 |= MCT_G_TCON_START;
> + ? ? ? exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
> +}
> +
> +static cycle_t exynos4_frc_read(struct clocksource *cs)
> +{
> + ? ? ? unsigned int lo, hi;
> + ? ? ? u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
> +
> + ? ? ? do {
> + ? ? ? ? ? ? ? hi = hi2;
> + ? ? ? ? ? ? ? lo = __raw_readl(EXYNOS4_MCT_G_CNT_L);
> + ? ? ? ? ? ? ? hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
> + ? ? ? } while (hi != hi2);
> +
> + ? ? ? return ((cycle_t)hi << 32) | lo;
> +}
> +
> +struct clocksource mct_frc = {
> + ? ? ? .name ? ? ? ? ? = "mct-frc",
> + ? ? ? .rating ? ? ? ? = 400,
> + ? ? ? .read ? ? ? ? ? = exynos4_frc_read,
> + ? ? ? .mask ? ? ? ? ? = CLOCKSOURCE_MASK(64),
> + ? ? ? .flags ? ? ? ? ?= CLOCK_SOURCE_IS_CONTINUOUS,
> +};
> +
> +static void __init exynos4_clocksource_init(void)
> +{
> + ? ? ? exynos4_mct_frc_start(0, 0);
> +
> + ? ? ? if (clocksource_register_hz(&mct_frc, clk_rate))
> + ? ? ? ? ? ? ? panic("%s: can't register clocksource\n", mct_frc.name);
> +}
> +
> +static void exynos4_mct_comp0_stop(void)
> +{
> + ? ? ? unsigned int tcon;
> +
> + ? ? ? tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
> + ? ? ? tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
> +
> + ? ? ? exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
> + ? ? ? exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
> +}
> +
> +static void exynos4_mct_comp0_start(enum clock_event_mode mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? unsigned long cycles)
> +{
> + ? ? ? unsigned int tcon;
> + ? ? ? cycle_t comp_cycle;
> +
> + ? ? ? tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
> +
> + ? ? ? if (mode == CLOCK_EVT_MODE_PERIODIC) {
> + ? ? ? ? ? ? ? tcon |= MCT_G_TCON_COMP0_AUTO_INC;
> + ? ? ? ? ? ? ? exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
> + ? ? ? }
> +
> + ? ? ? comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
> + ? ? ? exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
> + ? ? ? exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
> +
> + ? ? ? exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
> +
> + ? ? ? tcon |= MCT_G_TCON_COMP0_ENABLE;
> + ? ? ? exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
> +}
> +
> +static int exynos4_comp_set_next_event(unsigned long cycles,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct clock_event_device *evt)
> +{
> + ? ? ? exynos4_mct_comp0_start(evt->mode, cycles);
> +
> + ? ? ? return 0;
> +}
> +
> +static void exynos4_comp_set_mode(enum clock_event_mode mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct clock_event_device *evt)
> +{
> + ? ? ? exynos4_mct_comp0_stop();
> +
> + ? ? ? switch (mode) {
> + ? ? ? case CLOCK_EVT_MODE_PERIODIC:
> + ? ? ? ? ? ? ? exynos4_mct_comp0_start(mode, clk_cnt_per_tick);
> + ? ? ? ? ? ? ? break;
> +
> + ? ? ? case CLOCK_EVT_MODE_ONESHOT:
> + ? ? ? case CLOCK_EVT_MODE_UNUSED:
> + ? ? ? case CLOCK_EVT_MODE_SHUTDOWN:
> + ? ? ? case CLOCK_EVT_MODE_RESUME:
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +}
> +
> +static struct clock_event_device mct_comp_device = {
> + ? ? ? .name ? ? ? ? ? = "mct-comp",
> + ? ? ? .features ? ? ? = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
> + ? ? ? .rating ? ? ? ? = 250,
> + ? ? ? .set_next_event = exynos4_comp_set_next_event,
> + ? ? ? .set_mode ? ? ? = exynos4_comp_set_mode,
> +};
> +
> +static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
> +{
> + ? ? ? struct clock_event_device *evt = dev_id;
> +
> + ? ? ? exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
> +
> + ? ? ? evt->event_handler(evt);
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +static struct irqaction mct_comp_event_irq = {
> + ? ? ? .name ? ? ? ? ? = "mct_comp_irq",
> + ? ? ? .flags ? ? ? ? ?= IRQF_TIMER | IRQF_IRQPOLL,
> + ? ? ? .handler ? ? ? ?= exynos4_mct_comp_isr,
> + ? ? ? .dev_id ? ? ? ? = &mct_comp_device,
> +};
> +
> +static void exynos4_clockevent_init(void)
> +{
> + ? ? ? clk_cnt_per_tick = clk_rate / 2 / HZ;
> +
> + ? ? ? clockevents_calc_mult_shift(&mct_comp_device, clk_rate / 2, 5);
> + ? ? ? mct_comp_device.max_delta_ns =
> + ? ? ? ? ? ? ? clockevent_delta2ns(0xffffffff, &mct_comp_device);
> + ? ? ? mct_comp_device.min_delta_ns =
> + ? ? ? ? ? ? ? clockevent_delta2ns(0xf, &mct_comp_device);
> + ? ? ? mct_comp_device.cpumask = cpumask_of(0);
> + ? ? ? clockevents_register_device(&mct_comp_device);
> +
> + ? ? ? setup_irq(IRQ_MCT_G0, &mct_comp_event_irq);
> +}
> +
> +#ifdef CONFIG_LOCAL_TIMERS
> +/* Clock event handling */
> +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;
> +
> + ? ? ? tmp = __raw_readl(addr);
> + ? ? ? if (tmp & mask) {
> + ? ? ? ? ? ? ? tmp &= ~mask;
> + ? ? ? ? ? ? ? exynos4_mct_write(tmp, addr);
> + ? ? ? }
> +}
> +
> +static void exynos4_mct_tick_start(unsigned long cycles,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct mct_clock_event_device *mevt)
> +{
> + ? ? ? unsigned long tmp;
> +
> + ? ? ? exynos4_mct_tick_stop(mevt);
> +
> + ? ? ? tmp = (1 << 31) | cycles; ? ? ? /* MCT_L_UPDATE_ICNTB */
> +
> + ? ? ? /* update interrupt count buffer */
> + ? ? ? exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
> +
> + ? ? ? /* enable MCT tick interupt */
> + ? ? ? exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
> +
> + ? ? ? tmp = __raw_readl(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);
> +}
> +
> +static int exynos4_tick_set_next_event(unsigned long cycles,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct clock_event_device *evt)
> +{
> + ? ? ? struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
> +
> + ? ? ? exynos4_mct_tick_start(cycles, mevt);
> +
> + ? ? ? return 0;
> +}
> +
> +static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?struct clock_event_device *evt)
> +{
> + ? ? ? struct mct_clock_event_device *mevt = &mct_tick[smp_processor_id()];
> +
> + ? ? ? exynos4_mct_tick_stop(mevt);
> +
> + ? ? ? switch (mode) {
> + ? ? ? case CLOCK_EVT_MODE_PERIODIC:
> + ? ? ? ? ? ? ? exynos4_mct_tick_start(clk_cnt_per_tick, mevt);
> + ? ? ? ? ? ? ? break;
> +
> + ? ? ? case CLOCK_EVT_MODE_ONESHOT:
> + ? ? ? case CLOCK_EVT_MODE_UNUSED:
> + ? ? ? case CLOCK_EVT_MODE_SHUTDOWN:
> + ? ? ? case CLOCK_EVT_MODE_RESUME:
> + ? ? ? ? ? ? ? break;
> + ? ? ? }
> +}
> +
> +static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
> +{
> + ? ? ? struct mct_clock_event_device *mevt = dev_id;
> + ? ? ? struct clock_event_device *evt = mevt->evt;
> +
> + ? ? ? /*
> + ? ? ? ?* This is for supporting oneshot mode.
> + ? ? ? ?* Mct would generate interrupt periodically
> + ? ? ? ?* without explicit stopping.
> + ? ? ? ?*/
> + ? ? ? if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
> + ? ? ? ? ? ? ? exynos4_mct_tick_stop(mevt);
> +
> + ? ? ? /* Clear the MCT tick interrupt */
> + ? ? ? exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
> +
> + ? ? ? evt->event_handler(evt);
> +
> + ? ? ? return IRQ_HANDLED;
> +}
> +
> +static struct irqaction mct_tick0_event_irq = {
> + ? ? ? .name ? ? ? ? ? = "mct_tick0_irq",
> + ? ? ? .flags ? ? ? ? ?= IRQF_TIMER | IRQF_NOBALANCING,
> + ? ? ? .handler ? ? ? ?= exynos4_mct_tick_isr,
> +};
> +
> +static struct irqaction mct_tick1_event_irq = {
> + ? ? ? .name ? ? ? ? ? = "mct_tick1_irq",
> + ? ? ? .flags ? ? ? ? ?= IRQF_TIMER | IRQF_NOBALANCING,
> + ? ? ? .handler ? ? ? ?= exynos4_mct_tick_isr,
> +};
> +
> +static void exynos4_mct_tick_init(struct clock_event_device *evt)
> +{
> + ? ? ? unsigned int cpu = smp_processor_id();
> +
> + ? ? ? mct_tick[cpu].evt = evt;
> +
> + ? ? ? if (cpu == 0) {
> + ? ? ? ? ? ? ? mct_tick[cpu].base = EXYNOS4_MCT_L0_BASE;
> + ? ? ? ? ? ? ? evt->name = "mct_tick0";
> + ? ? ? } else {
> + ? ? ? ? ? ? ? mct_tick[cpu].base = EXYNOS4_MCT_L1_BASE;
> + ? ? ? ? ? ? ? evt->name = "mct_tick1";
> + ? ? ? }
> +
> + ? ? ? evt->cpumask = cpumask_of(cpu);
> + ? ? ? evt->set_next_event = exynos4_tick_set_next_event;
> + ? ? ? evt->set_mode = exynos4_tick_set_mode;
> + ? ? ? evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
> + ? ? ? evt->rating = 450;
> +
> + ? ? ? clockevents_calc_mult_shift(evt, clk_rate / 2, 5);
> + ? ? ? evt->max_delta_ns =
> + ? ? ? ? ? ? ? clockevent_delta2ns(0x7fffffff, evt);
> + ? ? ? evt->min_delta_ns =
> + ? ? ? ? ? ? ? clockevent_delta2ns(0xf, evt);
> +
> + ? ? ? clockevents_register_device(evt);
> +
> + ? ? ? exynos4_mct_write(0x1, mct_tick[cpu].base + MCT_L_TCNTB_OFFSET);
> +
> + ? ? ? if (cpu == 0) {
> + ? ? ? ? ? ? ? mct_tick0_event_irq.dev_id = &mct_tick[cpu];
> + ? ? ? ? ? ? ? setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
> + ? ? ? } else {
> + ? ? ? ? ? ? ? mct_tick1_event_irq.dev_id = &mct_tick[cpu];
> + ? ? ? ? ? ? ? irq_set_affinity(IRQ_MCT1, cpumask_of(1));
> + ? ? ? ? ? ? ? setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
> + ? ? ? }
> +}
> +
> +/* Setup the local clock events for a CPU */
> +void __cpuinit local_timer_setup(struct clock_event_device *evt)
> +{
> + ? ? ? exynos4_mct_tick_init(evt);
> +}
> +
> +int local_timer_ack(void)
> +{
> + ? ? ? return 0;
> +}
> +
> +#endif /* CONFIG_LOCAL_TIMERS */
> +
> +static void __init exynos4_timer_resources(void)
> +{
> + ? ? ? struct clk *mct_clk;
> + ? ? ? mct_clk = clk_get(NULL, "xtal");
> +
> + ? ? ? clk_rate = clk_get_rate(mct_clk);
> +}
> +
> +static void __init exynos4_timer_init(void)
> +{
> + ? ? ? exynos4_timer_resources();
> + ? ? ? exynos4_clocksource_init();
> + ? ? ? exynos4_clockevent_init();
> +}
> +
> +struct sys_timer exynos4_timer = {
> + ? ? ? .init ? ? ? ? ? = exynos4_timer_init,
> +};
> --
> 1.7.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at ?http://vger.kernel.org/majordomo-info.html
>

^ permalink raw reply	[flat|nested] 12+ messages in thread

* RE: [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
  2011-03-01  3:14     ` Kyungmin Park
@ 2011-03-02  7:29       ` Kukjin Kim
  -1 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-03-02  7:29 UTC (permalink / raw)
  To: 'Kyungmin Park'
  Cc: linux-samsung-soc, ben-linux, 'Russell King',
	'Changhwan Youn',
	linux-arm-kernel, 'Marek Szyprowski'

Kyungmin Park wrote:
> 
> Hi,
> 
> MCT is only possible from EVT1.0 or later. The current universal_c210
> used the EVT0 version.
> So can you remove the ifdef and board can select which timer is used?
> Please refer the omap implementation and how to use it.
> 
Basically, "CONFIG_EXYNOS4_MCT" can be selected in kernel menuconfig.
It means default one is using local timer which is included in every
Exynos4210.

> If we use the same configuration, It will be boot failed at
> universal_c210 board.
> 
No, see as above.

One more basically, each machine specific CONFIGs should be selected in
kernel menuconfig after "make exynos4_defconfig". Because each machine
supports different components on each one now.

As a note, however, I don't want to add each machine's defconfig. Maybe
Russell also.

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.

> Thank you,
> Kyungmin Park
> 
> On Mon, Feb 28, 2011 at 2:16 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> > From: Changhwan Youn <chaos.youn@samsung.com>
> >
> > The Multi-Core Timer(MCT) of EXYNOS4 is designed for implementing
> > clock source timer and clock event timers. This patch implements
> > 1 clock source timer with 64 bit free running counter of MCT and
> > 2 clock event timers with two of 31-bit tick counters.
> >
> > Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
> > Cc: Ben Dooks <ben-linux@fluff.org>
> > Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > ---
> > Changes since v3:
> > - Re-worked based on exynos4
> > - Changed member of clock_evetn_device like following
> >  struct mct_clock_event_device {
> >        struct clock_event_device *evt;
> >  ...
> > - Address comments from Russell King
> >  Added static into "irqreturn_t exynos4_mct_comp_isr()"
> >  Added .dev_id member into "struct irqaction mct_comp_event_irq"
> >
> > Changes since v2:
> > - Addressed comments from Russell King
> >  Added mct_clock_event_device structure
> > - Removed suspend()/resume() which are not used now
> >
> > Changes since v1:
> > - Addressed comments from Russell King
> >  (implemented global timer, used local timer and so on)
> > - Fixed small things
> >
> >  arch/arm/Kconfig                              |    2 +-
> >  arch/arm/mach-exynos4/Kconfig                 |    5 +
> >  arch/arm/mach-exynos4/Makefile                |   11 +-
> >  arch/arm/mach-exynos4/include/mach/regs-mct.h |   52 +++
> >  arch/arm/mach-exynos4/mct.c                   |  421
> +++++++++++++++++++++++++
> >  5 files changed, 488 insertions(+), 3 deletions(-)
> >  create mode 100644 arch/arm/mach-exynos4/include/mach/regs-mct.h
> >  create mode 100644 arch/arm/mach-exynos4/mct.c
> >
> > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> > index ec3bf98..b4db99b 100644
> > --- a/arch/arm/Kconfig
> > +++ b/arch/arm/Kconfig
> > @@ -1366,7 +1366,7 @@ config LOCAL_TIMERS
> >        bool "Use local timer interrupts"
> >        depends on SMP
> >        default y
> > -       select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP
> > +       select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_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/mach-exynos4/Kconfig
b/arch/arm/mach-exynos4/Kconfig
> > index ad03840..77b5d93 100644
> > --- a/arch/arm/mach-exynos4/Kconfig
> > +++ b/arch/arm/mach-exynos4/Kconfig
> > @@ -15,6 +15,11 @@ config CPU_EXYNOS4210
> >        help
> >          Enable EXYNOS4210 CPU support
> >
> > +config EXYNOS4_MCT
> > +       bool "Kernel timer support by MCT"
> > +       help
> > +         Use MCT (Multi Core Timer) as kernel timers
> > +
> >  config EXYNOS4_DEV_PD
> >        bool
> >        help
> > diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-
> exynos4/Makefile
> > index 0558235..de20b91 100644
> > --- a/arch/arm/mach-exynos4/Makefile
> > +++ b/arch/arm/mach-exynos4/Makefile
> > @@ -13,11 +13,18 @@ obj-                                :=
> >  # Core support for EXYNOS4 system
> >
> >  obj-$(CONFIG_CPU_EXYNOS4210)   += cpu.o init.o clock.o irq-combiner.o
> > -obj-$(CONFIG_CPU_EXYNOS4210)   += setup-i2c0.o time.o gpiolib.o
irq-eint.o
> dma.o
> > +obj-$(CONFIG_CPU_EXYNOS4210)   += setup-i2c0.o gpiolib.o irq-eint.o
dma.o
> >  obj-$(CONFIG_CPU_FREQ)         += cpufreq.o
> >
> >  obj-$(CONFIG_SMP)              += platsmp.o headsmp.o
> > -obj-$(CONFIG_LOCAL_TIMERS)     += localtimer.o
> > +
> > +ifeq ($(CONFIG_EXYNOS4_MCT),y)
> > +obj-y                          += mct.o
> > +else
> > +obj-y                          += time.o
> > +obj-$(CONFIG_LOCAL_TIMERS)     += localtimer.o
> > +endif
> > +
> >  obj-$(CONFIG_HOTPLUG_CPU)      += hotplug.o
> >
> >  # machine support

(snip)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
@ 2011-03-02  7:29       ` Kukjin Kim
  0 siblings, 0 replies; 12+ messages in thread
From: Kukjin Kim @ 2011-03-02  7:29 UTC (permalink / raw)
  To: linux-arm-kernel

Kyungmin Park wrote:
> 
> Hi,
> 
> MCT is only possible from EVT1.0 or later. The current universal_c210
> used the EVT0 version.
> So can you remove the ifdef and board can select which timer is used?
> Please refer the omap implementation and how to use it.
> 
Basically, "CONFIG_EXYNOS4_MCT" can be selected in kernel menuconfig.
It means default one is using local timer which is included in every
Exynos4210.

> If we use the same configuration, It will be boot failed at
> universal_c210 board.
> 
No, see as above.

One more basically, each machine specific CONFIGs should be selected in
kernel menuconfig after "make exynos4_defconfig". Because each machine
supports different components on each one now.

As a note, however, I don't want to add each machine's defconfig. Maybe
Russell also.

Thanks.

Best regards,
Kgene.
--
Kukjin Kim <kgene.kim@samsung.com>, Senior Engineer,
SW Solution Development Team, Samsung Electronics Co., Ltd.

> Thank you,
> Kyungmin Park
> 
> On Mon, Feb 28, 2011 at 2:16 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> > From: Changhwan Youn <chaos.youn@samsung.com>
> >
> > The Multi-Core Timer(MCT) of EXYNOS4 is designed for implementing
> > clock source timer and clock event timers. This patch implements
> > 1 clock source timer with 64 bit free running counter of MCT and
> > 2 clock event timers with two of 31-bit tick counters.
> >
> > Signed-off-by: Changhwan Youn <chaos.youn@samsung.com>
> > Cc: Ben Dooks <ben-linux@fluff.org>
> > Cc: Russell King <rmk+kernel@arm.linux.org.uk>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > ---
> > Changes since v3:
> > - Re-worked based on exynos4
> > - Changed member of clock_evetn_device like following
> > ?struct mct_clock_event_device {
> > ? ? ? ?struct clock_event_device *evt;
> > ?...
> > - Address comments from Russell King
> > ?Added static into "irqreturn_t exynos4_mct_comp_isr()"
> > ?Added .dev_id member into "struct irqaction mct_comp_event_irq"
> >
> > Changes since v2:
> > - Addressed comments from Russell King
> > ?Added mct_clock_event_device structure
> > - Removed suspend()/resume() which are not used now
> >
> > Changes since v1:
> > - Addressed comments from Russell King
> > ?(implemented global timer, used local timer and so on)
> > - Fixed small things
> >
> > ?arch/arm/Kconfig ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?| ? ?2 +-
> > ?arch/arm/mach-exynos4/Kconfig ? ? ? ? ? ? ? ? | ? ?5 +
> > ?arch/arm/mach-exynos4/Makefile ? ? ? ? ? ? ? ?| ? 11 +-
> > ?arch/arm/mach-exynos4/include/mach/regs-mct.h | ? 52 +++
> > ?arch/arm/mach-exynos4/mct.c ? ? ? ? ? ? ? ? ? | ?421
> +++++++++++++++++++++++++
> > ?5 files changed, 488 insertions(+), 3 deletions(-)
> > ?create mode 100644 arch/arm/mach-exynos4/include/mach/regs-mct.h
> > ?create mode 100644 arch/arm/mach-exynos4/mct.c
> >
> > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> > index ec3bf98..b4db99b 100644
> > --- a/arch/arm/Kconfig
> > +++ b/arch/arm/Kconfig
> > @@ -1366,7 +1366,7 @@ config LOCAL_TIMERS
> > ? ? ? ?bool "Use local timer interrupts"
> > ? ? ? ?depends on SMP
> > ? ? ? ?default y
> > - ? ? ? select HAVE_ARM_TWD if !ARCH_MSM_SCORPIONMP
> > + ? ? ? select HAVE_ARM_TWD if (!ARCH_MSM_SCORPIONMP && !EXYNOS4_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/mach-exynos4/Kconfig
b/arch/arm/mach-exynos4/Kconfig
> > index ad03840..77b5d93 100644
> > --- a/arch/arm/mach-exynos4/Kconfig
> > +++ b/arch/arm/mach-exynos4/Kconfig
> > @@ -15,6 +15,11 @@ config CPU_EXYNOS4210
> > ? ? ? ?help
> > ? ? ? ? ?Enable EXYNOS4210 CPU support
> >
> > +config EXYNOS4_MCT
> > + ? ? ? bool "Kernel timer support by MCT"
> > + ? ? ? help
> > + ? ? ? ? Use MCT (Multi Core Timer) as kernel timers
> > +
> > ?config EXYNOS4_DEV_PD
> > ? ? ? ?bool
> > ? ? ? ?help
> > diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-
> exynos4/Makefile
> > index 0558235..de20b91 100644
> > --- a/arch/arm/mach-exynos4/Makefile
> > +++ b/arch/arm/mach-exynos4/Makefile
> > @@ -13,11 +13,18 @@ obj- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?:=
> > ?# Core support for EXYNOS4 system
> >
> > ?obj-$(CONFIG_CPU_EXYNOS4210) ? += cpu.o init.o clock.o irq-combiner.o
> > -obj-$(CONFIG_CPU_EXYNOS4210) ? += setup-i2c0.o time.o gpiolib.o
irq-eint.o
> dma.o
> > +obj-$(CONFIG_CPU_EXYNOS4210) ? += setup-i2c0.o gpiolib.o irq-eint.o
dma.o
> > ?obj-$(CONFIG_CPU_FREQ) ? ? ? ? += cpufreq.o
> >
> > ?obj-$(CONFIG_SMP) ? ? ? ? ? ? ?+= platsmp.o headsmp.o
> > -obj-$(CONFIG_LOCAL_TIMERS) ? ? += localtimer.o
> > +
> > +ifeq ($(CONFIG_EXYNOS4_MCT),y)
> > +obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?+= mct.o
> > +else
> > +obj-y ? ? ? ? ? ? ? ? ? ? ? ? ?+= time.o
> > +obj-$(CONFIG_LOCAL_TIMERS) ? ? += localtimer.o
> > +endif
> > +
> > ?obj-$(CONFIG_HOTPLUG_CPU) ? ? ?+= hotplug.o
> >
> > ?# machine support

(snip)

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
  2011-03-02  7:29       ` Kukjin Kim
@ 2011-03-02  7:39         ` Kyungmin Park
  -1 siblings, 0 replies; 12+ messages in thread
From: Kyungmin Park @ 2011-03-02  7:39 UTC (permalink / raw)
  To: Kukjin Kim
  Cc: linux-arm-kernel, linux-samsung-soc, ben-linux, Changhwan Youn,
	Russell King, Marek Szyprowski

On Wed, Mar 2, 2011 at 4:29 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> Kyungmin Park wrote:
>>
>> Hi,
>>
>> MCT is only possible from EVT1.0 or later. The current universal_c210
>> used the EVT0 version.
>> So can you remove the ifdef and board can select which timer is used?
>> Please refer the omap implementation and how to use it.
>>
> Basically, "CONFIG_EXYNOS4_MCT" can be selected in kernel menuconfig.
> It means default one is using local timer which is included in every
> Exynos4210.
>
>> If we use the same configuration, It will be boot failed at
>> universal_c210 board.
>>
> No, see as above.
>
> One more basically, each machine specific CONFIGs should be selected in
> kernel menuconfig after "make exynos4_defconfig". Because each machine
> supports different components on each one now.
>
> As a note, however, I don't want to add each machine's defconfig. Maybe
> Russell also.

I agree, but there are better way, make it compile both and select
timer for each board.
Of course no need to add any statement if default timer is used.

BTW, What's the purpose of exynos4_defconfig? as you said, If I want
to use the MCT for EVT1 board,
then make defconfig and select it again? why do the these job?

Thank you,
Kyungmin Park

^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT
@ 2011-03-02  7:39         ` Kyungmin Park
  0 siblings, 0 replies; 12+ messages in thread
From: Kyungmin Park @ 2011-03-02  7:39 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 2, 2011 at 4:29 PM, Kukjin Kim <kgene.kim@samsung.com> wrote:
> Kyungmin Park wrote:
>>
>> Hi,
>>
>> MCT is only possible from EVT1.0 or later. The current universal_c210
>> used the EVT0 version.
>> So can you remove the ifdef and board can select which timer is used?
>> Please refer the omap implementation and how to use it.
>>
> Basically, "CONFIG_EXYNOS4_MCT" can be selected in kernel menuconfig.
> It means default one is using local timer which is included in every
> Exynos4210.
>
>> If we use the same configuration, It will be boot failed at
>> universal_c210 board.
>>
> No, see as above.
>
> One more basically, each machine specific CONFIGs should be selected in
> kernel menuconfig after "make exynos4_defconfig". Because each machine
> supports different components on each one now.
>
> As a note, however, I don't want to add each machine's defconfig. Maybe
> Russell also.

I agree, but there are better way, make it compile both and select
timer for each board.
Of course no need to add any statement if default timer is used.

BTW, What's the purpose of exynos4_defconfig? as you said, If I want
to use the MCT for EVT1 board,
then make defconfig and select it again? why do the these job?

Thank you,
Kyungmin Park

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2011-03-02  7:39 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-02-28  5:16 [PATCH 1/3] ARM: EXYNOS4: Add SYSTIMER IO Address mapping for MCT Kukjin Kim
2011-02-28  5:16 ` Kukjin Kim
2011-02-28  5:16 ` [PATCH 2/3] ARM: EXYNOS4: Add irq definition for kernel global timer Kukjin Kim
2011-02-28  5:16   ` Kukjin Kim
2011-02-28  5:16 ` [PATCH V4] ARM: EXYNOS4: Implement kernel timers using MCT Kukjin Kim
2011-02-28  5:16   ` Kukjin Kim
2011-03-01  3:14   ` Kyungmin Park
2011-03-01  3:14     ` Kyungmin Park
2011-03-02  7:29     ` Kukjin Kim
2011-03-02  7:29       ` Kukjin Kim
2011-03-02  7:39       ` Kyungmin Park
2011-03-02  7:39         ` Kyungmin Park

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.