2 * linux/arch/arm/mach-nomadik/timer.c
4 * Copyright (C) 2008 STMicroelectronics
5 * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
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.
11 #include <linux/init.h>
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
15 #include <linux/clockchips.h>
16 #include <linux/jiffies.h>
17 #include <asm/mach/time.h>
21 static u32 nmdk_count
; /* accumulated count */
22 static u32 nmdk_cycle
; /* write-once */
24 /* setup by the platform code */
25 void __iomem
*mtu_base
;
28 * clocksource: the MTU device is a decrementing counters, so we negate
29 * the value being read.
31 static cycle_t
nmdk_read_timer(struct clocksource
*cs
)
33 u32 count
= readl(mtu_base
+ MTU_VAL(0));
34 return nmdk_count
+ nmdk_cycle
- count
;
38 static struct clocksource nmdk_clksrc
= {
41 .read
= nmdk_read_timer
,
43 .flags
= CLOCK_SOURCE_IS_CONTINUOUS
,
47 * Clockevent device: currently only periodic mode is supported
49 static void nmdk_clkevt_mode(enum clock_event_mode mode
,
50 struct clock_event_device
*dev
)
55 case CLOCK_EVT_MODE_PERIODIC
:
56 /* enable interrupts -- and count current value? */
57 raw_local_irq_save(flags
);
58 writel(readl(mtu_base
+ MTU_IMSC
) | 1, mtu_base
+ MTU_IMSC
);
59 raw_local_irq_restore(flags
);
61 case CLOCK_EVT_MODE_ONESHOT
:
62 BUG(); /* Not supported, yet */
64 case CLOCK_EVT_MODE_SHUTDOWN
:
65 case CLOCK_EVT_MODE_UNUSED
:
67 raw_local_irq_save(flags
);
68 writel(readl(mtu_base
+ MTU_IMSC
) & ~1, mtu_base
+ MTU_IMSC
);
69 raw_local_irq_restore(flags
);
71 case CLOCK_EVT_MODE_RESUME
:
76 static struct clock_event_device nmdk_clkevt
= {
78 .features
= CLOCK_EVT_FEAT_PERIODIC
,
81 .set_mode
= nmdk_clkevt_mode
,
85 * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
86 * as we are the only users of mtu0 by now.
88 static irqreturn_t
nmdk_timer_interrupt(int irq
, void *dev_id
)
90 /* ack: "interrupt clear register" */
91 writel(1 << 0, mtu_base
+ MTU_ICR
);
93 /* we can't count lost ticks, unfortunately */
94 nmdk_count
+= nmdk_cycle
;
95 nmdk_clkevt
.event_handler(&nmdk_clkevt
);
101 * Set up timer interrupt, and return the current time in seconds.
103 static struct irqaction nmdk_timer_irq
= {
104 .name
= "Nomadik Timer Tick",
105 .flags
= IRQF_DISABLED
| IRQF_TIMER
,
106 .handler
= nmdk_timer_interrupt
,
109 static void nmdk_timer_reset(void)
113 writel(0, mtu_base
+ MTU_CR(0)); /* off */
115 /* configure load and background-load, and fire it up */
116 writel(nmdk_cycle
, mtu_base
+ MTU_LR(0));
117 writel(nmdk_cycle
, mtu_base
+ MTU_BGLR(0));
118 cr
= MTU_CRn_PERIODIC
| MTU_CRn_PRESCALE_1
| MTU_CRn_32BITS
;
119 writel(cr
, mtu_base
+ MTU_CR(0));
120 writel(cr
| MTU_CRn_ENA
, mtu_base
+ MTU_CR(0));
123 void __init
nmdk_timer_init(void)
128 rate
= CLOCK_TICK_RATE
; /* 2.4MHz */
129 nmdk_cycle
= (rate
+ HZ
/2) / HZ
;
131 /* Init the timer and register clocksource */
134 nmdk_clksrc
.mult
= clocksource_hz2mult(rate
, nmdk_clksrc
.shift
);
135 bits
= 8*sizeof(nmdk_count
);
136 nmdk_clksrc
.mask
= CLOCKSOURCE_MASK(bits
);
138 if (clocksource_register(&nmdk_clksrc
))
139 printk(KERN_ERR
"timer: failed to initialize clock "
140 "source %s\n", nmdk_clksrc
.name
);
142 /* Register irq and clockevents */
143 setup_irq(IRQ_MTU0
, &nmdk_timer_irq
);
144 nmdk_clkevt
.mult
= div_sc(rate
, NSEC_PER_SEC
, nmdk_clkevt
.shift
);
145 nmdk_clkevt
.cpumask
= cpumask_of(0);
146 clockevents_register_device(&nmdk_clkevt
);