blob: f34c933314f3d67704456c54628ee32651a8beb5 [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>
Thomas Abraham36ba5d52013-03-09 16:01:52 +090023#include <linux/of_irq.h>
24#include <linux/of_address.h>
Changhwan Youn30d8bea2011-03-11 10:39:57 +090025
Kukjin Kim2edb36c2012-11-15 15:48:56 +090026#include <asm/arch_timer.h>
Marc Zyngiera8cb6042012-01-10 19:44:19 +000027#include <asm/localtimer.h>
Changhwan Youn3a062282011-10-04 17:02:58 +090028
29#include <plat/cpu.h>
30
Changhwan Youn30d8bea2011-03-11 10:39:57 +090031#include <mach/map.h>
Changhwan Youn3a062282011-10-04 17:02:58 +090032#include <mach/irqs.h>
Changhwan Youn30d8bea2011-03-11 10:39:57 +090033#include <asm/mach/time.h>
34
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090035#define EXYNOS4_MCTREG(x) (x)
36#define EXYNOS4_MCT_G_CNT_L EXYNOS4_MCTREG(0x100)
37#define EXYNOS4_MCT_G_CNT_U EXYNOS4_MCTREG(0x104)
38#define EXYNOS4_MCT_G_CNT_WSTAT EXYNOS4_MCTREG(0x110)
39#define EXYNOS4_MCT_G_COMP0_L EXYNOS4_MCTREG(0x200)
40#define EXYNOS4_MCT_G_COMP0_U EXYNOS4_MCTREG(0x204)
41#define EXYNOS4_MCT_G_COMP0_ADD_INCR EXYNOS4_MCTREG(0x208)
42#define EXYNOS4_MCT_G_TCON EXYNOS4_MCTREG(0x240)
43#define EXYNOS4_MCT_G_INT_CSTAT EXYNOS4_MCTREG(0x244)
44#define EXYNOS4_MCT_G_INT_ENB EXYNOS4_MCTREG(0x248)
45#define EXYNOS4_MCT_G_WSTAT EXYNOS4_MCTREG(0x24C)
46#define _EXYNOS4_MCT_L_BASE EXYNOS4_MCTREG(0x300)
47#define EXYNOS4_MCT_L_BASE(x) (_EXYNOS4_MCT_L_BASE + (0x100 * x))
48#define EXYNOS4_MCT_L_MASK (0xffffff00)
49
50#define MCT_L_TCNTB_OFFSET (0x00)
51#define MCT_L_ICNTB_OFFSET (0x08)
52#define MCT_L_TCON_OFFSET (0x20)
53#define MCT_L_INT_CSTAT_OFFSET (0x30)
54#define MCT_L_INT_ENB_OFFSET (0x34)
55#define MCT_L_WSTAT_OFFSET (0x40)
56#define MCT_G_TCON_START (1 << 8)
57#define MCT_G_TCON_COMP0_AUTO_INC (1 << 1)
58#define MCT_G_TCON_COMP0_ENABLE (1 << 0)
59#define MCT_L_TCON_INTERVAL_MODE (1 << 2)
60#define MCT_L_TCON_INT_START (1 << 1)
61#define MCT_L_TCON_TIMER_START (1 << 0)
62
Changhwan Youn4d2e4d72012-03-09 15:09:21 -080063#define TICK_BASE_CNT 1
64
Changhwan Youn3a062282011-10-04 17:02:58 +090065enum {
66 MCT_INT_SPI,
67 MCT_INT_PPI
68};
69
Thomas Abrahamc371dc62013-03-09 16:01:50 +090070enum {
71 MCT_G0_IRQ,
72 MCT_G1_IRQ,
73 MCT_G2_IRQ,
74 MCT_G3_IRQ,
75 MCT_L0_IRQ,
76 MCT_L1_IRQ,
77 MCT_L2_IRQ,
78 MCT_L3_IRQ,
79 MCT_NR_IRQS,
80};
81
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090082static void __iomem *reg_base;
Changhwan Youn30d8bea2011-03-11 10:39:57 +090083static unsigned long clk_rate;
Changhwan Youn3a062282011-10-04 17:02:58 +090084static unsigned int mct_int_type;
Thomas Abrahamc371dc62013-03-09 16:01:50 +090085static int mct_irqs[MCT_NR_IRQS];
Changhwan Youn30d8bea2011-03-11 10:39:57 +090086
87struct mct_clock_event_device {
88 struct clock_event_device *evt;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090089 unsigned long base;
Changhwan Younc8987472011-10-04 17:09:26 +090090 char name[10];
Changhwan Youn30d8bea2011-03-11 10:39:57 +090091};
92
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090093static void exynos4_mct_write(unsigned int value, unsigned long offset)
Changhwan Youn30d8bea2011-03-11 10:39:57 +090094{
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090095 unsigned long stat_addr;
Changhwan Youn30d8bea2011-03-11 10:39:57 +090096 u32 mask;
97 u32 i;
98
Thomas Abrahama1ba7a72013-03-09 16:01:47 +090099 __raw_writel(value, reg_base + offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900100
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900101 if (likely(offset >= EXYNOS4_MCT_L_BASE(0))) {
102 stat_addr = (offset & ~EXYNOS4_MCT_L_MASK) + MCT_L_WSTAT_OFFSET;
103 switch (offset & EXYNOS4_MCT_L_MASK) {
104 case MCT_L_TCON_OFFSET:
Changhwan Younc8987472011-10-04 17:09:26 +0900105 mask = 1 << 3; /* L_TCON write status */
106 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900107 case MCT_L_ICNTB_OFFSET:
Changhwan Younc8987472011-10-04 17:09:26 +0900108 mask = 1 << 1; /* L_ICNTB write status */
109 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900110 case MCT_L_TCNTB_OFFSET:
Changhwan Younc8987472011-10-04 17:09:26 +0900111 mask = 1 << 0; /* L_TCNTB write status */
112 break;
113 default:
114 return;
115 }
116 } else {
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900117 switch (offset) {
118 case EXYNOS4_MCT_G_TCON:
Changhwan Younc8987472011-10-04 17:09:26 +0900119 stat_addr = EXYNOS4_MCT_G_WSTAT;
120 mask = 1 << 16; /* G_TCON write status */
121 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900122 case EXYNOS4_MCT_G_COMP0_L:
Changhwan Younc8987472011-10-04 17:09:26 +0900123 stat_addr = EXYNOS4_MCT_G_WSTAT;
124 mask = 1 << 0; /* G_COMP0_L write status */
125 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900126 case EXYNOS4_MCT_G_COMP0_U:
Changhwan Younc8987472011-10-04 17:09:26 +0900127 stat_addr = EXYNOS4_MCT_G_WSTAT;
128 mask = 1 << 1; /* G_COMP0_U write status */
129 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900130 case EXYNOS4_MCT_G_COMP0_ADD_INCR:
Changhwan Younc8987472011-10-04 17:09:26 +0900131 stat_addr = EXYNOS4_MCT_G_WSTAT;
132 mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
133 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900134 case EXYNOS4_MCT_G_CNT_L:
Changhwan Younc8987472011-10-04 17:09:26 +0900135 stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
136 mask = 1 << 0; /* G_CNT_L write status */
137 break;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900138 case EXYNOS4_MCT_G_CNT_U:
Changhwan Younc8987472011-10-04 17:09:26 +0900139 stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
140 mask = 1 << 1; /* G_CNT_U write status */
141 break;
142 default:
143 return;
144 }
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900145 }
146
147 /* Wait maximum 1 ms until written values are applied */
148 for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900149 if (__raw_readl(reg_base + stat_addr) & mask) {
150 __raw_writel(mask, reg_base + stat_addr);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900151 return;
152 }
153
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900154 panic("MCT hangs after writing %d (offset:0x%lx)\n", value, offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900155}
156
157/* Clocksource handling */
158static void exynos4_mct_frc_start(u32 hi, u32 lo)
159{
160 u32 reg;
161
162 exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
163 exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
164
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900165 reg = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900166 reg |= MCT_G_TCON_START;
167 exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
168}
169
170static cycle_t exynos4_frc_read(struct clocksource *cs)
171{
172 unsigned int lo, hi;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900173 u32 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900174
175 do {
176 hi = hi2;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900177 lo = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_L);
178 hi2 = __raw_readl(reg_base + EXYNOS4_MCT_G_CNT_U);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900179 } while (hi != hi2);
180
181 return ((cycle_t)hi << 32) | lo;
182}
183
Changhwan Younaa421c12011-09-02 14:10:52 +0900184static void exynos4_frc_resume(struct clocksource *cs)
185{
186 exynos4_mct_frc_start(0, 0);
187}
188
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900189struct clocksource mct_frc = {
190 .name = "mct-frc",
191 .rating = 400,
192 .read = exynos4_frc_read,
193 .mask = CLOCKSOURCE_MASK(64),
194 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
Changhwan Younaa421c12011-09-02 14:10:52 +0900195 .resume = exynos4_frc_resume,
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900196};
197
198static void __init exynos4_clocksource_init(void)
199{
200 exynos4_mct_frc_start(0, 0);
201
202 if (clocksource_register_hz(&mct_frc, clk_rate))
203 panic("%s: can't register clocksource\n", mct_frc.name);
204}
205
206static void exynos4_mct_comp0_stop(void)
207{
208 unsigned int tcon;
209
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900210 tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900211 tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
212
213 exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
214 exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
215}
216
217static void exynos4_mct_comp0_start(enum clock_event_mode mode,
218 unsigned long cycles)
219{
220 unsigned int tcon;
221 cycle_t comp_cycle;
222
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900223 tcon = __raw_readl(reg_base + EXYNOS4_MCT_G_TCON);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900224
225 if (mode == CLOCK_EVT_MODE_PERIODIC) {
226 tcon |= MCT_G_TCON_COMP0_AUTO_INC;
227 exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
228 }
229
230 comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
231 exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
232 exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
233
234 exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
235
236 tcon |= MCT_G_TCON_COMP0_ENABLE;
237 exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
238}
239
240static int exynos4_comp_set_next_event(unsigned long cycles,
241 struct clock_event_device *evt)
242{
243 exynos4_mct_comp0_start(evt->mode, cycles);
244
245 return 0;
246}
247
248static void exynos4_comp_set_mode(enum clock_event_mode mode,
249 struct clock_event_device *evt)
250{
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800251 unsigned long cycles_per_jiffy;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900252 exynos4_mct_comp0_stop();
253
254 switch (mode) {
255 case CLOCK_EVT_MODE_PERIODIC:
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800256 cycles_per_jiffy =
257 (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
258 exynos4_mct_comp0_start(mode, cycles_per_jiffy);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900259 break;
260
261 case CLOCK_EVT_MODE_ONESHOT:
262 case CLOCK_EVT_MODE_UNUSED:
263 case CLOCK_EVT_MODE_SHUTDOWN:
264 case CLOCK_EVT_MODE_RESUME:
265 break;
266 }
267}
268
269static struct clock_event_device mct_comp_device = {
270 .name = "mct-comp",
271 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
272 .rating = 250,
273 .set_next_event = exynos4_comp_set_next_event,
274 .set_mode = exynos4_comp_set_mode,
275};
276
277static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
278{
279 struct clock_event_device *evt = dev_id;
280
281 exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
282
283 evt->event_handler(evt);
284
285 return IRQ_HANDLED;
286}
287
288static struct irqaction mct_comp_event_irq = {
289 .name = "mct_comp_irq",
290 .flags = IRQF_TIMER | IRQF_IRQPOLL,
291 .handler = exynos4_mct_comp_isr,
292 .dev_id = &mct_comp_device,
293};
294
295static void exynos4_clockevent_init(void)
296{
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900297 mct_comp_device.cpumask = cpumask_of(0);
Shawn Guo838a2ae2013-01-12 11:50:05 +0000298 clockevents_config_and_register(&mct_comp_device, clk_rate,
299 0xf, 0xffffffff);
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900300 setup_irq(mct_irqs[MCT_G0_IRQ], &mct_comp_event_irq);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900301}
302
303#ifdef CONFIG_LOCAL_TIMERS
Kukjin Kim991a6c72011-12-08 10:04:49 +0900304
305static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
306
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900307/* Clock event handling */
308static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
309{
310 unsigned long tmp;
311 unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900312 unsigned long offset = mevt->base + MCT_L_TCON_OFFSET;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900313
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900314 tmp = __raw_readl(reg_base + offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900315 if (tmp & mask) {
316 tmp &= ~mask;
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900317 exynos4_mct_write(tmp, offset);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900318 }
319}
320
321static void exynos4_mct_tick_start(unsigned long cycles,
322 struct mct_clock_event_device *mevt)
323{
324 unsigned long tmp;
325
326 exynos4_mct_tick_stop(mevt);
327
328 tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
329
330 /* update interrupt count buffer */
331 exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
332
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300333 /* enable MCT tick interrupt */
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900334 exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
335
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900336 tmp = __raw_readl(reg_base + mevt->base + MCT_L_TCON_OFFSET);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900337 tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
338 MCT_L_TCON_INTERVAL_MODE;
339 exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
340}
341
342static int exynos4_tick_set_next_event(unsigned long cycles,
343 struct clock_event_device *evt)
344{
Marc Zyngiere700e412011-11-03 11:13:12 +0900345 struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900346
347 exynos4_mct_tick_start(cycles, mevt);
348
349 return 0;
350}
351
352static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
353 struct clock_event_device *evt)
354{
Marc Zyngiere700e412011-11-03 11:13:12 +0900355 struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800356 unsigned long cycles_per_jiffy;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900357
358 exynos4_mct_tick_stop(mevt);
359
360 switch (mode) {
361 case CLOCK_EVT_MODE_PERIODIC:
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800362 cycles_per_jiffy =
363 (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
364 exynos4_mct_tick_start(cycles_per_jiffy, mevt);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900365 break;
366
367 case CLOCK_EVT_MODE_ONESHOT:
368 case CLOCK_EVT_MODE_UNUSED:
369 case CLOCK_EVT_MODE_SHUTDOWN:
370 case CLOCK_EVT_MODE_RESUME:
371 break;
372 }
373}
374
Changhwan Younc8987472011-10-04 17:09:26 +0900375static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900376{
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900377 struct clock_event_device *evt = mevt->evt;
378
379 /*
380 * This is for supporting oneshot mode.
381 * Mct would generate interrupt periodically
382 * without explicit stopping.
383 */
384 if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
385 exynos4_mct_tick_stop(mevt);
386
387 /* Clear the MCT tick interrupt */
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900388 if (__raw_readl(reg_base + mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
Changhwan Youn3a062282011-10-04 17:02:58 +0900389 exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
390 return 1;
391 } else {
392 return 0;
393 }
394}
395
396static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
397{
398 struct mct_clock_event_device *mevt = dev_id;
399 struct clock_event_device *evt = mevt->evt;
400
401 exynos4_mct_tick_clear(mevt);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900402
403 evt->event_handler(evt);
404
405 return IRQ_HANDLED;
406}
407
408static struct irqaction mct_tick0_event_irq = {
409 .name = "mct_tick0_irq",
410 .flags = IRQF_TIMER | IRQF_NOBALANCING,
411 .handler = exynos4_mct_tick_isr,
412};
413
414static struct irqaction mct_tick1_event_irq = {
415 .name = "mct_tick1_irq",
416 .flags = IRQF_TIMER | IRQF_NOBALANCING,
417 .handler = exynos4_mct_tick_isr,
418};
419
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000420static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900421{
Marc Zyngiere700e412011-11-03 11:13:12 +0900422 struct mct_clock_event_device *mevt;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900423 unsigned int cpu = smp_processor_id();
424
Marc Zyngiere700e412011-11-03 11:13:12 +0900425 mevt = this_cpu_ptr(&percpu_mct_tick);
426 mevt->evt = evt;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900427
Marc Zyngiere700e412011-11-03 11:13:12 +0900428 mevt->base = EXYNOS4_MCT_L_BASE(cpu);
429 sprintf(mevt->name, "mct_tick%d", cpu);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900430
Marc Zyngiere700e412011-11-03 11:13:12 +0900431 evt->name = mevt->name;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900432 evt->cpumask = cpumask_of(cpu);
433 evt->set_next_event = exynos4_tick_set_next_event;
434 evt->set_mode = exynos4_tick_set_mode;
435 evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
436 evt->rating = 450;
Shawn Guo838a2ae2013-01-12 11:50:05 +0000437 clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
438 0xf, 0x7fffffff);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900439
Changhwan Youn4d2e4d72012-03-09 15:09:21 -0800440 exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900441
Changhwan Youn3a062282011-10-04 17:02:58 +0900442 if (mct_int_type == MCT_INT_SPI) {
443 if (cpu == 0) {
Marc Zyngiere700e412011-11-03 11:13:12 +0900444 mct_tick0_event_irq.dev_id = mevt;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900445 evt->irq = mct_irqs[MCT_L0_IRQ];
446 setup_irq(evt->irq, &mct_tick0_event_irq);
Changhwan Youn3a062282011-10-04 17:02:58 +0900447 } else {
Marc Zyngiere700e412011-11-03 11:13:12 +0900448 mct_tick1_event_irq.dev_id = mevt;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900449 evt->irq = mct_irqs[MCT_L1_IRQ];
450 setup_irq(evt->irq, &mct_tick1_event_irq);
451 irq_set_affinity(evt->irq, cpumask_of(1));
Changhwan Youn3a062282011-10-04 17:02:58 +0900452 }
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900453 } else {
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900454 enable_percpu_irq(mct_irqs[MCT_L0_IRQ], 0);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900455 }
Kukjin Kim4d487d72011-08-24 16:07:39 +0900456
457 return 0;
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900458}
459
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000460static void exynos4_local_timer_stop(struct clock_event_device *evt)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900461{
Amit Daniel Kachhape248cd52011-12-08 10:07:08 +0900462 unsigned int cpu = smp_processor_id();
Marc Zyngier28af6902011-07-22 12:52:37 +0100463 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
Marc Zyngiere700e412011-11-03 11:13:12 +0900464 if (mct_int_type == MCT_INT_SPI)
Amit Daniel Kachhape248cd52011-12-08 10:07:08 +0900465 if (cpu == 0)
466 remove_irq(evt->irq, &mct_tick0_event_irq);
467 else
468 remove_irq(evt->irq, &mct_tick1_event_irq);
Marc Zyngiere700e412011-11-03 11:13:12 +0900469 else
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900470 disable_percpu_irq(mct_irqs[MCT_L0_IRQ]);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900471}
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000472
473static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
474 .setup = exynos4_local_timer_setup,
475 .stop = exynos4_local_timer_stop,
476};
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900477#endif /* CONFIG_LOCAL_TIMERS */
478
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900479static void __init exynos4_timer_resources(struct device_node *np)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900480{
481 struct clk *mct_clk;
482 mct_clk = clk_get(NULL, "xtal");
483
484 clk_rate = clk_get_rate(mct_clk);
Marc Zyngiere700e412011-11-03 11:13:12 +0900485
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900486 reg_base = np ? of_iomap(np, 0) : S5P_VA_SYSTIMER;
487 if (!reg_base)
488 panic("%s: unable to ioremap mct address space\n", __func__);
Thomas Abrahama1ba7a72013-03-09 16:01:47 +0900489
Kukjin Kim991a6c72011-12-08 10:04:49 +0900490#ifdef CONFIG_LOCAL_TIMERS
Marc Zyngiere700e412011-11-03 11:13:12 +0900491 if (mct_int_type == MCT_INT_PPI) {
492 int err;
493
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900494 err = request_percpu_irq(mct_irqs[MCT_L0_IRQ],
Marc Zyngiere700e412011-11-03 11:13:12 +0900495 exynos4_mct_tick_isr, "MCT",
496 &percpu_mct_tick);
497 WARN(err, "MCT: can't request IRQ %d (%d)\n",
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900498 mct_irqs[MCT_L0_IRQ], err);
Marc Zyngiere700e412011-11-03 11:13:12 +0900499 }
Marc Zyngiera8cb6042012-01-10 19:44:19 +0000500
501 local_timer_register(&exynos4_mct_tick_ops);
Kukjin Kim991a6c72011-12-08 10:04:49 +0900502#endif /* CONFIG_LOCAL_TIMERS */
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900503}
504
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900505static const struct of_device_id exynos_mct_ids[] = {
506 { .compatible = "samsung,exynos4210-mct", .data = (void *)MCT_INT_SPI },
507 { .compatible = "samsung,exynos4412-mct", .data = (void *)MCT_INT_PPI },
508};
509
Stephen Warren6bb27d72012-11-08 12:40:59 -0700510void __init exynos4_timer_init(void)
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900511{
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900512 struct device_node *np = NULL;
513 const struct of_device_id *match;
514 u32 nr_irqs, i;
515
Kukjin Kim2edb36c2012-11-15 15:48:56 +0900516 if (soc_is_exynos5440()) {
517 arch_timer_of_register();
518 return;
519 }
520
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900521#ifdef CONFIG_OF
522 np = of_find_matching_node_and_match(NULL, exynos_mct_ids, &match);
523#endif
524 if (np) {
525 mct_int_type = (u32)(match->data);
526
527 /* This driver uses only one global timer interrupt */
528 mct_irqs[MCT_G0_IRQ] = irq_of_parse_and_map(np, MCT_G0_IRQ);
529
530 /*
531 * Find out the number of local irqs specified. The local
532 * timer irqs are specified after the four global timer
533 * irqs are specified.
534 */
535#ifdef CONFIG_OF
536 nr_irqs = of_irq_count(np);
537#endif
538 for (i = MCT_L0_IRQ; i < nr_irqs; i++)
539 mct_irqs[i] = irq_of_parse_and_map(np, i);
540 } else if (soc_is_exynos4210()) {
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900541 mct_irqs[MCT_G0_IRQ] = EXYNOS4_IRQ_MCT_G0;
542 mct_irqs[MCT_L0_IRQ] = EXYNOS4_IRQ_MCT_L0;
543 mct_irqs[MCT_L1_IRQ] = EXYNOS4_IRQ_MCT_L1;
Changhwan Youn3a062282011-10-04 17:02:58 +0900544 mct_int_type = MCT_INT_SPI;
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900545 } else {
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900546 panic("unable to determine mct controller type\n");
Thomas Abrahamc371dc62013-03-09 16:01:50 +0900547 }
Changhwan Youn3a062282011-10-04 17:02:58 +0900548
Thomas Abraham36ba5d52013-03-09 16:01:52 +0900549 exynos4_timer_resources(np);
Changhwan Youn30d8bea2011-03-11 10:39:57 +0900550 exynos4_clocksource_init();
551 exynos4_clockevent_init();
552}