Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * arch/s390/kernel/time.c | |
3 | * Time of day based timer functions. | |
4 | * | |
5 | * S390 version | |
6 | * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation | |
7 | * Author(s): Hartmut Penner (hp@de.ibm.com), | |
8 | * Martin Schwidefsky (schwidefsky@de.ibm.com), | |
9 | * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) | |
10 | * | |
11 | * Derived from "arch/i386/kernel/time.c" | |
12 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | |
13 | */ | |
14 | ||
1da177e4 LT |
15 | #include <linux/errno.h> |
16 | #include <linux/module.h> | |
17 | #include <linux/sched.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/param.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/mm.h> | |
22 | #include <linux/interrupt.h> | |
23 | #include <linux/time.h> | |
24 | #include <linux/delay.h> | |
25 | #include <linux/init.h> | |
26 | #include <linux/smp.h> | |
27 | #include <linux/types.h> | |
28 | #include <linux/profile.h> | |
29 | #include <linux/timex.h> | |
30 | #include <linux/notifier.h> | |
dc64bef5 | 31 | #include <linux/clocksource.h> |
1da177e4 LT |
32 | |
33 | #include <asm/uaccess.h> | |
34 | #include <asm/delay.h> | |
35 | #include <asm/s390_ext.h> | |
36 | #include <asm/div64.h> | |
37 | #include <asm/irq.h> | |
5a489b98 | 38 | #include <asm/irq_regs.h> |
1da177e4 LT |
39 | #include <asm/timer.h> |
40 | ||
41 | /* change this if you have some constant time drift */ | |
42 | #define USECS_PER_JIFFY ((unsigned long) 1000000/HZ) | |
43 | #define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12) | |
44 | ||
45 | /* | |
46 | * Create a small time difference between the timer interrupts | |
47 | * on the different cpus to avoid lock contention. | |
48 | */ | |
49 | #define CPU_DEVIATION (smp_processor_id() << 12) | |
50 | ||
51 | #define TICK_SIZE tick | |
52 | ||
1da177e4 LT |
53 | static ext_int_info_t ext_int_info_cc; |
54 | static u64 init_timer_cc; | |
55 | static u64 jiffies_timer_cc; | |
56 | static u64 xtime_cc; | |
57 | ||
1da177e4 LT |
58 | /* |
59 | * Scheduler clock - returns current time in nanosec units. | |
60 | */ | |
61 | unsigned long long sched_clock(void) | |
62 | { | |
9dbafa53 | 63 | return ((get_clock() - jiffies_timer_cc) * 125) >> 9; |
1da177e4 LT |
64 | } |
65 | ||
32f65f27 JG |
66 | /* |
67 | * Monotonic_clock - returns # of nanoseconds passed since time_init() | |
68 | */ | |
69 | unsigned long long monotonic_clock(void) | |
70 | { | |
71 | return sched_clock(); | |
72 | } | |
73 | EXPORT_SYMBOL(monotonic_clock); | |
74 | ||
1da177e4 LT |
75 | void tod_to_timeval(__u64 todval, struct timespec *xtime) |
76 | { | |
77 | unsigned long long sec; | |
78 | ||
79 | sec = todval >> 12; | |
80 | do_div(sec, 1000000); | |
81 | xtime->tv_sec = sec; | |
82 | todval -= (sec * 1000000) << 12; | |
83 | xtime->tv_nsec = ((todval * 1000) >> 12); | |
84 | } | |
85 | ||
1da177e4 | 86 | #ifdef CONFIG_PROFILING |
5a489b98 | 87 | #define s390_do_profile() profile_tick(CPU_PROFILING) |
1da177e4 | 88 | #else |
5a489b98 | 89 | #define s390_do_profile() do { ; } while(0) |
1da177e4 LT |
90 | #endif /* CONFIG_PROFILING */ |
91 | ||
92 | ||
93 | /* | |
94 | * timer_interrupt() needs to keep up the real-time clock, | |
95 | * as well as call the "do_timer()" routine every clocktick | |
96 | */ | |
5a489b98 | 97 | void account_ticks(void) |
1da177e4 LT |
98 | { |
99 | __u64 tmp; | |
3171a030 | 100 | __u32 ticks; |
1da177e4 LT |
101 | |
102 | /* Calculate how many ticks have passed. */ | |
103 | if (S390_lowcore.int_clock < S390_lowcore.jiffy_timer) { | |
104 | /* | |
105 | * We have to program the clock comparator even if | |
106 | * no tick has passed. That happens if e.g. an i/o | |
107 | * interrupt wakes up an idle processor that has | |
108 | * switched off its hz timer. | |
109 | */ | |
110 | tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION; | |
111 | asm volatile ("SCKC %0" : : "m" (tmp)); | |
112 | return; | |
113 | } | |
114 | tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer; | |
115 | if (tmp >= 2*CLK_TICKS_PER_JIFFY) { /* more than two ticks ? */ | |
116 | ticks = __div(tmp, CLK_TICKS_PER_JIFFY) + 1; | |
117 | S390_lowcore.jiffy_timer += | |
118 | CLK_TICKS_PER_JIFFY * (__u64) ticks; | |
119 | } else if (tmp >= CLK_TICKS_PER_JIFFY) { | |
120 | ticks = 2; | |
121 | S390_lowcore.jiffy_timer += 2*CLK_TICKS_PER_JIFFY; | |
122 | } else { | |
123 | ticks = 1; | |
124 | S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY; | |
125 | } | |
126 | ||
127 | /* set clock comparator for next tick */ | |
128 | tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION; | |
129 | asm volatile ("SCKC %0" : : "m" (tmp)); | |
130 | ||
131 | #ifdef CONFIG_SMP | |
132 | /* | |
133 | * Do not rely on the boot cpu to do the calls to do_timer. | |
134 | * Spread it over all cpus instead. | |
135 | */ | |
136 | write_seqlock(&xtime_lock); | |
137 | if (S390_lowcore.jiffy_timer > xtime_cc) { | |
3171a030 | 138 | __u32 xticks; |
1da177e4 LT |
139 | tmp = S390_lowcore.jiffy_timer - xtime_cc; |
140 | if (tmp >= 2*CLK_TICKS_PER_JIFFY) { | |
141 | xticks = __div(tmp, CLK_TICKS_PER_JIFFY); | |
142 | xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY; | |
143 | } else { | |
144 | xticks = 1; | |
145 | xtime_cc += CLK_TICKS_PER_JIFFY; | |
146 | } | |
3171a030 | 147 | do_timer(xticks); |
1da177e4 LT |
148 | } |
149 | write_sequnlock(&xtime_lock); | |
150 | #else | |
3171a030 | 151 | do_timer(ticks); |
1da177e4 LT |
152 | #endif |
153 | ||
154 | #ifdef CONFIG_VIRT_CPU_ACCOUNTING | |
1f1c12af | 155 | account_tick_vtime(current); |
1da177e4 LT |
156 | #else |
157 | while (ticks--) | |
5a489b98 | 158 | update_process_times(user_mode(get_irq_regs())); |
1da177e4 LT |
159 | #endif |
160 | ||
5a489b98 | 161 | s390_do_profile(); |
1da177e4 LT |
162 | } |
163 | ||
164 | #ifdef CONFIG_NO_IDLE_HZ | |
165 | ||
166 | #ifdef CONFIG_NO_IDLE_HZ_INIT | |
167 | int sysctl_hz_timer = 0; | |
168 | #else | |
169 | int sysctl_hz_timer = 1; | |
170 | #endif | |
171 | ||
172 | /* | |
173 | * Stop the HZ tick on the current CPU. | |
174 | * Only cpu_idle may call this function. | |
175 | */ | |
176 | static inline void stop_hz_timer(void) | |
177 | { | |
1b44e98d MS |
178 | unsigned long flags; |
179 | unsigned long seq, next; | |
4b7e0706 | 180 | __u64 timer, todval; |
5afdbd6e | 181 | int cpu = smp_processor_id(); |
1da177e4 LT |
182 | |
183 | if (sysctl_hz_timer != 0) | |
184 | return; | |
185 | ||
5afdbd6e | 186 | cpu_set(cpu, nohz_cpu_mask); |
1da177e4 LT |
187 | |
188 | /* | |
189 | * Leave the clock comparator set up for the next timer | |
190 | * tick if either rcu or a softirq is pending. | |
191 | */ | |
5afdbd6e HC |
192 | if (rcu_needs_cpu(cpu) || local_softirq_pending()) { |
193 | cpu_clear(cpu, nohz_cpu_mask); | |
1da177e4 LT |
194 | return; |
195 | } | |
196 | ||
197 | /* | |
198 | * This cpu is going really idle. Set up the clock comparator | |
199 | * for the next event. | |
200 | */ | |
1b44e98d MS |
201 | next = next_timer_interrupt(); |
202 | do { | |
203 | seq = read_seqbegin_irqsave(&xtime_lock, flags); | |
705af309 | 204 | timer = ((__u64) next) - ((__u64) jiffies) + jiffies_64; |
1b44e98d | 205 | } while (read_seqretry_irqrestore(&xtime_lock, seq, flags)); |
4b7e0706 MS |
206 | todval = -1ULL; |
207 | /* Be careful about overflows. */ | |
208 | if (timer < (-1ULL / CLK_TICKS_PER_JIFFY)) { | |
209 | timer = jiffies_timer_cc + timer * CLK_TICKS_PER_JIFFY; | |
210 | if (timer >= jiffies_timer_cc) | |
211 | todval = timer; | |
212 | } | |
213 | asm volatile ("SCKC %0" : : "m" (todval)); | |
1da177e4 LT |
214 | } |
215 | ||
216 | /* | |
217 | * Start the HZ tick on the current CPU. | |
218 | * Only cpu_idle may call this function. | |
219 | */ | |
220 | static inline void start_hz_timer(void) | |
221 | { | |
5a489b98 HC |
222 | BUG_ON(!in_interrupt()); |
223 | ||
1da177e4 LT |
224 | if (!cpu_isset(smp_processor_id(), nohz_cpu_mask)) |
225 | return; | |
5a489b98 | 226 | account_ticks(); |
1da177e4 LT |
227 | cpu_clear(smp_processor_id(), nohz_cpu_mask); |
228 | } | |
229 | ||
230 | static int nohz_idle_notify(struct notifier_block *self, | |
231 | unsigned long action, void *hcpu) | |
232 | { | |
233 | switch (action) { | |
234 | case CPU_IDLE: | |
235 | stop_hz_timer(); | |
236 | break; | |
237 | case CPU_NOT_IDLE: | |
238 | start_hz_timer(); | |
239 | break; | |
240 | } | |
241 | return NOTIFY_OK; | |
242 | } | |
243 | ||
244 | static struct notifier_block nohz_idle_nb = { | |
245 | .notifier_call = nohz_idle_notify, | |
246 | }; | |
247 | ||
2b67fc46 | 248 | static void __init nohz_init(void) |
1da177e4 LT |
249 | { |
250 | if (register_idle_notifier(&nohz_idle_nb)) | |
251 | panic("Couldn't register idle notifier"); | |
252 | } | |
253 | ||
254 | #endif | |
255 | ||
256 | /* | |
257 | * Start the clock comparator on the current CPU. | |
258 | */ | |
259 | void init_cpu_timer(void) | |
260 | { | |
261 | unsigned long cr0; | |
262 | __u64 timer; | |
263 | ||
264 | timer = jiffies_timer_cc + jiffies_64 * CLK_TICKS_PER_JIFFY; | |
265 | S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY; | |
266 | timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION; | |
267 | asm volatile ("SCKC %0" : : "m" (timer)); | |
268 | /* allow clock comparator timer interrupt */ | |
269 | __ctl_store(cr0, 0, 0); | |
270 | cr0 |= 0x800; | |
271 | __ctl_load(cr0, 0, 0); | |
272 | } | |
273 | ||
dc64bef5 MS |
274 | static cycle_t read_tod_clock(void) |
275 | { | |
276 | return get_clock(); | |
277 | } | |
278 | ||
279 | static struct clocksource clocksource_tod = { | |
280 | .name = "tod", | |
281 | .rating = 100, | |
282 | .read = read_tod_clock, | |
283 | .mask = -1ULL, | |
284 | .mult = 1000, | |
285 | .shift = 12, | |
286 | .is_continuous = 1, | |
287 | }; | |
288 | ||
289 | ||
1da177e4 LT |
290 | /* |
291 | * Initialize the TOD clock and the CPU timer of | |
292 | * the boot cpu. | |
293 | */ | |
294 | void __init time_init(void) | |
295 | { | |
296 | __u64 set_time_cc; | |
297 | int cc; | |
298 | ||
299 | /* kick the TOD clock */ | |
94c12cc7 MS |
300 | asm volatile( |
301 | " stck 0(%2)\n" | |
302 | " ipm %0\n" | |
303 | " srl %0,28" | |
304 | : "=d" (cc), "=m" (init_timer_cc) | |
305 | : "a" (&init_timer_cc) : "cc"); | |
1da177e4 LT |
306 | switch (cc) { |
307 | case 0: /* clock in set state: all is fine */ | |
308 | break; | |
309 | case 1: /* clock in non-set state: FIXME */ | |
310 | printk("time_init: TOD clock in non-set state\n"); | |
311 | break; | |
312 | case 2: /* clock in error state: FIXME */ | |
313 | printk("time_init: TOD clock in error state\n"); | |
314 | break; | |
315 | case 3: /* clock in stopped or not-operational state: FIXME */ | |
316 | printk("time_init: TOD clock stopped/non-operational\n"); | |
317 | break; | |
318 | } | |
319 | jiffies_timer_cc = init_timer_cc - jiffies_64 * CLK_TICKS_PER_JIFFY; | |
320 | ||
321 | /* set xtime */ | |
322 | xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY; | |
323 | set_time_cc = init_timer_cc - 0x8126d60e46000000LL + | |
324 | (0x3c26700LL*1000000*4096); | |
325 | tod_to_timeval(set_time_cc, &xtime); | |
326 | set_normalized_timespec(&wall_to_monotonic, | |
327 | -xtime.tv_sec, -xtime.tv_nsec); | |
328 | ||
329 | /* request the clock comparator external interrupt */ | |
d2c993d8 | 330 | if (register_early_external_interrupt(0x1004, NULL, |
1da177e4 LT |
331 | &ext_int_info_cc) != 0) |
332 | panic("Couldn't request external interrupt 0x1004"); | |
333 | ||
dc64bef5 MS |
334 | if (clocksource_register(&clocksource_tod) != 0) |
335 | panic("Could not register TOD clock source"); | |
336 | ||
1da177e4 LT |
337 | init_cpu_timer(); |
338 | ||
339 | #ifdef CONFIG_NO_IDLE_HZ | |
340 | nohz_init(); | |
341 | #endif | |
342 | ||
343 | #ifdef CONFIG_VIRT_TIMER | |
344 | vtime_init(); | |
345 | #endif | |
346 | } | |
347 |