Linux 3.9-rc1
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / arch / arm / mach-exynos / mct.c
CommitLineData
30d8bead
CY
1/* 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>
2edb36c4 22#include <linux/of.h>
30d8bead 23
2edb36c4 24#include <asm/arch_timer.h>
a8cb6041 25#include <asm/localtimer.h>
3a062281
CY
26
27#include <plat/cpu.h>
28
30d8bead 29#include <mach/map.h>
3a062281 30#include <mach/irqs.h>
30d8bead
CY
31#include <mach/regs-mct.h>
32#include <asm/mach/time.h>
33
4d2e4d7f
CY
34#define TICK_BASE_CNT 1
35
3a062281
CY
36enum {
37 MCT_INT_SPI,
38 MCT_INT_PPI
39};
40
30d8bead 41static unsigned long clk_rate;
3a062281 42static unsigned int mct_int_type;
30d8bead
CY
43
44struct mct_clock_event_device {
45 struct clock_event_device *evt;
46 void __iomem *base;
c8987470 47 char name[10];
30d8bead
CY
48};
49
30d8bead
CY
50static void exynos4_mct_write(unsigned int value, void *addr)
51{
52 void __iomem *stat_addr;
53 u32 mask;
54 u32 i;
55
56 __raw_writel(value, addr);
57
c8987470
CY
58 if (likely(addr >= EXYNOS4_MCT_L_BASE(0))) {
59 u32 base = (u32) addr & EXYNOS4_MCT_L_MASK;
60 switch ((u32) addr & ~EXYNOS4_MCT_L_MASK) {
61 case (u32) MCT_L_TCON_OFFSET:
62 stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET;
63 mask = 1 << 3; /* L_TCON write status */
64 break;
65 case (u32) MCT_L_ICNTB_OFFSET:
66 stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET;
67 mask = 1 << 1; /* L_ICNTB write status */
68 break;
69 case (u32) MCT_L_TCNTB_OFFSET:
70 stat_addr = (void __iomem *) base + MCT_L_WSTAT_OFFSET;
71 mask = 1 << 0; /* L_TCNTB write status */
72 break;
73 default:
74 return;
75 }
76 } else {
77 switch ((u32) addr) {
78 case (u32) EXYNOS4_MCT_G_TCON:
79 stat_addr = EXYNOS4_MCT_G_WSTAT;
80 mask = 1 << 16; /* G_TCON write status */
81 break;
82 case (u32) EXYNOS4_MCT_G_COMP0_L:
83 stat_addr = EXYNOS4_MCT_G_WSTAT;
84 mask = 1 << 0; /* G_COMP0_L write status */
85 break;
86 case (u32) EXYNOS4_MCT_G_COMP0_U:
87 stat_addr = EXYNOS4_MCT_G_WSTAT;
88 mask = 1 << 1; /* G_COMP0_U write status */
89 break;
90 case (u32) EXYNOS4_MCT_G_COMP0_ADD_INCR:
91 stat_addr = EXYNOS4_MCT_G_WSTAT;
92 mask = 1 << 2; /* G_COMP0_ADD_INCR w status */
93 break;
94 case (u32) EXYNOS4_MCT_G_CNT_L:
95 stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
96 mask = 1 << 0; /* G_CNT_L write status */
97 break;
98 case (u32) EXYNOS4_MCT_G_CNT_U:
99 stat_addr = EXYNOS4_MCT_G_CNT_WSTAT;
100 mask = 1 << 1; /* G_CNT_U write status */
101 break;
102 default:
103 return;
104 }
30d8bead
CY
105 }
106
107 /* Wait maximum 1 ms until written values are applied */
108 for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
109 if (__raw_readl(stat_addr) & mask) {
110 __raw_writel(mask, stat_addr);
111 return;
112 }
113
114 panic("MCT hangs after writing %d (addr:0x%08x)\n", value, (u32)addr);
115}
116
117/* Clocksource handling */
118static void exynos4_mct_frc_start(u32 hi, u32 lo)
119{
120 u32 reg;
121
122 exynos4_mct_write(lo, EXYNOS4_MCT_G_CNT_L);
123 exynos4_mct_write(hi, EXYNOS4_MCT_G_CNT_U);
124
125 reg = __raw_readl(EXYNOS4_MCT_G_TCON);
126 reg |= MCT_G_TCON_START;
127 exynos4_mct_write(reg, EXYNOS4_MCT_G_TCON);
128}
129
130static cycle_t exynos4_frc_read(struct clocksource *cs)
131{
132 unsigned int lo, hi;
133 u32 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
134
135 do {
136 hi = hi2;
137 lo = __raw_readl(EXYNOS4_MCT_G_CNT_L);
138 hi2 = __raw_readl(EXYNOS4_MCT_G_CNT_U);
139 } while (hi != hi2);
140
141 return ((cycle_t)hi << 32) | lo;
142}
143
aa421c13
CY
144static void exynos4_frc_resume(struct clocksource *cs)
145{
146 exynos4_mct_frc_start(0, 0);
147}
148
30d8bead
CY
149struct clocksource mct_frc = {
150 .name = "mct-frc",
151 .rating = 400,
152 .read = exynos4_frc_read,
153 .mask = CLOCKSOURCE_MASK(64),
154 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
aa421c13 155 .resume = exynos4_frc_resume,
30d8bead
CY
156};
157
158static void __init exynos4_clocksource_init(void)
159{
160 exynos4_mct_frc_start(0, 0);
161
162 if (clocksource_register_hz(&mct_frc, clk_rate))
163 panic("%s: can't register clocksource\n", mct_frc.name);
164}
165
166static void exynos4_mct_comp0_stop(void)
167{
168 unsigned int tcon;
169
170 tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
171 tcon &= ~(MCT_G_TCON_COMP0_ENABLE | MCT_G_TCON_COMP0_AUTO_INC);
172
173 exynos4_mct_write(tcon, EXYNOS4_MCT_G_TCON);
174 exynos4_mct_write(0, EXYNOS4_MCT_G_INT_ENB);
175}
176
177static void exynos4_mct_comp0_start(enum clock_event_mode mode,
178 unsigned long cycles)
179{
180 unsigned int tcon;
181 cycle_t comp_cycle;
182
183 tcon = __raw_readl(EXYNOS4_MCT_G_TCON);
184
185 if (mode == CLOCK_EVT_MODE_PERIODIC) {
186 tcon |= MCT_G_TCON_COMP0_AUTO_INC;
187 exynos4_mct_write(cycles, EXYNOS4_MCT_G_COMP0_ADD_INCR);
188 }
189
190 comp_cycle = exynos4_frc_read(&mct_frc) + cycles;
191 exynos4_mct_write((u32)comp_cycle, EXYNOS4_MCT_G_COMP0_L);
192 exynos4_mct_write((u32)(comp_cycle >> 32), EXYNOS4_MCT_G_COMP0_U);
193
194 exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_ENB);
195
196 tcon |= MCT_G_TCON_COMP0_ENABLE;
197 exynos4_mct_write(tcon , EXYNOS4_MCT_G_TCON);
198}
199
200static int exynos4_comp_set_next_event(unsigned long cycles,
201 struct clock_event_device *evt)
202{
203 exynos4_mct_comp0_start(evt->mode, cycles);
204
205 return 0;
206}
207
208static void exynos4_comp_set_mode(enum clock_event_mode mode,
209 struct clock_event_device *evt)
210{
4d2e4d7f 211 unsigned long cycles_per_jiffy;
30d8bead
CY
212 exynos4_mct_comp0_stop();
213
214 switch (mode) {
215 case CLOCK_EVT_MODE_PERIODIC:
4d2e4d7f
CY
216 cycles_per_jiffy =
217 (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
218 exynos4_mct_comp0_start(mode, cycles_per_jiffy);
30d8bead
CY
219 break;
220
221 case CLOCK_EVT_MODE_ONESHOT:
222 case CLOCK_EVT_MODE_UNUSED:
223 case CLOCK_EVT_MODE_SHUTDOWN:
224 case CLOCK_EVT_MODE_RESUME:
225 break;
226 }
227}
228
229static struct clock_event_device mct_comp_device = {
230 .name = "mct-comp",
231 .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
232 .rating = 250,
233 .set_next_event = exynos4_comp_set_next_event,
234 .set_mode = exynos4_comp_set_mode,
235};
236
237static irqreturn_t exynos4_mct_comp_isr(int irq, void *dev_id)
238{
239 struct clock_event_device *evt = dev_id;
240
241 exynos4_mct_write(0x1, EXYNOS4_MCT_G_INT_CSTAT);
242
243 evt->event_handler(evt);
244
245 return IRQ_HANDLED;
246}
247
248static struct irqaction mct_comp_event_irq = {
249 .name = "mct_comp_irq",
250 .flags = IRQF_TIMER | IRQF_IRQPOLL,
251 .handler = exynos4_mct_comp_isr,
252 .dev_id = &mct_comp_device,
253};
254
255static void exynos4_clockevent_init(void)
256{
30d8bead 257 mct_comp_device.cpumask = cpumask_of(0);
838a2ae8
SG
258 clockevents_config_and_register(&mct_comp_device, clk_rate,
259 0xf, 0xffffffff);
30d8bead 260
bb19a751
KK
261 if (soc_is_exynos5250())
262 setup_irq(EXYNOS5_IRQ_MCT_G0, &mct_comp_event_irq);
263 else
264 setup_irq(EXYNOS4_IRQ_MCT_G0, &mct_comp_event_irq);
30d8bead
CY
265}
266
267#ifdef CONFIG_LOCAL_TIMERS
991a6c7d
KK
268
269static DEFINE_PER_CPU(struct mct_clock_event_device, percpu_mct_tick);
270
30d8bead
CY
271/* Clock event handling */
272static void exynos4_mct_tick_stop(struct mct_clock_event_device *mevt)
273{
274 unsigned long tmp;
275 unsigned long mask = MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START;
276 void __iomem *addr = mevt->base + MCT_L_TCON_OFFSET;
277
278 tmp = __raw_readl(addr);
279 if (tmp & mask) {
280 tmp &= ~mask;
281 exynos4_mct_write(tmp, addr);
282 }
283}
284
285static void exynos4_mct_tick_start(unsigned long cycles,
286 struct mct_clock_event_device *mevt)
287{
288 unsigned long tmp;
289
290 exynos4_mct_tick_stop(mevt);
291
292 tmp = (1 << 31) | cycles; /* MCT_L_UPDATE_ICNTB */
293
294 /* update interrupt count buffer */
295 exynos4_mct_write(tmp, mevt->base + MCT_L_ICNTB_OFFSET);
296
25985edc 297 /* enable MCT tick interrupt */
30d8bead
CY
298 exynos4_mct_write(0x1, mevt->base + MCT_L_INT_ENB_OFFSET);
299
300 tmp = __raw_readl(mevt->base + MCT_L_TCON_OFFSET);
301 tmp |= MCT_L_TCON_INT_START | MCT_L_TCON_TIMER_START |
302 MCT_L_TCON_INTERVAL_MODE;
303 exynos4_mct_write(tmp, mevt->base + MCT_L_TCON_OFFSET);
304}
305
306static int exynos4_tick_set_next_event(unsigned long cycles,
307 struct clock_event_device *evt)
308{
e700e41d 309 struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
30d8bead
CY
310
311 exynos4_mct_tick_start(cycles, mevt);
312
313 return 0;
314}
315
316static inline void exynos4_tick_set_mode(enum clock_event_mode mode,
317 struct clock_event_device *evt)
318{
e700e41d 319 struct mct_clock_event_device *mevt = this_cpu_ptr(&percpu_mct_tick);
4d2e4d7f 320 unsigned long cycles_per_jiffy;
30d8bead
CY
321
322 exynos4_mct_tick_stop(mevt);
323
324 switch (mode) {
325 case CLOCK_EVT_MODE_PERIODIC:
4d2e4d7f
CY
326 cycles_per_jiffy =
327 (((unsigned long long) NSEC_PER_SEC / HZ * evt->mult) >> evt->shift);
328 exynos4_mct_tick_start(cycles_per_jiffy, mevt);
30d8bead
CY
329 break;
330
331 case CLOCK_EVT_MODE_ONESHOT:
332 case CLOCK_EVT_MODE_UNUSED:
333 case CLOCK_EVT_MODE_SHUTDOWN:
334 case CLOCK_EVT_MODE_RESUME:
335 break;
336 }
337}
338
c8987470 339static int exynos4_mct_tick_clear(struct mct_clock_event_device *mevt)
30d8bead 340{
30d8bead
CY
341 struct clock_event_device *evt = mevt->evt;
342
343 /*
344 * This is for supporting oneshot mode.
345 * Mct would generate interrupt periodically
346 * without explicit stopping.
347 */
348 if (evt->mode != CLOCK_EVT_MODE_PERIODIC)
349 exynos4_mct_tick_stop(mevt);
350
351 /* Clear the MCT tick interrupt */
3a062281
CY
352 if (__raw_readl(mevt->base + MCT_L_INT_CSTAT_OFFSET) & 1) {
353 exynos4_mct_write(0x1, mevt->base + MCT_L_INT_CSTAT_OFFSET);
354 return 1;
355 } else {
356 return 0;
357 }
358}
359
360static irqreturn_t exynos4_mct_tick_isr(int irq, void *dev_id)
361{
362 struct mct_clock_event_device *mevt = dev_id;
363 struct clock_event_device *evt = mevt->evt;
364
365 exynos4_mct_tick_clear(mevt);
30d8bead
CY
366
367 evt->event_handler(evt);
368
369 return IRQ_HANDLED;
370}
371
372static struct irqaction mct_tick0_event_irq = {
373 .name = "mct_tick0_irq",
374 .flags = IRQF_TIMER | IRQF_NOBALANCING,
375 .handler = exynos4_mct_tick_isr,
376};
377
378static struct irqaction mct_tick1_event_irq = {
379 .name = "mct_tick1_irq",
380 .flags = IRQF_TIMER | IRQF_NOBALANCING,
381 .handler = exynos4_mct_tick_isr,
382};
383
a8cb6041 384static int __cpuinit exynos4_local_timer_setup(struct clock_event_device *evt)
30d8bead 385{
e700e41d 386 struct mct_clock_event_device *mevt;
30d8bead 387 unsigned int cpu = smp_processor_id();
eeed66e3 388 int mct_lx_irq;
30d8bead 389
e700e41d
MZ
390 mevt = this_cpu_ptr(&percpu_mct_tick);
391 mevt->evt = evt;
30d8bead 392
e700e41d
MZ
393 mevt->base = EXYNOS4_MCT_L_BASE(cpu);
394 sprintf(mevt->name, "mct_tick%d", cpu);
30d8bead 395
e700e41d 396 evt->name = mevt->name;
30d8bead
CY
397 evt->cpumask = cpumask_of(cpu);
398 evt->set_next_event = exynos4_tick_set_next_event;
399 evt->set_mode = exynos4_tick_set_mode;
400 evt->features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
401 evt->rating = 450;
838a2ae8
SG
402 clockevents_config_and_register(evt, clk_rate / (TICK_BASE_CNT + 1),
403 0xf, 0x7fffffff);
30d8bead 404
4d2e4d7f 405 exynos4_mct_write(TICK_BASE_CNT, mevt->base + MCT_L_TCNTB_OFFSET);
30d8bead 406
3a062281
CY
407 if (mct_int_type == MCT_INT_SPI) {
408 if (cpu == 0) {
eeed66e3
CY
409 mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L0 :
410 EXYNOS5_IRQ_MCT_L0;
e700e41d 411 mct_tick0_event_irq.dev_id = mevt;
eeed66e3
CY
412 evt->irq = mct_lx_irq;
413 setup_irq(mct_lx_irq, &mct_tick0_event_irq);
3a062281 414 } else {
eeed66e3
CY
415 mct_lx_irq = soc_is_exynos4210() ? EXYNOS4_IRQ_MCT_L1 :
416 EXYNOS5_IRQ_MCT_L1;
e700e41d 417 mct_tick1_event_irq.dev_id = mevt;
eeed66e3
CY
418 evt->irq = mct_lx_irq;
419 setup_irq(mct_lx_irq, &mct_tick1_event_irq);
420 irq_set_affinity(mct_lx_irq, cpumask_of(1));
3a062281 421 }
30d8bead 422 } else {
bb19a751 423 enable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER, 0);
30d8bead 424 }
4d487d7e
KK
425
426 return 0;
30d8bead
CY
427}
428
a8cb6041 429static void exynos4_local_timer_stop(struct clock_event_device *evt)
30d8bead 430{
e248cd5d 431 unsigned int cpu = smp_processor_id();
28af690a 432 evt->set_mode(CLOCK_EVT_MODE_UNUSED, evt);
e700e41d 433 if (mct_int_type == MCT_INT_SPI)
e248cd5d
ADK
434 if (cpu == 0)
435 remove_irq(evt->irq, &mct_tick0_event_irq);
436 else
437 remove_irq(evt->irq, &mct_tick1_event_irq);
e700e41d 438 else
bb19a751 439 disable_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER);
30d8bead 440}
a8cb6041
MZ
441
442static struct local_timer_ops exynos4_mct_tick_ops __cpuinitdata = {
443 .setup = exynos4_local_timer_setup,
444 .stop = exynos4_local_timer_stop,
445};
30d8bead
CY
446#endif /* CONFIG_LOCAL_TIMERS */
447
448static void __init exynos4_timer_resources(void)
449{
450 struct clk *mct_clk;
451 mct_clk = clk_get(NULL, "xtal");
452
453 clk_rate = clk_get_rate(mct_clk);
e700e41d 454
991a6c7d 455#ifdef CONFIG_LOCAL_TIMERS
e700e41d
MZ
456 if (mct_int_type == MCT_INT_PPI) {
457 int err;
458
bb19a751 459 err = request_percpu_irq(EXYNOS_IRQ_MCT_LOCALTIMER,
e700e41d
MZ
460 exynos4_mct_tick_isr, "MCT",
461 &percpu_mct_tick);
462 WARN(err, "MCT: can't request IRQ %d (%d)\n",
bb19a751 463 EXYNOS_IRQ_MCT_LOCALTIMER, err);
e700e41d 464 }
a8cb6041
MZ
465
466 local_timer_register(&exynos4_mct_tick_ops);
991a6c7d 467#endif /* CONFIG_LOCAL_TIMERS */
30d8bead
CY
468}
469
6bb27d73 470void __init exynos4_timer_init(void)
30d8bead 471{
2edb36c4
KK
472 if (soc_is_exynos5440()) {
473 arch_timer_of_register();
474 return;
475 }
476
eeed66e3 477 if ((soc_is_exynos4210()) || (soc_is_exynos5250()))
3a062281
CY
478 mct_int_type = MCT_INT_SPI;
479 else
480 mct_int_type = MCT_INT_PPI;
481
30d8bead
CY
482 exynos4_timer_resources();
483 exynos4_clocksource_init();
484 exynos4_clockevent_init();
485}