upload tizen1.0 source
[kernel/linux-2.6.36.git] / arch / arm / mach-s5pv310 / mct.c
1 /* linux/arch/arm/mach-s5pv310/mct.c
2  *
3  * Copyright (c) 2010 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com
5  *
6  * S5PV310 (and compatible) HRT 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
22 #include <mach/map.h>
23 #include <mach/regs-mct.h>
24 #include <asm/mach/time.h>
25
26 #ifdef CONFIG_SEC_DEBUG_KERNEL_HISTORY
27 #include <plat/debug-kernel-history.h> /* YUBAEK 20110905 RAMDUMP_DEBUG : added debug-kernel-history.h  */
28 #endif
29
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;
35
36 enum mct_tick_type {
37         TICK0,
38         TICK1,
39 };
40
41 static void s5pv310_mct_write(unsigned int value, void *addr)
42 {
43         void __iomem *stat_addr;
44         u32 mask;
45         u32 i;
46
47         __raw_writel(value, addr);
48
49         switch ((u32) addr) {
50         case (u32) S5PV310_MCT_G_TCON:
51                 stat_addr = S5PV310_MCT_G_WSTAT;
52                 mask = G_WSTAT_TCON_STAT;
53                 break;
54         case (u32) S5PV310_MCT_G_CNT_L:
55                 stat_addr = S5PV310_MCT_G_CNT_WSTAT;
56                 mask = G_CNT_L_STAT;
57                 break;
58         case (u32) S5PV310_MCT_G_CNT_U:
59                 stat_addr = S5PV310_MCT_G_CNT_WSTAT;
60                 mask = G_CNT_U_STAT;
61                 break;
62         case (u32) S5PV310_MCT_L0_TCON:
63                 stat_addr = S5PV310_MCT_L0_WSTAT;
64                 mask = L_TCON_STAT;
65                 break;
66         case (u32) S5PV310_MCT_L1_TCON:
67                 stat_addr = S5PV310_MCT_L1_WSTAT;
68                 mask = L_TCON_STAT;
69                 break;
70         case (u32) S5PV310_MCT_L0_TCNTB:
71                 stat_addr = S5PV310_MCT_L0_WSTAT;
72                 mask = L_TCNTB_STAT;
73                 break;
74         case (u32) S5PV310_MCT_L1_TCNTB:
75                 stat_addr = S5PV310_MCT_L1_WSTAT;
76                 mask = L_TCNTB_STAT;
77                 break;
78         case (u32) S5PV310_MCT_L0_ICNTB:
79                 stat_addr = S5PV310_MCT_L0_WSTAT;
80                 mask = L_ICNTB_STAT;
81                 break;
82         case (u32) S5PV310_MCT_L1_ICNTB:
83                 stat_addr = S5PV310_MCT_L1_WSTAT;
84                 mask = L_ICNTB_STAT;
85                 break;
86         default:
87                 return;
88         }
89
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);
94                         return;
95                 }
96
97         /* Workaround: Try again if fail */
98         __raw_writel(value, addr);
99
100         for (i = 0; i < loops_per_jiffy / 1000 * HZ; i++)
101                 if (__raw_readl(stat_addr) & mask) {
102                         __raw_writel(mask, stat_addr);
103                         return;
104                 }
105
106         panic("mct hangs after writing %d to 0x%x address\n", value, (u32) addr);
107 }
108
109 static void s5pv310_mct_tick_stop(enum mct_tick_type type)
110 {
111         unsigned long reg;
112         void __iomem *addr;
113
114         if (type == TICK0)
115                 addr = S5PV310_MCT_L0_TCON;
116         else
117                 addr = S5PV310_MCT_L1_TCON;
118
119         reg = __raw_readl(addr);
120         reg &= ~(L_TCON_INT_START | L_TCON_TIMER_START);
121         s5pv310_mct_write(reg, addr);
122 }
123
124 static void s5pv310_mct_tick_start(enum mct_tick_type type,
125                                         unsigned long cycles)
126 {
127         unsigned long reg;
128         void __iomem *addr;
129
130         s5pv310_mct_tick_stop(type);
131
132         if (type == TICK0) {
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;
136         } else {
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;
140         }
141
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);
145 }
146
147 static inline int s5pv310_tick_set_next_event(enum mct_tick_type type,
148                                         unsigned long cycles)
149 {
150         s5pv310_mct_tick_start(type, cycles);
151         return 0;
152 }
153
154 static inline void s5pv310_tick_set_mode(enum mct_tick_type type,
155                                 enum clock_event_mode mode)
156 {
157         s5pv310_mct_tick_stop(type);
158
159         switch (mode) {
160         case CLOCK_EVT_MODE_PERIODIC:
161                 s5pv310_mct_tick_start(type, clk_cnt_per_tick);
162                 break;
163         case CLOCK_EVT_MODE_ONESHOT:
164         case CLOCK_EVT_MODE_UNUSED:
165         case CLOCK_EVT_MODE_SHUTDOWN:
166         case CLOCK_EVT_MODE_RESUME:
167                 break;
168         }
169 }
170
171 static int s5pv310_tick0_set_next_event(unsigned long cycles,
172                                         struct clock_event_device *evt)
173 {
174         return s5pv310_tick_set_next_event(TICK0, cycles);
175 }
176
177 static int s5pv310_tick1_set_next_event(unsigned long cycles,
178                                         struct clock_event_device *evt)
179 {
180         return s5pv310_tick_set_next_event(TICK1, cycles);
181 }
182
183 static void s5pv310_tick0_set_mode(enum clock_event_mode mode,
184                                 struct clock_event_device *evt)
185 {
186         s5pv310_tick_set_mode(TICK0, mode);
187 }
188
189 static void s5pv310_tick1_set_mode(enum clock_event_mode mode,
190                                 struct clock_event_device *evt)
191 {
192         s5pv310_tick_set_mode(TICK1, mode);
193 }
194
195 static struct clock_event_device mct_tick0_device = {
196         .name           = "mct-tick0",
197         .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
198         .rating         = 450,
199         .set_next_event = s5pv310_tick0_set_next_event,
200         .set_mode       = s5pv310_tick0_set_mode,
201 };
202
203 static struct clock_event_device mct_tick1_device = {
204         .name           = "mct-tick1",
205         .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
206         .rating         = 450,
207         .set_next_event = s5pv310_tick1_set_next_event,
208         .set_mode       = s5pv310_tick1_set_mode,
209 };
210
211 irqreturn_t s5pv310_mct0_event_isr(int irq, void *dev_id)
212 {
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();
217 #endif
218      /* YUBAEK 20110905 RAMDUMP_DEBUG : timer test  -- */
219
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);
224
225         evt->event_handler(evt);
226
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);
230
231         return IRQ_HANDLED;
232 }
233
234 irqreturn_t s5pv310_mct1_event_isr(int irq, void *dev_id)
235 {
236         struct clock_event_device *evt;
237
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);
242
243         evt->event_handler(evt);
244
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);
248
249         return IRQ_HANDLED;
250 }
251
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,
256 };
257
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,
262 };
263
264 static void s5pv310_mct_clockevent_init(struct clock_event_device *dev)
265 {
266         clockevents_calc_mult_shift(dev, clk_rate / 2, 5);
267         dev->max_delta_ns =
268                 clockevent_delta2ns(0x7fffffff, dev);
269         dev->min_delta_ns =
270                 clockevent_delta2ns(0xf, dev);
271 }
272
273 static void __init s5pv310_clockevent0_init(void)
274 {
275         clk_cnt_per_tick = clk_rate / 2 / HZ;
276
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);
281
282         setup_irq(IRQ_MCT_L0, &mct_tick0_event_irq);
283         setup_irq(IRQ_MCT_L1, &mct_tick1_event_irq);
284 }
285
286 static void s5pv310_clockevent1_init(void)
287 {
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);
292
293
294 #ifdef CONFIG_USE_EXT_GIC
295         irq_set_affinity(IRQ_MCT_L1, cpumask_of(1));
296 #else
297         irq_set_affinity(IRQ_MCT1, cpumask_of(1));
298 #endif
299 }
300
301 #ifdef CONFIG_LOCAL_TIMERS
302 /*
303  * Setup the local clock events for a CPU.
304  */
305 void __cpuinit local_timer_setup(struct clock_event_device *evt)
306 {
307         unsigned int cpu = smp_processor_id();
308
309         /* For cpu 1 */
310         if (cpu == 1)
311                 s5pv310_clockevent1_init();
312 }
313
314 int local_timer_ack(void)
315 {
316         return 0;
317 }
318
319 #ifdef CONFIG_HOTPLUG_CPU
320 void local_timer_stop(void)
321 {
322         unsigned int cpu = smp_processor_id();
323         /* For cpu 1 */
324         if (cpu == 1)
325                 s5pv310_mct_tick_stop(TICK1);
326 }
327 #endif
328
329 #endif
330
331 static void s5pv310_mct_frc_start(u32 hi, u32 lo)
332 {
333         u32 reg;
334
335         s5pv310_mct_write(lo, S5PV310_MCT_G_CNT_L);
336         s5pv310_mct_write(hi, S5PV310_MCT_G_CNT_U);
337
338         reg = __raw_readl(S5PV310_MCT_G_TCON);
339         reg |= G_TCON_START;
340         s5pv310_mct_write(reg, S5PV310_MCT_G_TCON);
341 }
342
343 /*
344  * Clocksource handling
345  */
346 static cycle_t s5pv310_frc_read(struct clocksource *cs)
347 {
348         unsigned int lo, hi;
349         u32 hi2 = __raw_readl(S5PV310_MCT_G_CNT_U);
350
351         do {
352                 hi = hi2;
353                 lo = __raw_readl(S5PV310_MCT_G_CNT_L);
354                 hi2 = __raw_readl(S5PV310_MCT_G_CNT_U);
355         } while (hi != hi2);
356
357         return ((cycle_t)hi << 32) | lo;
358 }
359
360 static void s5pv310_frc_suspend(struct clocksource *cs)
361 {
362         time_suspended = s5pv310_frc_read(cs);
363 };
364
365 static void s5pv310_frc_resume(struct clocksource *cs)
366 {
367         s5pv310_mct_frc_start((u32)(time_suspended >> 32), (u32)time_suspended);
368
369         s5pv310_mct_write(0x1, S5PV310_MCT_L0_TCNTB);
370 };
371
372 struct clocksource mct_frc = {
373         .name           = "mct-frc",
374         .rating         = 400,
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,
380 };
381
382 static void __init s5pv310_clocksource_init(void)
383 {
384         s5pv310_mct_frc_start(0, 0);
385
386         if (clocksource_register_hz(&mct_frc, clk_rate))
387                 panic("%s: can't register clocksource\n", mct_frc.name);
388
389         sched_shift = 16;
390         sched_mult = clocksource_khz2mult(clk_rate / 1000, sched_shift);
391 }
392
393 unsigned long long sched_clock(void)
394 {
395         struct clocksource *cs = &mct_frc;
396
397         return clocksource_cyc2ns(cs->read(NULL), sched_mult, sched_shift);
398 }
399
400 static void __init s5pv310_timer_resources(void)
401 {
402         struct clk *mct_clk;
403         mct_clk = clk_get(NULL, "xtal");
404
405         clk_rate = clk_get_rate(mct_clk);
406 }
407
408 static void __init s5pv310_timer_init(void)
409 {
410         s5pv310_timer_resources();
411         s5pv310_clockevent0_init();
412         s5pv310_clocksource_init();
413 }
414
415 struct sys_timer s5pv310_timer = {
416         .init           = s5pv310_timer_init,
417 };