blob: 1061db4118aa71883db0338267f4f87d4691eb1c [file] [log] [blame]
Changhwan Youn30d8bea2011-03-11 10:39:57 +09001/* linux/arch/arm/mach-exynos4/mct.c
2 *
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
5 *
6 * EXYNOS4 MCT(Multi-Core Timer) support
7 *
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.
11*/
12
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>
21#include <linux/percpu.h>
Kukjin Kim2edb36c2012-11-15 15:48:56 +090022#include <linux/of.h>
Changhwan Youn30d8bea2011-03-11 10:39:57 +090023
Kukjin Kim2edb36c2012-11-15 15:48:56 +090024#include <asm/arch_timer.h>
Marc Zyngiera8cb6042012-01-10 19:44:19 +000025#include <asm/localtimer.h>
Changhwan Youn3a062282011-10-04 17:02:58 +090026
27#include <plat/cpu.h>
28
Changhwan Youn30d8bea2011-03-11 10:39:57 +090029#include <mach/map.h>
Changhwan Youn3a062282011-10-04 17:02:58 +090030#include <mach/irqs.h>
Changhwan Youn30d8bea2011-03-11 10:39:57 +090031#include <asm/mach/time.h>
32
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090033#define EXYNOS4_MCTREG(x) (x)
34#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
35#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
36#define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110)
37#define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200)
38#define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204)
39#define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208)
40#define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240)
41#define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244)
42#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248)
43#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C)
44#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300)
45#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x))
46#define EXYNOS4_MCT_L_MASK (0xffffff00)
47
48#define MCT_L_TCNTB_OFFSET (0x00)
49#define MCT_L_ICNTB_OFFSET (0x08)
50#define MCT_L_TCON_OFFSET (0x20)
51#define MCT_L_INT_CSTAT_OFFSET (0x30)
52#define MCT_L_INT_ENB_OFFSET (0x34)
53#define MCT_L_WSTAT_OFFSET (0x40)
54#define MCT_G_TCON_START (1 << 8)
55#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1)
56#define MCT_G_TCON_COMP0_ENABLE (1 << 0)
57#define MCT_L_TCON_INTERVAL_MODE (1 << 2)
58#define MCT_L_TCON_INT_START (1 << 1)
59#define MCT_L_TCON_TIMER_START (1 << 0)
60
Changhwan Youn4d2e4d72012-03-09 15:09:21 -080061#define TICK_BASE_CNT 1
62
Changhwan Youn3a062282011-10-04 17:02:58 +090063enum {
64 MCT_INT_SPI,
65 MCT_INT_PPI
66};
67
Thomas Abrahamc371dc62013-03-09 16:01:50 +090068enum {
69 MCT_G0_IRQ,
70 MCT_G1_IRQ,
71 MCT_G2_IRQ,
72 MCT_G3_IRQ,
73 MCT_L0_IRQ,
74 MCT_L1_IRQ,
75 MCT_L2_IRQ,
76 MCT_L3_IRQ,
77 MCT_NR_IRQS,
78};
79
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090080static void __iomem *reg_base;
Changhwan Youn30d8bea2011-03-11 10:39:57 +090081static unsigned long clk_rate;
Changhwan Youn3a062282011-10-04 17:02:58 +090082static unsigned int mct_int_type;
Thomas Abrahamc371dc62013-03-09 16:01:50 +090083static int mct_irqs[MCT_NR_IRQS];
Changhwan Youn30d8bea2011-03-11 10:39:57 +090084
85struct mct_clock_event_device {
86 struct clock_event_device *evt;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090087 unsigned long base;
Changhwan Younc8987472011-10-04 17:09:26 +090088 char name[10];
Changhwan Youn30d8bea2011-03-11 10:39:57 +090089};
90
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090091static void exynos4_mct_write(unsigned int value, unsigned long offset)
Changhwan Youn30d8bea2011-03-11 10:39:57 +090092{
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090093 unsigned long stat_addr;
Changhwan Youn30d8bea2011-03-11 10:39:57 +090094 u32 mask;
95 u32 i;
96
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090097 __raw_writel(value, reg_base + offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +090098
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090099 if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
100 stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
101 switch (offset & EXYNOS4_MCT_L_MASK) {
102 case MCT_L_TCON_OFFSET:
Changhwan Younc8987472011-10-04 17:09:26 +0900103 mask = 1 << 3; /* L_TCON write status */
104 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900105 case MCT_L_ICNTB_OFFSET:
Changhwan Younc8987472011-10-04 17:09:26 +0900106 mask = 1 << 1; /* L_ICNTB write status */
107 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900108 case MCT_L_TCNTB_OFFSET:
Changhwan Younc8987472011-10-04 17:09:26 +0900109 mask = 1 << 0; /* L_TCNTB write status */
110 break;
111 default:
112 return;
113 }
114 } else {
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900115 switch (offset) {
116 case EXYNOS4_MCT_G_TCON:
Changhwan Younc8987472011-10-04 17:09:26 +0900117 stat_addr = EXYNOS4_MCT_G_WSTAT;
118 mask = 1 << 16; /* G_TCON write status */
119 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900120 case EXYNOS4_MCT_G_COMP0_L:
Changhwan Younc8987472011-10-04 17:09:26 +0900121 stat_addr = EXYNOS4_MCT_G_WSTAT;
122 mask = 1 << 0; /* G_COMP0_L write status */
123 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900124 case EXYNOS4_MCT_G_COMP0_U:
Changhwan Younc8987472011-10-04 17:09:26 +0900125 stat_addr = EXYNOS4_MCT_G_WSTAT;
126 mask = 1 << 1; /* G_COMP0_U write status */
127 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900128 case EXYNOS4_MCT_G_COMP0_ADD_INCR:
Changhwan Younc8987472011-10-04 17:09:26 +0900129 stat_addr = EXYNOS4_MCT_G_WSTAT;
130 mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
131 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900132 case EXYNOS4_MCT_G_CNT_L:
Changhwan Younc8987472011-10-04 17:09:26 +0900133 stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
134 mask = 1 << 0; /* G_CNT_L write status */
135 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900136 case EXYNOS4_MCT_G_CNT_U:
Changhwan Younc8987472011-10-04 17:09:26 +0900137 stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
138 mask = 1 << 1; /* G_CNT_U write status */
139 break;
140 default:
141 return;
142 }
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900143 }
144
145 /* Wait maximum 1 ms until written values are applied */
146 for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900147 if (__raw_readl(reg_base + stat_addr) & mask) {
148 __raw_writel(mask, reg_base + stat_addr);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900149 return;
150 }
151
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900152 panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900153}
154
155/* Clocksource handling */
156static void exynos4_mct_frc_start(u32 hi, u32 lo)
157{
158 u32 reg;
159
160 exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
161 exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
162
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900163 reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900164 reg |= MCT_G_TCON_START;
165 exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
166}
167
168static cycle_t exynos4_frc_read(struct clocksource *cs)
169{
170 unsigned int lo, hi;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900171 u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900172
173 do {
174 hi = hi2;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900175 lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L);
176 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900177 } while (hi != hi2);
178
179 return ((cycle_t)hi << 32) | lo;
180}
181
Changhwan Younaa421c12011-09-02 14:10:52 +0900182static void exynos4_frc_resume(struct clocksource *cs)
183{
184 exynos4_mct_frc_start(0, 0);
185}
186
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900187struct clocksource mct_frc = {
188 .name = "mct-frc",
189 .rating = 400,
190 .read = exynos4_frc_read,
191 .mask = CLOCKSOURCE_MASK(64),
192 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
Changhwan Younaa421c12011-09-02 14:10:52 +0900193 .resume = exynos4_frc_resume,
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900194};
195
196static void __init exynos4_clocksource_init(void)
197{
198 exynos4_mct_frc_start(0, 0);
199
200 if (clocksource_register_hz(&mct_frc, clk_rate))
201 panic("%s: can't register clocksource\n", mct_frc.name);
202}
203
204static void exynos4_mct_comp0_stop(void)
205{
206 unsigned int tcon;
207
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900208 tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900209 tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
210
211 exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
212 exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
213}
214
215static void exynos4_mct_comp0_start(enum clock_event_mode mode,
216 unsigned long cycles)
217{
218 unsigned int tcon;
219 cycle_t comp_cycle;
220
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900221 tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900222
223 if (mode == CLOCK_EVT_MODE_PERIODIC) {
224 tcon |= MCT_G_TCON_COMP0_AUTO_INC;
225 exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
226 }
227
228 comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
229 exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
230 exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
231
232 exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
233
234 tcon |= MCT_G_TCON_COMP0_ENABLE;
235 exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
236}
237
238static int exynos4_comp_set_next_event(unsigned long cycles,
239 struct clock_event_device *evt)
240{
241 exynos4_mct_comp0_start(evt->mode, cycles);
242
243 return 0;
244}
245
246static void exynos4_comp_set_mode(enum clock_event_mode mode,
247 struct clock_event_device *evt)
248{
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800249 unsigned long cycles_per_jiffy;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900250 exynos4_mct_comp0_stop();
251
252 switch (mode) {
253 case CLOCK_EVT_MODE_PERIODIC:
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800254 cycles_per_jiffy =
255 (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
256 exynos4_mct_comp0_start(mode, cycles_per_jiffy);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900257 break;
258
259 case CLOCK_EVT_MODE_ONESHOT:
260 case CLOCK_EVT_MODE_UNUSED:
261 case CLOCK_EVT_MODE_SHUTDOWN:
262 case CLOCK_EVT_MODE_RESUME:
263 break;
264 }
265}
266
267static struct clock_event_device mct_comp_device = {
268 .name = "mct-comp",
269 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
270 .rating = 250,
271 .set_next_event = exynos4_comp_set_next_event,
272 .set_mode = exynos4_comp_set_mode,
273};
274
275static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
276{
277 struct clock_event_device *evt = dev_id;
278
279 exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
280
281 evt->event_handler(evt);
282
283 return IRQ_HANDLED;
284}
285
286static struct irqaction mct_comp_event_irq = {
287 .name = "mct_comp_irq",
288 .flags = IRQF_TIMER | IRQF_IRQPOLL,
289 .handler = exynos4_mct_comp_isr,
290 .dev_id = &mct_comp_device,
291};
292
293static void exynos4_clockevent_init(void)
294{
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900295 mct_comp_device.cpumask = cpumask_of(0);
Shawn Guo838a2ae2013-01-12 11:50:05 +0000296 clockevents_config_and_register(&mct_comp_device, clk_rate,
297 0xf, 0xffffffff);
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900298 setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900299}
300
301#ifdef CONFIG_LOCAL_TIMERS
Kukjin Kim991a6c72011-12-08 10:04:49 +0900302
303static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
304
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900305/* Clock event handling */
306static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
307{
308 unsigned long tmp;
309 unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900310 unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900311
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900312 tmp = __raw_readl(reg_base + offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900313 if (tmp & mask) {
314 tmp &= ~mask;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900315 exynos4_mct_write(tmp, offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900316 }
317}
318
319static void exynos4_mct_tick_start(unsigned long cycles,
320 struct mct_clock_event_device *mevt)
321{
322 unsigned long tmp;
323
324 exynos4_mct_tick_stop(mevt);
325
326 tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
327
328 /* update interrupt count buffer */
329 exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
330
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300331 /* enable MCT tick interrupt */
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900332 exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
333
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900334 tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900335 tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
336 MCT_L_TCON_INTERVAL_MODE;
337 exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
338}
339
340static int exynos4_tick_set_next_event(unsigned long cycles,
341 struct clock_event_device *evt)
342{
Marc Zyngiere700e412011-11-03 11:13:12 +0900343 struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900344
345 exynos4_mct_tick_start(cycles, mevt);
346
347 return 0;
348}
349
350static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
351 struct clock_event_device *evt)
352{
Marc Zyngiere700e412011-11-03 11:13:12 +0900353 struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800354 unsigned long cycles_per_jiffy;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900355
356 exynos4_mct_tick_stop(mevt);
357
358 switch (mode) {
359 case CLOCK_EVT_MODE_PERIODIC:
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800360 cycles_per_jiffy =
361 (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
362 exynos4_mct_tick_start(cycles_per_jiffy, mevt);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900363 break;
364
365 case CLOCK_EVT_MODE_ONESHOT:
366 case CLOCK_EVT_MODE_UNUSED:
367 case CLOCK_EVT_MODE_SHUTDOWN:
368 case CLOCK_EVT_MODE_RESUME:
369 break;
370 }
371}
372
Changhwan Younc8987472011-10-04 17:09:26 +0900373static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900374{
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900375 struct clock_event_device *evt = mevt->evt;
376
377 /*
378 * This is for supporting oneshot mode.
379 * Mct would generate interrupt periodically
380 * without explicit stopping.
381 */
382 if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
383 exynos4_mct_tick_stop(mevt);
384
385 /* Clear the MCT tick interrupt */
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900386 if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
Changhwan Youn3a062282011-10-04 17:02:58 +0900387 exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
388 return 1;
389 } else {
390 return 0;
391 }
392}
393
394static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
395{
396 struct mct_clock_event_device *mevt = dev_id;
397 struct clock_event_device *evt = mevt->evt;
398
399 exynos4_mct_tick_clear(mevt);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900400
401 evt->event_handler(evt);
402
403 return IRQ_HANDLED;
404}
405
406static struct irqaction mct_tick0_event_irq = {
407 .name = "mct_tick0_irq",
408 .flags = IRQF_TIMER | IRQF_NOBALANCING,
409 .handler = exynos4_mct_tick_isr,
410};
411
412static struct irqaction mct_tick1_event_irq = {
413 .name = "mct_tick1_irq",
414 .flags = IRQF_TIMER | IRQF_NOBALANCING,
415 .handler = exynos4_mct_tick_isr,
416};
417
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000418static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900419{
Marc Zyngiere700e412011-11-03 11:13:12 +0900420 struct mct_clock_event_device *mevt;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900421 unsigned int cpu = smp_processor_id();
422
Marc Zyngiere700e412011-11-03 11:13:12 +0900423 mevt = this_cpu_ptr(&percpu_mct_tick);
424 mevt->evt = evt;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900425
Marc Zyngiere700e412011-11-03 11:13:12 +0900426 mevt->base = EXYNOS4_MCT_L_BASE(cpu);
427 sprintf(mevt->name, "mct_tick%d", cpu);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900428
Marc Zyngiere700e412011-11-03 11:13:12 +0900429 evt->name = mevt->name;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900430 evt->cpumask = cpumask_of(cpu);
431 evt->set_next_event = exynos4_tick_set_next_event;
432 evt->set_mode = exynos4_tick_set_mode;
433 evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
434 evt->rating = 450;
Shawn Guo838a2ae2013-01-12 11:50:05 +0000435 clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
436 0xf, 0x7fffffff);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900437
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800438 exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900439
Changhwan Youn3a062282011-10-04 17:02:58 +0900440 if (mct_int_type == MCT_INT_SPI) {
441 if (cpu == 0) {
Marc Zyngiere700e412011-11-03 11:13:12 +0900442 mct_tick0_event_irq.dev_id = mevt;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900443 evt->irq = mct_irqs[MCT_L0_IRQ];
444 setup_irq(evt->irq, &mct_tick0_event_irq);
Changhwan Youn3a062282011-10-04 17:02:58 +0900445 } else {
Marc Zyngiere700e412011-11-03 11:13:12 +0900446 mct_tick1_event_irq.dev_id = mevt;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900447 evt->irq = mct_irqs[MCT_L1_IRQ];
448 setup_irq(evt->irq, &mct_tick1_event_irq);
449 irq_set_affinity(evt->irq, cpumask_of(1));
Changhwan Youn3a062282011-10-04 17:02:58 +0900450 }
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900451 } else {
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900452 enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900453 }
Kukjin Kim4d487d72011-08-24 16:07:39 +0900454
455 return 0;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900456}
457
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000458static void exynos4_local_timer_stop(struct clock_event_device *evt)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900459{
Amit Daniel Kachhape248cd52011-12-08 10:07:08 +0900460 unsigned int cpu = smp_processor_id();
Marc Zyngier28af6902011-07-22 12:52:37 +0100461 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
Marc Zyngiere700e412011-11-03 11:13:12 +0900462 if (mct_int_type == MCT_INT_SPI)
Amit Daniel Kachhape248cd52011-12-08 10:07:08 +0900463 if (cpu == 0)
464 remove_irq(evt->irq, &mct_tick0_event_irq);
465 else
466 remove_irq(evt->irq, &mct_tick1_event_irq);
Marc Zyngiere700e412011-11-03 11:13:12 +0900467 else
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900468 disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900469}
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000470
471static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
472 .setup = exynos4_local_timer_setup,
473 .stop = exynos4_local_timer_stop,
474};
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900475#endif /* CONFIG_LOCAL_TIMERS */
476
477static void __init exynos4_timer_resources(void)
478{
479 struct clk *mct_clk;
480 mct_clk = clk_get(NULL, "xtal");
481
482 clk_rate = clk_get_rate(mct_clk);
Marc Zyngiere700e412011-11-03 11:13:12 +0900483
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900484 reg_base = S5P_VA_SYSTIMER;
485
Kukjin Kim991a6c72011-12-08 10:04:49 +0900486#ifdef CONFIG_LOCAL_TIMERS
Marc Zyngiere700e412011-11-03 11:13:12 +0900487 if (mct_int_type == MCT_INT_PPI) {
488 int err;
489
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900490 err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
Marc Zyngiere700e412011-11-03 11:13:12 +0900491 exynos4_mct_tick_isr, "MCT",
492 &percpu_mct_tick);
493 WARN(err, "MCT: can't request IRQ %d (%d)\n",
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900494 mct_irqs[MCT_L0_IRQ], err);
Marc Zyngiere700e412011-11-03 11:13:12 +0900495 }
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000496
497 local_timer_register(&exynos4_mct_tick_ops);
Kukjin Kim991a6c72011-12-08 10:04:49 +0900498#endif /* CONFIG_LOCAL_TIMERS */
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900499}
500
Stephen Warren6bb27d72012-11-08 12:40:59 -0700501void __init exynos4_timer_init(void)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900502{
Kukjin Kim2edb36c2012-11-15 15:48:56 +0900503 if (soc_is_exynos5440()) {
504 arch_timer_of_register();
505 return;
506 }
507
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900508 if (soc_is_exynos4210()) {
509 mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0;
510 mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0;
511 mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1;
Changhwan Youn3a062282011-10-04 17:02:58 +0900512 mct_int_type = MCT_INT_SPI;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900513 } else if (soc_is_exynos5250()) {
514 mct_irqs[MCT_G0_IRQ] = EXYNOS5_IRQ_MCT_G0;
515 mct_irqs[MCT_L0_IRQ] = EXYNOS5_IRQ_MCT_L0;
516 mct_irqs[MCT_L1_IRQ] = EXYNOS5_IRQ_MCT_L1;
517 mct_int_type = MCT_INT_SPI;
518 } else {
519 mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0;
520 mct_irqs[MCT_L0_IRQ] = EXYNOS_IRQ_MCT_LOCALTIMER;
Changhwan Youn3a062282011-10-04 17:02:58 +0900521 mct_int_type = MCT_INT_PPI;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900522 }
Changhwan Youn3a062282011-10-04 17:02:58 +0900523
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900524 exynos4_timer_resources();
525 exynos4_clocksource_init();
526 exynos4_clockevent_init();
527}