aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-nomadik/timer.c
diff options
context:
space:
mode:
authorAlessandro Rubini <rubini@gnudd.com>2009-07-02 14:06:47 -0400
committerRussell King <rmk+kernel@arm.linux.org.uk>2009-07-02 16:20:44 -0400
commit28ad94ec61dc60207dbffdb95ff870c617fbb832 (patch)
tree4ee4d2fc06f98d70c0f7f803179b94638bc0e850 /arch/arm/mach-nomadik/timer.c
parent8c81b52422147b4b09f5adb8d0c6963342a336c6 (diff)
[ARM] 5590/1: Add basic support for ST Nomadik 8815 SoC and evaluation board
This patch adds the basic infrastructure for the Nomadik 8815 CPU and the "Nomadik Hardware Kit" NHK8815. This patch only includes the serial console and core stuff, no drivers. Signed-off-by: Alessandro Rubini <rubini@unipv.it> Acked-by: Andrea Gallo <andrea.gallo@stericsson.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-nomadik/timer.c')
-rw-r--r--arch/arm/mach-nomadik/timer.c164
1 files changed, 164 insertions, 0 deletions
diff --git a/arch/arm/mach-nomadik/timer.c b/arch/arm/mach-nomadik/timer.c
new file mode 100644
index 000000000000..d1738e7061d4
--- /dev/null
+++ b/arch/arm/mach-nomadik/timer.c
@@ -0,0 +1,164 @@
1/*
2 * linux/arch/arm/mach-nomadik/timer.c
3 *
4 * Copyright (C) 2008 STMicroelectronics
5 * Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
6 *
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.
10 */
11#include <linux/init.h>
12#include <linux/interrupt.h>
13#include <linux/irq.h>
14#include <linux/io.h>
15#include <linux/clockchips.h>
16#include <linux/jiffies.h>
17#include <asm/mach/time.h>
18#include <mach/mtu.h>
19
20#define TIMER_CTRL 0x80 /* No divisor */
21#define TIMER_PERIODIC 0x40
22#define TIMER_SZ32BIT 0x02
23
24/* Initial value for SRC control register: all timers use MXTAL/8 source */
25#define SRC_CR_INIT_MASK 0x00007fff
26#define SRC_CR_INIT_VAL 0x2aaa8000
27
28static u32 nmdk_count; /* accumulated count */
29static u32 nmdk_cycle; /* write-once */
30static __iomem void *mtu_base;
31
32/*
33 * clocksource: the MTU device is a decrementing counters, so we negate
34 * the value being read.
35 */
36static cycle_t nmdk_read_timer(struct clocksource *cs)
37{
38 u32 count = readl(mtu_base + MTU_VAL(0));
39 return nmdk_count + nmdk_cycle - count;
40
41}
42
43static struct clocksource nmdk_clksrc = {
44 .name = "mtu_0",
45 .rating = 120,
46 .read = nmdk_read_timer,
47 .shift = 20,
48 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
49};
50
51/*
52 * Clockevent device: currently only periodic mode is supported
53 */
54static void nmdk_clkevt_mode(enum clock_event_mode mode,
55 struct clock_event_device *dev)
56{
57 unsigned long flags;
58
59 switch (mode) {
60 case CLOCK_EVT_MODE_PERIODIC:
61 /* enable interrupts -- and count current value? */
62 raw_local_irq_save(flags);
63 writel(readl(mtu_base + MTU_IMSC) | 1, mtu_base + MTU_IMSC);
64 raw_local_irq_restore(flags);
65 break;
66 case CLOCK_EVT_MODE_ONESHOT:
67 BUG(); /* Not supported, yet */
68 /* FALLTHROUGH */
69 case CLOCK_EVT_MODE_SHUTDOWN:
70 case CLOCK_EVT_MODE_UNUSED:
71 /* disable irq */
72 raw_local_irq_save(flags);
73 writel(readl(mtu_base + MTU_IMSC) & ~1, mtu_base + MTU_IMSC);
74 raw_local_irq_restore(flags);
75 break;
76 case CLOCK_EVT_MODE_RESUME:
77 break;
78 }
79}
80
81static struct clock_event_device nmdk_clkevt = {
82 .name = "mtu_0",
83 .features = CLOCK_EVT_FEAT_PERIODIC,
84 .shift = 32,
85 .rating = 100,
86 .set_mode = nmdk_clkevt_mode,
87};
88
89/*
90 * IRQ Handler for the timer 0 of the MTU block. The irq is not shared
91 * as we are the only users of mtu0 by now.
92 */
93static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id)
94{
95 /* ack: "interrupt clear register" */
96 writel( 1 << 0, mtu_base + MTU_ICR);
97
98 /* we can't count lost ticks, unfortunately */
99 nmdk_count += nmdk_cycle;
100 nmdk_clkevt.event_handler(&nmdk_clkevt);
101
102 return IRQ_HANDLED;
103}
104
105/*
106 * Set up timer interrupt, and return the current time in seconds.
107 */
108static struct irqaction nmdk_timer_irq = {
109 .name = "Nomadik Timer Tick",
110 .flags = IRQF_DISABLED | IRQF_TIMER,
111 .handler = nmdk_timer_interrupt,
112};
113
114static void nmdk_timer_reset(void)
115{
116 u32 cr;
117
118 writel(0, mtu_base + MTU_CR(0)); /* off */
119
120 /* configure load and background-load, and fire it up */
121 writel(nmdk_cycle, mtu_base + MTU_LR(0));
122 writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
123 cr = MTU_CRn_PERIODIC | MTU_CRn_PRESCALE_1 | MTU_CRn_32BITS;
124 writel(cr, mtu_base + MTU_CR(0));
125 writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
126}
127
128static void __init nmdk_timer_init(void)
129{
130 u32 src_cr;
131 unsigned long rate;
132 int bits;
133
134 rate = CLOCK_TICK_RATE; /* 2.4MHz */
135 nmdk_cycle = (rate + HZ/2) / HZ;
136
137 /* Configure timer sources in "system reset controller" ctrl reg */
138 src_cr = readl(io_p2v(NOMADIK_SRC_BASE));
139 src_cr &= SRC_CR_INIT_MASK;
140 src_cr |= SRC_CR_INIT_VAL;
141 writel(src_cr, io_p2v(NOMADIK_SRC_BASE));
142
143 /* Save global pointer to mtu, used by functions above */
144 mtu_base = io_p2v(NOMADIK_MTU0_BASE);
145
146 /* Init the timer and register clocksource */
147 nmdk_timer_reset();
148
149 nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
150 bits = 8*sizeof(nmdk_count);
151 nmdk_clksrc.mask = CLOCKSOURCE_MASK(bits);
152
153 clocksource_register(&nmdk_clksrc);
154
155 /* Register irq and clockevents */
156 setup_irq(IRQ_MTU0, &nmdk_timer_irq);
157 nmdk_clkevt.mult = div_sc(rate, NSEC_PER_SEC, nmdk_clkevt.shift);
158 nmdk_clkevt.cpumask = cpumask_of(0);
159 clockevents_register_device(&nmdk_clkevt);
160}
161
162struct sys_timer nomadik_timer = {
163 .init = nmdk_timer_init,
164};