8090403
[firefly-linux-kernel-4.4.55.git] /
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 cycle_t s5p_timer_read(struct clocksource *cs)
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 0;
312         }
313
314         return (cycle_t) ~__raw_readl(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         u32 cyc;
329         unsigned long offset = 0;
330
331         switch (timer_source.source_id) {
332         case S5P_PWM0:
333         case S5P_PWM1:
334         case S5P_PWM2:
335         case S5P_PWM3:
336                 offset = (timer_source.source_id * 0x0c) + 0x14;
337                 break;
338
339         case S5P_PWM4:
340                 offset = 0x40;
341                 break;
342
343         default:
344                 printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id);
345                 return 0;
346         }
347
348         cyc = ~__raw_readl(S3C_TIMERREG(offset));
349         return cyc_to_sched_clock(&cd, cyc, (u32)~0);
350 }
351
352 static void notrace s5p_update_sched_clock(void)
353 {
354         u32 cyc;
355         unsigned long offset = 0;
356
357         switch (timer_source.source_id) {
358         case S5P_PWM0:
359         case S5P_PWM1:
360         case S5P_PWM2:
361         case S5P_PWM3:
362                 offset = (timer_source.source_id * 0x0c) + 0x14;
363                 break;
364
365         case S5P_PWM4:
366                 offset = 0x40;
367                 break;
368
369         default:
370                 printk(KERN_ERR "Invalid Timer %d\n", timer_source.source_id);
371         }
372
373         cyc = ~__raw_readl(S3C_TIMERREG(offset));
374         update_sched_clock(&cd, cyc, (u32)~0);
375 }
376
377 struct clocksource time_clocksource = {
378         .name           = "s5p_clocksource_timer",
379         .rating         = 250,
380         .read           = s5p_timer_read,
381         .mask           = CLOCKSOURCE_MASK(32),
382         .flags          = CLOCK_SOURCE_IS_CONTINUOUS,
383 };
384
385 static void __init s5p_clocksource_init(void)
386 {
387         unsigned long pclk;
388         unsigned long clock_rate;
389
390         pclk = clk_get_rate(timerclk);
391
392         clk_set_rate(tdiv_source, pclk / 2);
393         clk_set_parent(tin_source, tdiv_source);
394
395         clock_rate = clk_get_rate(tin_source);
396
397         init_sched_clock(&cd, s5p_update_sched_clock, 32, clock_rate);
398
399         s5p_time_setup(timer_source.source_id, TCNT_MAX);
400         s5p_time_start(timer_source.source_id, PERIODIC);
401
402         if (clocksource_register_hz(&time_clocksource, clock_rate))
403                 panic("%s: can't register clocksource\n", time_clocksource.name);
404 }
405
406 static void __init s5p_timer_resources(void)
407 {
408
409         unsigned long event_id = timer_source.event_id;
410         unsigned long source_id = timer_source.source_id;
411
412         timerclk = clk_get(NULL, "timers");
413         if (IS_ERR(timerclk))
414                 panic("failed to get timers clock for timer");
415
416         clk_enable(timerclk);
417
418         tin_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tin");
419         if (IS_ERR(tin_event))
420                 panic("failed to get pwm-tin clock for event timer");
421
422         tdiv_event = clk_get(&s3c_device_timer[event_id].dev, "pwm-tdiv");
423         if (IS_ERR(tdiv_event))
424                 panic("failed to get pwm-tdiv clock for event timer");
425
426         clk_enable(tin_event);
427
428         tin_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tin");
429         if (IS_ERR(tin_source))
430                 panic("failed to get pwm-tin clock for source timer");
431
432         tdiv_source = clk_get(&s3c_device_timer[source_id].dev, "pwm-tdiv");
433         if (IS_ERR(tdiv_source))
434                 panic("failed to get pwm-tdiv clock for source timer");
435
436         clk_enable(tin_source);
437 }
438
439 static void __init s5p_timer_init(void)
440 {
441         s5p_timer_resources();
442         s5p_clockevent_init();
443         s5p_clocksource_init();
444 }
445
446 struct sys_timer s5p_timer = {
447         .init           = s5p_timer_init,
448 };