1 /* linux/arch/arm/mach-exynos4/time.c
3 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
6 * EXYNOS4 (and compatible) HRT support
7 * PWM 2/4 is used for this feature
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License version 2 as
11 * published by the Free Software Foundation.
14 #include <linux/sched.h>
15 #include <linux/interrupt.h>
16 #include <linux/irq.h>
17 #include <linux/err.h>
18 #include <linux/clk.h>
19 #include <linux/clockchips.h>
20 #include <linux/platform_device.h>
22 #include <asm/smp_twd.h>
25 #include <plat/regs-timer.h>
26 #include <asm/mach/time.h>
28 static unsigned long clock_count_per_tick
;
30 static struct clk
*tin2
;
31 static struct clk
*tin4
;
32 static struct clk
*tdiv2
;
33 static struct clk
*tdiv4
;
34 static struct clk
*timerclk
;
36 static void exynos4_pwm_stop(unsigned int pwm_id
)
40 tcon
= __raw_readl(S3C2410_TCON
);
44 tcon
&= ~S3C2410_TCON_T2START
;
47 tcon
&= ~S3C2410_TCON_T4START
;
52 __raw_writel(tcon
, S3C2410_TCON
);
55 static void exynos4_pwm_init(unsigned int pwm_id
, unsigned long tcnt
)
59 tcon
= __raw_readl(S3C2410_TCON
);
61 /* timers reload after counting zero, so reduce the count by 1 */
64 /* ensure timer is stopped... */
68 tcon
|= S3C2410_TCON_T2MANUALUPD
;
70 __raw_writel(tcnt
, S3C2410_TCNTB(2));
71 __raw_writel(tcnt
, S3C2410_TCMPB(2));
72 __raw_writel(tcon
, S3C2410_TCON
);
77 tcon
|= S3C2410_TCON_T4MANUALUPD
;
79 __raw_writel(tcnt
, S3C2410_TCNTB(4));
80 __raw_writel(tcnt
, S3C2410_TCMPB(4));
81 __raw_writel(tcon
, S3C2410_TCON
);
89 static inline void exynos4_pwm_start(unsigned int pwm_id
, bool periodic
)
93 tcon
= __raw_readl(S3C2410_TCON
);
97 tcon
|= S3C2410_TCON_T2START
;
98 tcon
&= ~S3C2410_TCON_T2MANUALUPD
;
101 tcon
|= S3C2410_TCON_T2RELOAD
;
103 tcon
&= ~S3C2410_TCON_T2RELOAD
;
106 tcon
|= S3C2410_TCON_T4START
;
107 tcon
&= ~S3C2410_TCON_T4MANUALUPD
;
110 tcon
|= S3C2410_TCON_T4RELOAD
;
112 tcon
&= ~S3C2410_TCON_T4RELOAD
;
117 __raw_writel(tcon
, S3C2410_TCON
);
120 static int exynos4_pwm_set_next_event(unsigned long cycles
,
121 struct clock_event_device
*evt
)
123 exynos4_pwm_init(2, cycles
);
124 exynos4_pwm_start(2, 0);
128 static void exynos4_pwm_set_mode(enum clock_event_mode mode
,
129 struct clock_event_device
*evt
)
134 case CLOCK_EVT_MODE_PERIODIC
:
135 exynos4_pwm_init(2, clock_count_per_tick
);
136 exynos4_pwm_start(2, 1);
138 case CLOCK_EVT_MODE_ONESHOT
:
140 case CLOCK_EVT_MODE_UNUSED
:
141 case CLOCK_EVT_MODE_SHUTDOWN
:
142 case CLOCK_EVT_MODE_RESUME
:
147 static struct clock_event_device pwm_event_device
= {
148 .name
= "pwm_timer2",
149 .features
= CLOCK_EVT_FEAT_PERIODIC
| CLOCK_EVT_FEAT_ONESHOT
,
152 .set_next_event
= exynos4_pwm_set_next_event
,
153 .set_mode
= exynos4_pwm_set_mode
,
156 irqreturn_t
exynos4_clock_event_isr(int irq
, void *dev_id
)
158 struct clock_event_device
*evt
= &pwm_event_device
;
160 evt
->event_handler(evt
);
165 static struct irqaction exynos4_clock_event_irq
= {
166 .name
= "pwm_timer2_irq",
167 .flags
= IRQF_DISABLED
| IRQF_TIMER
| IRQF_IRQPOLL
,
168 .handler
= exynos4_clock_event_isr
,
171 static void __init
exynos4_clockevent_init(void)
174 unsigned long clock_rate
;
177 pclk
= clk_get_rate(timerclk
);
179 /* configure clock tick */
181 tscaler
= clk_get_parent(tdiv2
);
183 clk_set_rate(tscaler
, pclk
/ 2);
184 clk_set_rate(tdiv2
, pclk
/ 2);
185 clk_set_parent(tin2
, tdiv2
);
187 clock_rate
= clk_get_rate(tin2
);
189 clock_count_per_tick
= clock_rate
/ HZ
;
191 pwm_event_device
.mult
=
192 div_sc(clock_rate
, NSEC_PER_SEC
, pwm_event_device
.shift
);
193 pwm_event_device
.max_delta_ns
=
194 clockevent_delta2ns(-1, &pwm_event_device
);
195 pwm_event_device
.min_delta_ns
=
196 clockevent_delta2ns(1, &pwm_event_device
);
198 pwm_event_device
.cpumask
= cpumask_of(0);
199 clockevents_register_device(&pwm_event_device
);
201 setup_irq(IRQ_TIMER2
, &exynos4_clock_event_irq
);
204 static cycle_t
exynos4_pwm4_read(struct clocksource
*cs
)
206 return (cycle_t
) ~__raw_readl(S3C_TIMERREG(0x40));
210 static void exynos4_pwm4_resume(struct clocksource
*cs
)
214 pclk
= clk_get_rate(timerclk
);
216 clk_set_rate(tdiv4
, pclk
/ 2);
217 clk_set_parent(tin4
, tdiv4
);
219 exynos4_pwm_init(4, ~0);
220 exynos4_pwm_start(4, 1);
224 struct clocksource pwm_clocksource
= {
225 .name
= "pwm_timer4",
227 .read
= exynos4_pwm4_read
,
228 .mask
= CLOCKSOURCE_MASK(32),
229 .flags
= CLOCK_SOURCE_IS_CONTINUOUS
,
231 .resume
= exynos4_pwm4_resume
,
235 static void __init
exynos4_clocksource_init(void)
238 unsigned long clock_rate
;
240 pclk
= clk_get_rate(timerclk
);
242 clk_set_rate(tdiv4
, pclk
/ 2);
243 clk_set_parent(tin4
, tdiv4
);
245 clock_rate
= clk_get_rate(tin4
);
247 exynos4_pwm_init(4, ~0);
248 exynos4_pwm_start(4, 1);
250 if (clocksource_register_hz(&pwm_clocksource
, clock_rate
))
251 panic("%s: can't register clocksource\n", pwm_clocksource
.name
);
254 static void __init
exynos4_timer_resources(void)
256 struct platform_device tmpdev
;
258 tmpdev
.dev
.bus
= &platform_bus_type
;
260 timerclk
= clk_get(NULL
, "timers");
261 if (IS_ERR(timerclk
))
262 panic("failed to get timers clock for system timer");
264 clk_enable(timerclk
);
267 tin2
= clk_get(&tmpdev
.dev
, "pwm-tin");
269 panic("failed to get pwm-tin2 clock for system timer");
271 tdiv2
= clk_get(&tmpdev
.dev
, "pwm-tdiv");
273 panic("failed to get pwm-tdiv2 clock for system timer");
277 tin4
= clk_get(&tmpdev
.dev
, "pwm-tin");
279 panic("failed to get pwm-tin4 clock for system timer");
281 tdiv4
= clk_get(&tmpdev
.dev
, "pwm-tdiv");
283 panic("failed to get pwm-tdiv4 clock for system timer");
288 static void __init
exynos4_timer_init(void)
290 #ifdef CONFIG_LOCAL_TIMERS
291 twd_base
= S5P_VA_TWD
;
294 exynos4_timer_resources();
295 exynos4_clockevent_init();
296 exynos4_clocksource_init();
299 struct sys_timer exynos4_timer
= {
300 .init
= exynos4_timer_init
,