~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/plat-s5p/s5p-time.c

Version: ~ [ linux-5.2-rc6 ] ~ [ linux-5.1.15 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.56 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.130 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.183 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.183 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.19.8 ] ~ [ linux-3.18.140 ] ~ [ linux-3.17.8 ] ~ [ linux-3.16.69 ] ~ [ linux-3.15.10 ] ~ [ linux-3.14.79 ] ~ [ linux-3.13.11 ] ~ [ linux-3.12.74 ] ~ [ linux-3.11.10 ] ~ [ linux-3.10.108 ] ~ [ linux-3.9.11 ] ~ [ linux-3.8.13 ] ~ [ linux-3.7.10 ] ~ [ linux-3.6.11 ] ~ [ linux-3.5.7 ] ~ [ linux-3.4.113 ] ~ [ linux-3.3.8 ] ~ [ linux-3.2.102 ] ~ [ linux-3.1.10 ] ~ [ linux-3.0.101 ] ~ [ linux-2.6.39.4 ] ~ [ linux-2.6.38.8 ] ~ [ linux-2.6.37.6 ] ~ [ linux-2.6.36.4 ] ~ [ linux-2.6.35.14 ] ~ [ linux-2.6.34.15 ] ~ [ linux-2.6.33.20 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.5 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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 };
420 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp