1 /* linux/arch/arm/mach-s5pv310/mct.c
3 * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
6 * S5PV310 (and compatible) HRT support
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License version 2 as
10 * published by the Free Software Foundation.
13 #include <linux/sched.h>
14 #include <linux/interrupt.h>
15 #include <linux/irq.h>
16 #include <linux/err.h>
17 #include <linux/clk.h>
18 #include <linux/clockchips.h>
19 #include <linux/platform_device.h>
20 #include <linux/delay.h>
23 #include <mach/regs-mct.h>
24 #include <asm/mach/time.h>
26 #ifdef CONFIG_SEC_DEBUG_KERNEL_HISTORY
27 #include <plat/debug-kernel-history.h> /* YUBAEK 20110905 RAMDUMP_DEBUG : added debug-kernel-history.h */
30 static unsigned long clk_cnt_per_tick;
31 static unsigned long clk_rate;
32 static cycle_t time_suspended;
33 static u32 sched_mult;
34 static u32 sched_shift;
41 static void s5pv310_mct_write(unsigned int value, void *addr)
43 void __iomem *stat_addr;
47 __raw_writel(value, addr);
50 case (u32) S5PV310_MCT_G_TCON:
51 stat_addr = S5PV310_MCT_G_WSTAT;
52 mask = G_WSTAT_TCON_STAT;
54 case (u32) S5PV310_MCT_G_CNT_L:
55 stat_addr = S5PV310_MCT_G_CNT_WSTAT;
58 case (u32) S5PV310_MCT_G_CNT_U:
59 stat_addr = S5PV310_MCT_G_CNT_WSTAT;
62 case (u32) S5PV310_MCT_L0_TCON:
63 stat_addr = S5PV310_MCT_L0_WSTAT;
66 case (u32) S5PV310_MCT_L1_TCON:
67 stat_addr = S5PV310_MCT_L1_WSTAT;
70 case (u32) S5PV310_MCT_L0_TCNTB:
71 stat_addr = S5PV310_MCT_L0_WSTAT;
74 case (u32) S5PV310_MCT_L1_TCNTB:
75 stat_addr = S5PV310_MCT_L1_WSTAT;
78 case (u32) S5PV310_MCT_L0_ICNTB:
79 stat_addr = S5PV310_MCT_L0_WSTAT;
82 case (u32) S5PV310_MCT_L1_ICNTB:
83 stat_addr = S5PV310_MCT_L1_WSTAT;
90 /* Wait maximum 1 ms until written values are applied */
91 for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
92 if (__raw_readl(stat_addr) & mask) {
93 __raw_writel(mask, stat_addr);
97 /* Workaround: Try again if fail */
98 __raw_writel(value, addr);
100 for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
101 if (__raw_readl(stat_addr) & mask) {
102 __raw_writel(mask, stat_addr);
106 panic("mct hangs after writing %d to 0x%x address\n", value, (u32) addr);
109 static void s5pv310_mct_tick_stop(enum mct_tick_type type)
115 addr = S5PV310_MCT_L0_TCON;
117 addr = S5PV310_MCT_L1_TCON;
119 reg = __raw_readl(addr);
120 reg &= ~(L_TCON_INT_START | L_TCON_TIMER_START);
121 s5pv310_mct_write(reg, addr);
124 static void s5pv310_mct_tick_start(enum mct_tick_type type,
125 unsigned long cycles)
130 s5pv310_mct_tick_stop(type);
133 s5pv310_mct_write(L_UPDATE_ICNTB | cycles, S5PV310_MCT_L0_ICNTB);
134 s5pv310_mct_write(L_INT_ENB_CNTIE, S5PV310_MCT_L0_INT_ENB);
135 addr = S5PV310_MCT_L0_TCON;
137 s5pv310_mct_write(L_UPDATE_ICNTB | cycles, S5PV310_MCT_L1_ICNTB);
138 s5pv310_mct_write(L_INT_ENB_CNTIE, S5PV310_MCT_L1_INT_ENB);
139 addr = S5PV310_MCT_L1_TCON;
142 reg = __raw_readl(addr);
143 reg |= L_TCON_INT_START | L_TCON_TIMER_START | L_TCON_INTERVAL_MODE;
144 s5pv310_mct_write(reg, addr);
147 static inline int s5pv310_tick_set_next_event(enum mct_tick_type type,
148 unsigned long cycles)
150 s5pv310_mct_tick_start(type, cycles);
154 static inline void s5pv310_tick_set_mode(enum mct_tick_type type,
155 enum clock_event_mode mode)
157 s5pv310_mct_tick_stop(type);
160 case CLOCK_EVT_MODE_PERIODIC:
161 s5pv310_mct_tick_start(type, clk_cnt_per_tick);
163 case CLOCK_EVT_MODE_ONESHOT:
164 case CLOCK_EVT_MODE_UNUSED:
165 case CLOCK_EVT_MODE_SHUTDOWN:
166 case CLOCK_EVT_MODE_RESUME:
171 static int s5pv310_tick0_set_next_event(unsigned long cycles,
172 struct clock_event_device *evt)
174 return s5pv310_tick_set_next_event(TICK0, cycles);
177 static int s5pv310_tick1_set_next_event(unsigned long cycles,
178 struct clock_event_device *evt)
180 return s5pv310_tick_set_next_event(TICK1, cycles);
183 static void s5pv310_tick0_set_mode(enum clock_event_mode mode,
184 struct clock_event_device *evt)
186 s5pv310_tick_set_mode(TICK0, mode);
189 static void s5pv310_tick1_set_mode(enum clock_event_mode mode,
190 struct clock_event_device *evt)
192 s5pv310_tick_set_mode(TICK1, mode);
195 static struct clock_event_device mct_tick0_device = {
197 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
199 .set_next_event = s5pv310_tick0_set_next_event,
200 .set_mode = s5pv310_tick0_set_mode,
203 static struct clock_event_device mct_tick1_device = {
205 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
207 .set_next_event = s5pv310_tick1_set_next_event,
208 .set_mode = s5pv310_tick1_set_mode,
211 irqreturn_t s5pv310_mct0_event_isr(int irq, void *dev_id)
213 struct clock_event_device *evt;
214 /* YUBAEK 20110905 RAMDUMP_DEBUG : timer test ++ */
215 #ifdef CONFIG_SEC_DEBUG_KERNEL_HISTORY
216 slp_kernel_debug_time();
218 /* YUBAEK 20110905 RAMDUMP_DEBUG : timer test -- */
220 /* Clear the mct tick interrupt */
221 evt = &mct_tick0_device;
222 if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
223 s5pv310_mct_tick_stop(TICK0);
225 evt->event_handler(evt);
227 s5pv310_mct_write(0x1, S5PV310_MCT_L0_INT_CSTAT);
228 /* Limitation for MCT Timer */
229 s5pv310_mct_write(0x1, S5PV310_MCT_L0_INT_CSTAT);
234 irqreturn_t s5pv310_mct1_event_isr(int irq, void *dev_id)
236 struct clock_event_device *evt;
238 /* Clear the mct tick interrupt */
239 evt = &mct_tick1_device;
240 if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
241 s5pv310_mct_tick_stop(TICK1);
243 evt->event_handler(evt);
245 s5pv310_mct_write(0x1, S5PV310_MCT_L1_INT_CSTAT);
246 /* Limitation for MCT Timer */
247 s5pv310_mct_write(0x1, S5PV310_MCT_L1_INT_CSTAT);
252 static struct irqaction mct_tick0_event_irq = {
253 .name = "mct_tick0_irq",
254 .flags = IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING,
255 .handler = s5pv310_mct0_event_isr,
258 static struct irqaction mct_tick1_event_irq = {
259 .name = "mct_tick1_irq",
260 .flags = IRQF_TIMER | IRQF_NOBALANCING,
261 .handler = s5pv310_mct1_event_isr,
264 static void s5pv310_mct_clockevent_init(struct clock_event_device *dev)
266 clockevents_calc_mult_shift(dev, clk_rate / 2, 5);
268 clockevent_delta2ns(0x7fffffff, dev);
270 clockevent_delta2ns(0xf, dev);
273 static void __init s5pv310_clockevent0_init(void)
275 clk_cnt_per_tick = clk_rate / 2 / HZ;
277 s5pv310_mct_write(0x1, S5PV310_MCT_L0_TCNTB);
278 s5pv310_mct_clockevent_init(&mct_tick0_device);
279 mct_tick0_device.cpumask = cpumask_of(0);
280 clockevents_register_device(&mct_tick0_device);
282 setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
283 setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
286 static void s5pv310_clockevent1_init(void)
288 s5pv310_mct_write(0x1, S5PV310_MCT_L1_TCNTB);
289 s5pv310_mct_clockevent_init(&mct_tick1_device);
290 mct_tick1_device.cpumask = cpumask_of(1);
291 clockevents_register_device(&mct_tick1_device);
294 #ifdef CONFIG_USE_EXT_GIC
295 irq_set_affinity(IRQ_MCT_L1, cpumask_of(1));
297 irq_set_affinity(IRQ_MCT1, cpumask_of(1));
301 #ifdef CONFIG_LOCAL_TIMERS
303 * Setup the local clock events for a CPU.
305 void __cpuinit local_timer_setup(struct clock_event_device *evt)
307 unsigned int cpu = smp_processor_id();
311 s5pv310_clockevent1_init();
314 int local_timer_ack(void)
319 #ifdef CONFIG_HOTPLUG_CPU
320 void local_timer_stop(void)
322 unsigned int cpu = smp_processor_id();
325 s5pv310_mct_tick_stop(TICK1);
331 static void s5pv310_mct_frc_start(u32 hi, u32 lo)
335 s5pv310_mct_write(lo, S5PV310_MCT_G_CNT_L);
336 s5pv310_mct_write(hi, S5PV310_MCT_G_CNT_U);
338 reg = __raw_readl(S5PV310_MCT_G_TCON);
340 s5pv310_mct_write(reg, S5PV310_MCT_G_TCON);
344 * Clocksource handling
346 static cycle_t s5pv310_frc_read(struct clocksource *cs)
349 u32 hi2 = __raw_readl(S5PV310_MCT_G_CNT_U);
353 lo = __raw_readl(S5PV310_MCT_G_CNT_L);
354 hi2 = __raw_readl(S5PV310_MCT_G_CNT_U);
357 return ((cycle_t)hi << 32) | lo;
360 static void s5pv310_frc_suspend(struct clocksource *cs)
362 time_suspended = s5pv310_frc_read(cs);
365 static void s5pv310_frc_resume(struct clocksource *cs)
367 s5pv310_mct_frc_start((u32)(time_suspended >> 32), (u32)time_suspended);
369 s5pv310_mct_write(0x1, S5PV310_MCT_L0_TCNTB);
372 struct clocksource mct_frc = {
375 .read = s5pv310_frc_read,
376 .mask = CLOCKSOURCE_MASK(64),
377 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
378 .suspend = s5pv310_frc_suspend,
379 .resume = s5pv310_frc_resume,
382 static void __init s5pv310_clocksource_init(void)
384 s5pv310_mct_frc_start(0, 0);
386 if (clocksource_register_hz(&mct_frc, clk_rate))
387 panic("%s: can't register clocksource\n", mct_frc.name);
390 sched_mult = clocksource_khz2mult(clk_rate / 1000, sched_shift);
393 unsigned long long sched_clock(void)
395 struct clocksource *cs = &mct_frc;
397 return clocksource_cyc2ns(cs->read(NULL), sched_mult, sched_shift);
400 static void __init s5pv310_timer_resources(void)
403 mct_clk = clk_get(NULL, "xtal");
405 clk_rate = clk_get_rate(mct_clk);
408 static void __init s5pv310_timer_init(void)
410 s5pv310_timer_resources();
411 s5pv310_clockevent0_init();
412 s5pv310_clocksource_init();
415 struct sys_timer s5pv310_timer = {
416 .init = s5pv310_timer_init,