Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/benh/powerpc
[linux-2.6.git] / arch / arm / plat-s5p / s5p-time.c
1 /* linux/arch/arm/plat-s5p/s5p-time.c
2  *
3  * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com/
5  *
6  * S5P - Common hr-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
21 #include <asm/smp_twd.h>
22 #include <asm/mach/time.h>
23 #include <asm/mach/arch.h>
24 #include <asm/mach/map.h>
25 #include <asm/sched_clock.h>
26
27 #include <mach/map.h>
28 #include <plat/devs.h>
29 #include <plat/regs-timer.h>
30 #include <plat/s5p-time.h>
31
32 static struct clk *tin_event;
33 static struct clk *tin_source;
34 static struct clk *tdiv_event;
35 static struct clk *tdiv_source;
36 static struct clk *timerclk;
37 static struct s5p_timer_source timer_source;
38 static unsigned long clock_count_per_tick;
39 static void s5p_timer_resume(void);
40
41 static void s5p_time_stop(enum s5p_timer_mode mode)
42 {
43         unsigned long tcon;
44
45         tcon = __raw_readl(S3C2410_TCON);
46
47         switch (mode) {
48         case S5P_PWM0:
49                 tcon &= ~S3C2410_TCON_T0START;
50                 break;
51
52         case S5P_PWM1:
53                 tcon &= ~S3C2410_TCON_T1START;
54                 break;
55
56         case S5P_PWM2:
57                 tcon &= ~S3C2410_TCON_T2START;
58                 break;
59
60         case S5P_PWM3:
61                 tcon &= ~S3C2410_TCON_T3START;
62                 break;
63
64         case S5P_PWM4:
65                 tcon &= ~S3C2410_TCON_T4START;
66                 break;
67
68         default:
69                 printk(KERN_ERR "Invalid Timer %d\n", mode);
70                 break;
71         }
72         __raw_writel(tcon, S3C2410_TCON);
73 }
74
75 static void s5p_time_setup(enum s5p_timer_mode mode, unsigned long tcnt)
76 {
77         unsigned long tcon;
78
79         tcon = __raw_readl(S3C2410_TCON);
80
81         tcnt--;
82
83         switch (mode) {
84         case S5P_PWM0:
85                 tcon &= ~(0x0f << 0);
86                 tcon |= S3C2410_TCON_T0MANUALUPD;
87                 break;
88
89         case S5P_PWM1:
90                 tcon &= ~(0x0f << 8);
91                 tcon |= S3C2410_TCON_T1MANUALUPD;
92                 break;
93
94         case S5P_PWM2:
95                 tcon &= ~(0x0f << 12);
96                 tcon |= S3C2410_TCON_T2MANUALUPD;
97                 break;
98
99         case S5P_PWM3:
100                 tcon &= ~(0x0f << 16);
101                 tcon |= S3C2410_TCON_T3MANUALUPD;
102                 break;
103
104         case S5P_PWM4:
105                 tcon &= ~(0x07 << 20);
106                 tcon |= S3C2410_TCON_T4MANUALUPD;
107                 break;
108
109         default:
110                 printk(KERN_ERR "Invalid Timer %d\n", mode);
111                 break;
112         }
113
114         __raw_writel(tcnt, S3C2410_TCNTB(mode));
115         __raw_writel(tcnt, S3C2410_TCMPB(mode));
116         __raw_writel(tcon, S3C2410_TCON);
117 }
118
119 static void s5p_time_start(enum s5p_timer_mode mode, bool periodic)
120 {
121         unsigned long tcon;
122
123         tcon  = __raw_readl(S3C2410_TCON);
124
125         switch (mode) {
126         case S5P_PWM0:
127                 tcon |= S3C2410_TCON_T0START;
128                 tcon &= ~S3C2410_TCON_T0MANUALUPD;
129
130                 if (periodic)
131                         tcon |= S3C2410_TCON_T0RELOAD;
132                 else
133                         tcon &= ~S3C2410_TCON_T0RELOAD;
134                 break;
135
136         case S5P_PWM1:
137                 tcon |= S3C2410_TCON_T1START;
138                 tcon &= ~S3C2410_TCON_T1MANUALUPD;
139
140                 if (periodic)
141                         tcon |= S3C2410_TCON_T1RELOAD;
142                 else
143                         tcon &= ~S3C2410_TCON_T1RELOAD;
144                 break;
145
146         case S5P_PWM2:
147                 tcon |= S3C2410_TCON_T2START;
148                 tcon &= ~S3C2410_TCON_T2MANUALUPD;
149
150                 if (periodic)
151                         tcon |= S3C2410_TCON_T2RELOAD;
152                 else
153                         tcon &= ~S3C2410_TCON_T2RELOAD;
154                 break;
155
156         case S5P_PWM3:
157                 tcon |= S3C2410_TCON_T3START;
158                 tcon &= ~S3C2410_TCON_T3MANUALUPD;
159
160                 if (periodic)
161                         tcon |= S3C2410_TCON_T3RELOAD;
162                 else
163                         tcon &= ~S3C2410_TCON_T3RELOAD;
164                 break;
165
166         case S5P_PWM4:
167                 tcon |= S3C2410_TCON_T4START;
168                 tcon &= ~S3C2410_TCON_T4MANUALUPD;
169
170                 if (periodic)
171                         tcon |= S3C2410_TCON_T4RELOAD;
172                 else
173                         tcon &= ~S3C2410_TCON_T4RELOAD;
174                 break;
175
176         default:
177                 printk(KERN_ERR "Invalid Timer %d\n", mode);
178                 break;
179         }
180         __raw_writel(tcon, S3C2410_TCON);
181 }
182
183 static int s5p_set_next_event(unsigned long cycles,
184                                 struct clock_event_device *evt)
185 {
186         s5p_time_setup(timer_source.event_id, cycles);
187         s5p_time_start(timer_source.event_id, NON_PERIODIC);
188
189         return 0;
190 }
191
192 static void s5p_set_mode(enum clock_event_mode mode,
193                                 struct clock_event_device *evt)
194 {
195         s5p_time_stop(timer_source.event_id);
196
197         switch (mode) {
198         case CLOCK_EVT_MODE_PERIODIC:
199                 s5p_time_setup(timer_source.event_id, clock_count_per_tick);
200                 s5p_time_start(timer_source.event_id, PERIODIC);
201                 break;
202
203         case CLOCK_EVT_MODE_ONESHOT:
204                 break;
205
206         case CLOCK_EVT_MODE_UNUSED:
207         case CLOCK_EVT_MODE_SHUTDOWN:
208                 break;
209
210         case CLOCK_EVT_MODE_RESUME:
211                 s5p_timer_resume();
212                 break;
213         }
214 }
215
216 static void s5p_timer_resume(void)
217 {
218         /* event timer restart */
219         s5p_time_setup(timer_source.event_id, clock_count_per_tick);
220         s5p_time_start(timer_source.event_id, PERIODIC);
221
222         /* source timer restart */
223         s5p_time_setup(timer_source.source_id, TCNT_MAX);
224         s5p_time_start(timer_source.source_id, PERIODIC);
225 }
226
227 void __init s5p_set_timer_source(enum s5p_timer_mode event,
228                                  enum s5p_timer_mode source)
229 {
230         s3c_device_timer[event].dev.bus = &platform_bus_type;
231         s3c_device_timer[source].dev.bus = &platform_bus_type;
232
233         timer_source.event_id = event;
234         timer_source.source_id = source;
235 }
236
237 static struct clock_event_device time_event_device = {
238         .name           = "s5p_event_timer",
239         .features       = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,
240         .rating         = 200,
241         .set_next_event = s5p_set_next_event,
242         .set_mode       = s5p_set_mode,
243 };
244
245 static irqreturn_t s5p_clock_event_isr(int irq, void *dev_id)
246 {
247         struct clock_event_device *evt = dev_id;
248
249         evt->event_handler(evt);
250
251         return IRQ_HANDLED;
252 }
253
254 static struct irqaction s5p_clock_event_irq = {
255         .name           = "s5p_time_irq",
256         .flags          = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
257         .handler        = s5p_clock_event_isr,
258         .dev_id         = &time_event_device,
259 };
260
261 static void __init s5p_clockevent_init(void)
262 {
263         unsigned long pclk;
264         unsigned long clock_rate;
265         unsigned int irq_number;
266         struct clk *tscaler;
267
268         pclk = clk_get_rate(timerclk);
269
270         tscaler = clk_get_parent(tdiv_event);
271
272         clk_set_rate(tscaler, pclk / 2);
273         clk_set_rate(tdiv_event, pclk / 2);
274         clk_set_parent(tin_event, tdiv_event);
275
276         clock_rate = clk_get_rate(tin_event);
277         clock_count_per_tick = clock_rate / HZ;
278
279         clockevents_calc_mult_shift(&time_event_device,
280                                     clock_rate, S5PTIMER_MIN_RANGE);
281         time_event_device.max_delta_ns =
282                 clockevent_delta2ns(-1, &time_event_device);
283         time_event_device.min_delta_ns =
284                 clockevent_delta2ns(1, &time_event_device);
285
286         time_event_device.cpumask = cpumask_of(0);
287         clockevents_register_device(&time_event_device);
288
289         irq_number = timer_source.event_id + IRQ_TIMER0;
290         setup_irq(irq_number, &s5p_clock_event_irq);
291 }
292
293 static void __iomem *s5p_timer_reg(void)
294 {
295         unsigned long offset = 0;
296
297         switch (timer_source.source_id) {
298         case S5P_PWM0:
299         case S5P_PWM1:
300         case S5P_PWM2:
301         case S5P_PWM3:
302                 offset = (timer_source.source_id * 0x0c) + 0x14;
303                 break;
304
305         case S5P_PWM4:
306                 offset = 0x40;
307                 break;
308
309         default:
310                 printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id);
311                 return NULL;
312         }
313
314         return S3C_TIMERREG(offset);
315 }
316
317 /*
318  * Override the global weak sched_clock symbol with this
319  * local implementation which uses the clocksource to get some
320  * better resolution when scheduling the kernel. We accept that
321  * this wraps around for now, since it is just a relative time
322  * stamp. (Inspired by U300 implementation.)
323  */
324 static DEFINE_CLOCK_DATA(cd);
325
326 unsigned long long notrace sched_clock(void)
327 {
328         void __iomem *reg = s5p_timer_reg();
329
330         if (!reg)
331                 return 0;
332
333         return cyc_to_sched_clock(&cd, ~__raw_readl(reg), (u32)~0);
334 }
335
336 static void notrace s5p_update_sched_clock(void)
337 {
338         void __iomem *reg = s5p_timer_reg();
339
340         if (!reg)
341                 return;
342
343         update_sched_clock(&cd, ~__raw_readl(reg), (u32)~0);
344 }
345
346 static void __init s5p_clocksource_init(void)
347 {
348         unsigned long pclk;
349         unsigned long clock_rate;
350
351         pclk = clk_get_rate(timerclk);
352
353         clk_set_rate(tdiv_source, pclk / 2);
354         clk_set_parent(tin_source, tdiv_source);
355
356         clock_rate = clk_get_rate(tin_source);
357
358         s5p_time_setup(timer_source.source_id, TCNT_MAX);
359         s5p_time_start(timer_source.source_id, PERIODIC);
360
361         init_sched_clock(&cd, s5p_update_sched_clock, 32, clock_rate);
362
363         if (clocksource_mmio_init(s5p_timer_reg(), "s5p_clocksource_timer",
364                         clock_rate, 250, 32, clocksource_mmio_readl_down))
365                 panic("s5p_clocksource_timer: can't register clocksource\n");
366 }
367
368 static void __init s5p_timer_resources(void)
369 {
370
371         unsigned long event_id = timer_source.event_id;
372         unsigned long source_id = timer_source.source_id;
373         char devname[15];
374
375         timerclk = clk_get(NULL, "timers");
376         if (IS_ERR(timerclk))
377                 panic("failed to get timers clock for timer");
378
379         clk_enable(timerclk);
380
381         sprintf(devname, "s3c24xx-pwm.%lu", event_id);
382         s3c_device_timer[event_id].id = event_id;
383         s3c_device_timer[event_id].dev.init_name = devname;
384
385         tin_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tin");
386         if (IS_ERR(tin_event))
387                 panic("failed to get pwm-tin clock for event timer");
388
389         tdiv_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tdiv");
390         if (IS_ERR(tdiv_event))
391                 panic("failed to get pwm-tdiv clock for event timer");
392
393         clk_enable(tin_event);
394
395         sprintf(devname, "s3c24xx-pwm.%lu", source_id);
396         s3c_device_timer[source_id].id = source_id;
397         s3c_device_timer[source_id].dev.init_name = devname;
398
399         tin_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tin");
400         if (IS_ERR(tin_source))
401                 panic("failed to get pwm-tin clock for source timer");
402
403         tdiv_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tdiv");
404         if (IS_ERR(tdiv_source))
405                 panic("failed to get pwm-tdiv clock for source timer");
406
407         clk_enable(tin_source);
408 }
409
410 static void __init s5p_timer_init(void)
411 {
412         s5p_timer_resources();
413         s5p_clockevent_init();
414         s5p_clocksource_init();
415 }
416
417 struct sys_timer s5p_timer = {
418         .init           = s5p_timer_init,
419 };