2 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
3 * http://www.samsung.com/
5 * samsung - Common hr-timer support (s3c and s5p)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
14 #include <linux/err.h>
15 #include <linux/clk.h>
16 #include <linux/clockchips.h>
17 #include <linux/list.h>
18 #include <linux/module.h>
20 #include <linux/of_address.h>
21 #include <linux/of_irq.h>
22 #include <linux/platform_device.h>
23 #include <linux/slab.h>
25 #include <clocksource/samsung_pwm.h>
27 #include <asm/sched_clock.h>
33 #define REG_TCFG0 0x00
34 #define REG_TCFG1 0x04
36 #define REG_TINT_CSTAT 0x44
38 #define REG_TCNTB(chan) (0x0c + 12 * (chan))
39 #define REG_TCMPB(chan) (0x10 + 12 * (chan))
41 #define TCFG0_PRESCALER_MASK 0xff
42 #define TCFG0_PRESCALER1_SHIFT 8
44 #define TCFG1_SHIFT(x) ((x) * 4)
45 #define TCFG1_MUX_MASK 0xf
47 #define TCON_START(chan) (1 << (4 * (chan) + 0))
48 #define TCON_MANUALUPDATE(chan) (1 << (4 * (chan) + 1))
49 #define TCON_INVERT(chan) (1 << (4 * (chan) + 2))
50 #define TCON_AUTORELOAD(chan) (1 << (4 * (chan) + 3))
52 DEFINE_SPINLOCK(samsung_pwm_lock
);
53 EXPORT_SYMBOL(samsung_pwm_lock
);
55 struct samsung_pwm_clocksource
{
57 unsigned int irq
[SAMSUNG_PWM_NUM
];
58 struct samsung_pwm_variant variant
;
62 unsigned int event_id
;
63 unsigned int source_id
;
64 unsigned int tcnt_max
;
65 unsigned int tscaler_div
;
68 unsigned long clock_count_per_tick
;
71 static struct samsung_pwm_clocksource pwm
;
73 static void samsung_timer_set_prescale(unsigned int channel
, u16 prescale
)
80 shift
= TCFG0_PRESCALER1_SHIFT
;
82 spin_lock_irqsave(&samsung_pwm_lock
, flags
);
84 reg
= readl(pwm
.base
+ REG_TCFG0
);
85 reg
&= ~(TCFG0_PRESCALER_MASK
<< shift
);
86 reg
|= (prescale
- 1) << shift
;
87 writel(reg
, pwm
.base
+ REG_TCFG0
);
89 spin_unlock_irqrestore(&samsung_pwm_lock
, flags
);
92 static void samsung_timer_set_divisor(unsigned int channel
, u8 divisor
)
94 u8 shift
= TCFG1_SHIFT(channel
);
99 bits
= (fls(divisor
) - 1) - pwm
.variant
.div_base
;
101 spin_lock_irqsave(&samsung_pwm_lock
, flags
);
103 reg
= readl(pwm
.base
+ REG_TCFG1
);
104 reg
&= ~(TCFG1_MUX_MASK
<< shift
);
105 reg
|= bits
<< shift
;
106 writel(reg
, pwm
.base
+ REG_TCFG1
);
108 spin_unlock_irqrestore(&samsung_pwm_lock
, flags
);
111 static void samsung_time_stop(unsigned int channel
)
119 spin_lock_irqsave(&samsung_pwm_lock
, flags
);
121 tcon
= __raw_readl(pwm
.base
+ REG_TCON
);
122 tcon
&= ~TCON_START(channel
);
123 __raw_writel(tcon
, pwm
.base
+ REG_TCON
);
125 spin_unlock_irqrestore(&samsung_pwm_lock
, flags
);
128 static void samsung_time_setup(unsigned int channel
, unsigned long tcnt
)
132 unsigned int tcon_chan
= channel
;
137 spin_lock_irqsave(&samsung_pwm_lock
, flags
);
139 tcon
= __raw_readl(pwm
.base
+ REG_TCON
);
141 tcon
&= ~(TCON_START(tcon_chan
) | TCON_AUTORELOAD(tcon_chan
));
142 tcon
|= TCON_MANUALUPDATE(tcon_chan
);
144 __raw_writel(tcnt
, pwm
.base
+ REG_TCNTB(channel
));
145 __raw_writel(tcnt
, pwm
.base
+ REG_TCMPB(channel
));
146 __raw_writel(tcon
, pwm
.base
+ REG_TCON
);
148 spin_unlock_irqrestore(&samsung_pwm_lock
, flags
);
151 static void samsung_time_start(unsigned int channel
, bool periodic
)
159 spin_lock_irqsave(&samsung_pwm_lock
, flags
);
161 tcon
= __raw_readl(pwm
.base
+ REG_TCON
);
163 tcon
&= ~TCON_MANUALUPDATE(channel
);
164 tcon
|= TCON_START(channel
);
167 tcon
|= TCON_AUTORELOAD(channel
);
169 tcon
&= ~TCON_AUTORELOAD(channel
);
171 __raw_writel(tcon
, pwm
.base
+ REG_TCON
);
173 spin_unlock_irqrestore(&samsung_pwm_lock
, flags
);
176 static int samsung_set_next_event(unsigned long cycles
,
177 struct clock_event_device
*evt
)
180 * This check is needed to account for internal rounding
181 * errors inside clockevents core, which might result in
182 * passing cycles = 0, which in turn would not generate any
183 * timer interrupt and hang the system.
185 * Another solution would be to set up the clockevent device
186 * with min_delta = 2, but this would unnecessarily increase
187 * the minimum sleep period.
192 samsung_time_setup(pwm
.event_id
, cycles
);
193 samsung_time_start(pwm
.event_id
, false);
198 static void samsung_timer_resume(void)
200 /* event timer restart */
201 samsung_time_setup(pwm
.event_id
, pwm
.clock_count_per_tick
- 1);
202 samsung_time_start(pwm
.event_id
, true);
204 /* source timer restart */
205 samsung_time_setup(pwm
.source_id
, pwm
.tcnt_max
);
206 samsung_time_start(pwm
.source_id
, true);
209 static void samsung_set_mode(enum clock_event_mode mode
,
210 struct clock_event_device
*evt
)
212 samsung_time_stop(pwm
.event_id
);
215 case CLOCK_EVT_MODE_PERIODIC
:
216 samsung_time_setup(pwm
.event_id
, pwm
.clock_count_per_tick
- 1);
217 samsung_time_start(pwm
.event_id
, true);
220 case CLOCK_EVT_MODE_ONESHOT
:
223 case CLOCK_EVT_MODE_UNUSED
:
224 case CLOCK_EVT_MODE_SHUTDOWN
:
227 case CLOCK_EVT_MODE_RESUME
:
228 samsung_timer_resume();
233 static struct clock_event_device time_event_device
= {
234 .name
= "samsung_event_timer",
235 .features
= CLOCK_EVT_FEAT_PERIODIC
| CLOCK_EVT_FEAT_ONESHOT
,
237 .set_next_event
= samsung_set_next_event
,
238 .set_mode
= samsung_set_mode
,
241 static irqreturn_t
samsung_clock_event_isr(int irq
, void *dev_id
)
243 struct clock_event_device
*evt
= dev_id
;
245 if (pwm
.variant
.has_tint_cstat
) {
246 u32 mask
= (1 << pwm
.event_id
);
247 writel(mask
| (mask
<< 5), pwm
.base
+ REG_TINT_CSTAT
);
250 evt
->event_handler(evt
);
255 static struct irqaction samsung_clock_event_irq
= {
256 .name
= "samsung_time_irq",
257 .flags
= IRQF_DISABLED
| IRQF_TIMER
| IRQF_IRQPOLL
,
258 .handler
= samsung_clock_event_isr
,
259 .dev_id
= &time_event_device
,
262 static void __init
samsung_clockevent_init(void)
265 unsigned long clock_rate
;
266 unsigned int irq_number
;
268 pclk
= clk_get_rate(pwm
.timerclk
);
270 samsung_timer_set_prescale(pwm
.event_id
, pwm
.tscaler_div
);
271 samsung_timer_set_divisor(pwm
.event_id
, pwm
.tdiv
);
273 clock_rate
= pclk
/ (pwm
.tscaler_div
* pwm
.tdiv
);
274 pwm
.clock_count_per_tick
= clock_rate
/ HZ
;
276 time_event_device
.cpumask
= cpumask_of(0);
277 clockevents_config_and_register(&time_event_device
,
278 clock_rate
, 1, pwm
.tcnt_max
);
280 irq_number
= pwm
.irq
[pwm
.event_id
];
281 setup_irq(irq_number
, &samsung_clock_event_irq
);
283 if (pwm
.variant
.has_tint_cstat
) {
284 u32 mask
= (1 << pwm
.event_id
);
285 writel(mask
| (mask
<< 5), pwm
.base
+ REG_TINT_CSTAT
);
289 static void __iomem
*samsung_timer_reg(void)
291 switch (pwm
.source_id
) {
296 return pwm
.base
+ pwm
.source_id
* 0x0c + 0x14;
299 return pwm
.base
+ 0x40;
307 * Override the global weak sched_clock symbol with this
308 * local implementation which uses the clocksource to get some
309 * better resolution when scheduling the kernel. We accept that
310 * this wraps around for now, since it is just a relative time
311 * stamp. (Inspired by U300 implementation.)
313 static u32 notrace
samsung_read_sched_clock(void)
315 void __iomem
*reg
= samsung_timer_reg();
320 return ~__raw_readl(reg
);
323 static void __init
samsung_clocksource_init(void)
325 void __iomem
*reg
= samsung_timer_reg();
327 unsigned long clock_rate
;
330 pclk
= clk_get_rate(pwm
.timerclk
);
332 samsung_timer_set_prescale(pwm
.source_id
, pwm
.tscaler_div
);
333 samsung_timer_set_divisor(pwm
.source_id
, pwm
.tdiv
);
335 clock_rate
= pclk
/ (pwm
.tscaler_div
* pwm
.tdiv
);
337 samsung_time_setup(pwm
.source_id
, pwm
.tcnt_max
);
338 samsung_time_start(pwm
.source_id
, true);
340 setup_sched_clock(samsung_read_sched_clock
,
341 pwm
.variant
.bits
, clock_rate
);
343 ret
= clocksource_mmio_init(reg
, "samsung_clocksource_timer",
344 clock_rate
, 250, pwm
.variant
.bits
,
345 clocksource_mmio_readl_down
);
347 panic("samsung_clocksource_timer: can't register clocksource\n");
350 static void __init
samsung_timer_resources(void)
352 pwm
.timerclk
= clk_get(NULL
, "timers");
353 if (IS_ERR(pwm
.timerclk
))
354 panic("failed to get timers clock for timer");
356 clk_prepare_enable(pwm
.timerclk
);
358 pwm
.tcnt_max
= (1UL << pwm
.variant
.bits
) - 1;
359 if (pwm
.variant
.bits
== 16) {
360 pwm
.tscaler_div
= 25;
371 static void __init
_samsung_pwm_clocksource_init(void)
376 mask
= ~pwm
.variant
.output_mask
& ((1 << SAMSUNG_PWM_NUM
) - 1);
377 channel
= fls(mask
) - 1;
379 panic("failed to find PWM channel for clocksource");
380 pwm
.source_id
= channel
;
382 mask
&= ~(1 << channel
);
383 channel
= fls(mask
) - 1;
385 panic("failed to find PWM channel for clock event");
386 pwm
.event_id
= channel
;
388 samsung_timer_resources();
389 samsung_clockevent_init();
390 samsung_clocksource_init();
393 void __init
samsung_pwm_clocksource_init(void __iomem
*base
,
394 unsigned int *irqs
, struct samsung_pwm_variant
*variant
)
397 memcpy(&pwm
.variant
, variant
, sizeof(pwm
.variant
));
398 memcpy(pwm
.irq
, irqs
, SAMSUNG_PWM_NUM
* sizeof(*irqs
));
400 _samsung_pwm_clocksource_init();
403 #ifdef CONFIG_CLKSRC_OF
404 static void __init
samsung_pwm_alloc(struct device_node
*np
,
405 const struct samsung_pwm_variant
*variant
)
408 struct property
*prop
;
413 memcpy(&pwm
.variant
, variant
, sizeof(pwm
.variant
));
414 for (i
= 0; i
< SAMSUNG_PWM_NUM
; ++i
)
415 pwm
.irq
[i
] = irq_of_parse_and_map(np
, i
);
417 of_property_for_each_u32(np
, "samsung,pwm-outputs", prop
, cur
, val
) {
418 if (val
>= SAMSUNG_PWM_NUM
) {
419 pr_warning("%s: invalid channel index in samsung,pwm-outputs property\n",
423 pwm
.variant
.output_mask
|= 1 << val
;
426 of_address_to_resource(np
, 0, &res
);
427 if (!request_mem_region(res
.start
,
428 resource_size(&res
), "samsung-pwm")) {
429 pr_err("%s: failed to request IO mem region\n", __func__
);
433 pwm
.base
= ioremap(res
.start
, resource_size(&res
));
435 pr_err("%s: failed to map PWM registers\n", __func__
);
436 release_mem_region(res
.start
, resource_size(&res
));
440 _samsung_pwm_clocksource_init();
443 static const struct samsung_pwm_variant s3c24xx_variant
= {
446 .has_tint_cstat
= false,
447 .tclk_mask
= (1 << 4),
450 static void __init
s3c2410_pwm_clocksource_init(struct device_node
*np
)
452 samsung_pwm_alloc(np
, &s3c24xx_variant
);
454 CLOCKSOURCE_OF_DECLARE(s3c2410_pwm
, "samsung,s3c2410-pwm", s3c2410_pwm_clocksource_init
);
456 static const struct samsung_pwm_variant s3c64xx_variant
= {
459 .has_tint_cstat
= true,
460 .tclk_mask
= (1 << 7) | (1 << 6) | (1 << 5),
463 static void __init
s3c64xx_pwm_clocksource_init(struct device_node
*np
)
465 samsung_pwm_alloc(np
, &s3c64xx_variant
);
467 CLOCKSOURCE_OF_DECLARE(s3c6400_pwm
, "samsung,s3c6400-pwm", s3c64xx_pwm_clocksource_init
);
469 static const struct samsung_pwm_variant s5p64x0_variant
= {
472 .has_tint_cstat
= true,
476 static void __init
s5p64x0_pwm_clocksource_init(struct device_node
*np
)
478 samsung_pwm_alloc(np
, &s5p64x0_variant
);
480 CLOCKSOURCE_OF_DECLARE(s5p6440_pwm
, "samsung,s5p6440-pwm", s5p64x0_pwm_clocksource_init
);
482 static const struct samsung_pwm_variant s5p_variant
= {
485 .has_tint_cstat
= true,
486 .tclk_mask
= (1 << 5),
489 static void __init
s5p_pwm_clocksource_init(struct device_node
*np
)
491 samsung_pwm_alloc(np
, &s5p_variant
);
493 CLOCKSOURCE_OF_DECLARE(s5pc100_pwm
, "samsung,s5pc100-pwm", s5p_pwm_clocksource_init
);