Commit | Line | Data |
---|---|---|
28ad94ec | 1 | /* |
a0719f52 | 2 | * linux/arch/arm/plat-nomadik/timer.c |
28ad94ec AR |
3 | * |
4 | * Copyright (C) 2008 STMicroelectronics | |
b102c01f | 5 | * Copyright (C) 2010 Alessandro Rubini |
8fbb97a2 | 6 | * Copyright (C) 2010 Linus Walleij for ST-Ericsson |
28ad94ec AR |
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 | #include <linux/init.h> | |
13 | #include <linux/interrupt.h> | |
14 | #include <linux/irq.h> | |
15 | #include <linux/io.h> | |
16 | #include <linux/clockchips.h> | |
ba327b1e | 17 | #include <linux/clk.h> |
28ad94ec | 18 | #include <linux/jiffies.h> |
ba327b1e | 19 | #include <linux/err.h> |
8fbb97a2 LW |
20 | #include <linux/cnt32_to_63.h> |
21 | #include <linux/timer.h> | |
28ad94ec | 22 | #include <asm/mach/time.h> |
28ad94ec | 23 | |
59b559d7 | 24 | #include <plat/mtu.h> |
28ad94ec | 25 | |
8fbb97a2 | 26 | void __iomem *mtu_base; /* Assigned by machine code */ |
59b559d7 | 27 | |
2a847513 LW |
28 | /* |
29 | * Kernel assumes that sched_clock can be called early | |
30 | * but the MTU may not yet be initialized. | |
31 | */ | |
32 | static cycle_t nmdk_read_timer_dummy(struct clocksource *cs) | |
33 | { | |
34 | return 0; | |
35 | } | |
36 | ||
b102c01f | 37 | /* clocksource: MTU decrements, so we negate the value being read. */ |
28ad94ec AR |
38 | static cycle_t nmdk_read_timer(struct clocksource *cs) |
39 | { | |
b102c01f | 40 | return -readl(mtu_base + MTU_VAL(0)); |
28ad94ec AR |
41 | } |
42 | ||
43 | static struct clocksource nmdk_clksrc = { | |
44 | .name = "mtu_0", | |
b102c01f | 45 | .rating = 200, |
2a847513 | 46 | .read = nmdk_read_timer_dummy, |
b102c01f | 47 | .mask = CLOCKSOURCE_MASK(32), |
28ad94ec AR |
48 | .flags = CLOCK_SOURCE_IS_CONTINUOUS, |
49 | }; | |
50 | ||
2a847513 LW |
51 | /* |
52 | * Override the global weak sched_clock symbol with this | |
53 | * local implementation which uses the clocksource to get some | |
8fbb97a2 LW |
54 | * better resolution when scheduling the kernel. |
55 | * | |
56 | * Because the hardware timer period may be quite short | |
57 | * (32.3 secs on the 133 MHz MTU timer selection on ux500) | |
58 | * and because cnt32_to_63() needs to be called at least once per | |
59 | * half period to work properly, a kernel keepwarm() timer is set up | |
60 | * to ensure this requirement is always met. | |
61 | * | |
62 | * Also the sched_clock timer will wrap around at some point, | |
63 | * here we set it to run continously for a year. | |
2a847513 | 64 | */ |
8fbb97a2 LW |
65 | #define SCHED_CLOCK_MIN_WRAP 3600*24*365 |
66 | static struct timer_list cnt32_to_63_keepwarm_timer; | |
67 | static u32 sched_mult; | |
68 | static u32 sched_shift; | |
69 | ||
2a847513 LW |
70 | unsigned long long notrace sched_clock(void) |
71 | { | |
8fbb97a2 LW |
72 | u64 cycles; |
73 | ||
74 | if (unlikely(!mtu_base)) | |
75 | return 0; | |
76 | ||
77 | cycles = cnt32_to_63(-readl(mtu_base + MTU_VAL(0))); | |
78 | /* | |
79 | * sched_mult is guaranteed to be even so will | |
80 | * shift out bit 63 | |
81 | */ | |
82 | return (cycles * sched_mult) >> sched_shift; | |
83 | } | |
84 | ||
85 | /* Just kick sched_clock every so often */ | |
86 | static void cnt32_to_63_keepwarm(unsigned long data) | |
87 | { | |
88 | mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + data)); | |
89 | (void) sched_clock(); | |
90 | } | |
91 | ||
92 | /* | |
93 | * Set up a timer to keep sched_clock():s 32_to_63 algorithm warm | |
94 | * once in half a 32bit timer wrap interval. | |
95 | */ | |
96 | static void __init nmdk_sched_clock_init(unsigned long rate) | |
97 | { | |
98 | u32 v; | |
99 | unsigned long delta; | |
100 | u64 days; | |
101 | ||
102 | /* Find the apropriate mult and shift factors */ | |
103 | clocks_calc_mult_shift(&sched_mult, &sched_shift, | |
104 | rate, NSEC_PER_SEC, SCHED_CLOCK_MIN_WRAP); | |
105 | /* We need to multiply by an even number to get rid of bit 63 */ | |
106 | if (sched_mult & 1) | |
107 | sched_mult++; | |
108 | ||
109 | /* Let's see what we get, take max counter and scale it */ | |
110 | days = (0xFFFFFFFFFFFFFFFFLLU * sched_mult) >> sched_shift; | |
111 | do_div(days, NSEC_PER_SEC); | |
112 | do_div(days, (3600*24)); | |
113 | ||
114 | pr_info("sched_clock: using %d bits @ %lu Hz wrap in %lu days\n", | |
115 | (64 - sched_shift), rate, (unsigned long) days); | |
116 | ||
117 | /* | |
118 | * Program a timer to kick us at half 32bit wraparound | |
119 | * Formula: seconds per wrap = (2^32) / f | |
120 | */ | |
121 | v = 0xFFFFFFFFUL / rate; | |
122 | /* We want half of the wrap time to keep cnt32_to_63 warm */ | |
123 | v /= 2; | |
124 | pr_debug("sched_clock: prescaled timer rate: %lu Hz, " | |
125 | "initialize keepwarm timer every %d seconds\n", rate, v); | |
126 | /* Convert seconds to jiffies */ | |
127 | delta = msecs_to_jiffies(v*1000); | |
128 | setup_timer(&cnt32_to_63_keepwarm_timer, cnt32_to_63_keepwarm, delta); | |
129 | mod_timer(&cnt32_to_63_keepwarm_timer, round_jiffies(jiffies + delta)); | |
2a847513 LW |
130 | } |
131 | ||
b102c01f | 132 | /* Clockevent device: use one-shot mode */ |
28ad94ec AR |
133 | static void nmdk_clkevt_mode(enum clock_event_mode mode, |
134 | struct clock_event_device *dev) | |
135 | { | |
b102c01f AR |
136 | u32 cr; |
137 | ||
28ad94ec AR |
138 | switch (mode) { |
139 | case CLOCK_EVT_MODE_PERIODIC: | |
b102c01f | 140 | pr_err("%s: periodic mode not supported\n", __func__); |
28ad94ec AR |
141 | break; |
142 | case CLOCK_EVT_MODE_ONESHOT: | |
b102c01f AR |
143 | /* Load highest value, enable device, enable interrupts */ |
144 | cr = readl(mtu_base + MTU_CR(1)); | |
145 | writel(0, mtu_base + MTU_LR(1)); | |
146 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(1)); | |
a0719f52 | 147 | writel(1 << 1, mtu_base + MTU_IMSC); |
b102c01f | 148 | break; |
28ad94ec AR |
149 | case CLOCK_EVT_MODE_SHUTDOWN: |
150 | case CLOCK_EVT_MODE_UNUSED: | |
b102c01f AR |
151 | /* disable irq */ |
152 | writel(0, mtu_base + MTU_IMSC); | |
2917947a LW |
153 | /* disable timer */ |
154 | cr = readl(mtu_base + MTU_CR(1)); | |
155 | cr &= ~MTU_CRn_ENA; | |
156 | writel(cr, mtu_base + MTU_CR(1)); | |
157 | /* load some high default value */ | |
158 | writel(0xffffffff, mtu_base + MTU_LR(1)); | |
28ad94ec AR |
159 | break; |
160 | case CLOCK_EVT_MODE_RESUME: | |
161 | break; | |
162 | } | |
163 | } | |
164 | ||
b102c01f AR |
165 | static int nmdk_clkevt_next(unsigned long evt, struct clock_event_device *ev) |
166 | { | |
167 | /* writing the value has immediate effect */ | |
168 | writel(evt, mtu_base + MTU_LR(1)); | |
169 | return 0; | |
170 | } | |
171 | ||
28ad94ec | 172 | static struct clock_event_device nmdk_clkevt = { |
b102c01f AR |
173 | .name = "mtu_1", |
174 | .features = CLOCK_EVT_FEAT_ONESHOT, | |
b102c01f | 175 | .rating = 200, |
28ad94ec | 176 | .set_mode = nmdk_clkevt_mode, |
b102c01f | 177 | .set_next_event = nmdk_clkevt_next, |
28ad94ec AR |
178 | }; |
179 | ||
180 | /* | |
b102c01f | 181 | * IRQ Handler for timer 1 of the MTU block. |
28ad94ec AR |
182 | */ |
183 | static irqreturn_t nmdk_timer_interrupt(int irq, void *dev_id) | |
184 | { | |
b102c01f | 185 | struct clock_event_device *evdev = dev_id; |
28ad94ec | 186 | |
b102c01f AR |
187 | writel(1 << 1, mtu_base + MTU_ICR); /* Interrupt clear reg */ |
188 | evdev->event_handler(evdev); | |
28ad94ec AR |
189 | return IRQ_HANDLED; |
190 | } | |
191 | ||
28ad94ec AR |
192 | static struct irqaction nmdk_timer_irq = { |
193 | .name = "Nomadik Timer Tick", | |
194 | .flags = IRQF_DISABLED | IRQF_TIMER, | |
195 | .handler = nmdk_timer_interrupt, | |
b102c01f | 196 | .dev_id = &nmdk_clkevt, |
28ad94ec AR |
197 | }; |
198 | ||
59b559d7 | 199 | void __init nmdk_timer_init(void) |
28ad94ec | 200 | { |
28ad94ec | 201 | unsigned long rate; |
ba327b1e | 202 | struct clk *clk0; |
a0719f52 | 203 | u32 cr = MTU_CRn_32BITS; |
ba327b1e LW |
204 | |
205 | clk0 = clk_get_sys("mtu0", NULL); | |
206 | BUG_ON(IS_ERR(clk0)); | |
207 | ||
ba327b1e | 208 | clk_enable(clk0); |
b102c01f AR |
209 | |
210 | /* | |
a0719f52 LW |
211 | * Tick rate is 2.4MHz for Nomadik and 2.4Mhz, 100MHz or 133 MHz |
212 | * for ux500. | |
213 | * Use a divide-by-16 counter if the tick rate is more than 32MHz. | |
214 | * At 32 MHz, the timer (with 32 bit counter) can be programmed | |
215 | * to wake-up at a max 127s a head in time. Dividing a 2.4 MHz timer | |
216 | * with 16 gives too low timer resolution. | |
b102c01f | 217 | */ |
ba327b1e | 218 | rate = clk_get_rate(clk0); |
a0719f52 | 219 | if (rate > 32000000) { |
b102c01f AR |
220 | rate /= 16; |
221 | cr |= MTU_CRn_PRESCALE_16; | |
222 | } else { | |
223 | cr |= MTU_CRn_PRESCALE_1; | |
224 | } | |
2917947a | 225 | clocksource_calc_mult_shift(&nmdk_clksrc, rate, MTU_MIN_RANGE); |
28ad94ec | 226 | |
b102c01f AR |
227 | /* Timer 0 is the free running clocksource */ |
228 | writel(cr, mtu_base + MTU_CR(0)); | |
229 | writel(0, mtu_base + MTU_LR(0)); | |
230 | writel(0, mtu_base + MTU_BGLR(0)); | |
231 | writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0)); | |
28ad94ec | 232 | |
8fbb97a2 | 233 | /* Now the clock source is ready */ |
2a847513 | 234 | nmdk_clksrc.read = nmdk_read_timer; |
28ad94ec | 235 | |
59b559d7 | 236 | if (clocksource_register(&nmdk_clksrc)) |
b102c01f AR |
237 | pr_err("timer: failed to initialize clock source %s\n", |
238 | nmdk_clksrc.name); | |
239 | ||
8fbb97a2 LW |
240 | nmdk_sched_clock_init(rate); |
241 | ||
99f76891 LW |
242 | /* Timer 1 is used for events */ |
243 | ||
2917947a LW |
244 | clockevents_calc_mult_shift(&nmdk_clkevt, rate, MTU_MIN_RANGE); |
245 | ||
b102c01f | 246 | writel(cr | MTU_CRn_ONESHOT, mtu_base + MTU_CR(1)); /* off, currently */ |
2917947a | 247 | |
b102c01f AR |
248 | nmdk_clkevt.max_delta_ns = |
249 | clockevent_delta2ns(0xffffffff, &nmdk_clkevt); | |
250 | nmdk_clkevt.min_delta_ns = | |
251 | clockevent_delta2ns(0x00000002, &nmdk_clkevt); | |
252 | nmdk_clkevt.cpumask = cpumask_of(0); | |
28ad94ec AR |
253 | |
254 | /* Register irq and clockevents */ | |
255 | setup_irq(IRQ_MTU0, &nmdk_timer_irq); | |
28ad94ec AR |
256 | clockevents_register_device(&nmdk_clkevt); |
257 | } |