aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2008-07-14 03:58:56 -0400
committerHeiko Carstens <heiko.carstens@de.ibm.com>2008-07-14 04:02:09 -0400
commitd2fec595511b5718bdb65645b3d5d99800d97943 (patch)
treea94c3560fc2ad6aa89d61d646f73f4d7c1dfcc9b
parent761cdf6aacdb76f819050f4938cdab1f4cdcb945 (diff)
[S390] stp support.
Add support for clock synchronization with the server time protocol. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com>
-rw-r--r--arch/s390/kernel/time.c634
-rw-r--r--drivers/s390/cio/chsc.c49
-rw-r--r--drivers/s390/s390mach.c4
-rw-r--r--drivers/s390/s390mach.h3
-rw-r--r--include/asm-s390/etr.h45
5 files changed, 582 insertions, 153 deletions
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 7aec676fefd5..7418bebb547f 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -3,7 +3,7 @@
3 * Time of day based timer functions. 3 * Time of day based timer functions.
4 * 4 *
5 * S390 version 5 * S390 version
6 * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation 6 * Copyright IBM Corp. 1999, 2008
7 * Author(s): Hartmut Penner (hp@de.ibm.com), 7 * Author(s): Hartmut Penner (hp@de.ibm.com),
8 * Martin Schwidefsky (schwidefsky@de.ibm.com), 8 * Martin Schwidefsky (schwidefsky@de.ibm.com),
9 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com) 9 * Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com)
@@ -31,6 +31,7 @@
31#include <linux/notifier.h> 31#include <linux/notifier.h>
32#include <linux/clocksource.h> 32#include <linux/clocksource.h>
33#include <linux/clockchips.h> 33#include <linux/clockchips.h>
34#include <linux/bootmem.h>
34#include <asm/uaccess.h> 35#include <asm/uaccess.h>
35#include <asm/delay.h> 36#include <asm/delay.h>
36#include <asm/s390_ext.h> 37#include <asm/s390_ext.h>
@@ -162,7 +163,7 @@ void init_cpu_timer(void)
162 /* Enable clock comparator timer interrupt. */ 163 /* Enable clock comparator timer interrupt. */
163 __ctl_set_bit(0,11); 164 __ctl_set_bit(0,11);
164 165
165 /* Always allow ETR external interrupts, even without an ETR. */ 166 /* Always allow the timing alert external interrupt. */
166 __ctl_set_bit(0, 4); 167 __ctl_set_bit(0, 4);
167} 168}
168 169
@@ -170,8 +171,21 @@ static void clock_comparator_interrupt(__u16 code)
170{ 171{
171} 172}
172 173
174static void etr_timing_alert(struct etr_irq_parm *);
175static void stp_timing_alert(struct stp_irq_parm *);
176
177static void timing_alert_interrupt(__u16 code)
178{
179 if (S390_lowcore.ext_params & 0x00c40000)
180 etr_timing_alert((struct etr_irq_parm *)
181 &S390_lowcore.ext_params);
182 if (S390_lowcore.ext_params & 0x00038000)
183 stp_timing_alert((struct stp_irq_parm *)
184 &S390_lowcore.ext_params);
185}
186
173static void etr_reset(void); 187static void etr_reset(void);
174static void etr_ext_handler(__u16); 188static void stp_reset(void);
175 189
176/* 190/*
177 * Get the TOD clock running. 191 * Get the TOD clock running.
@@ -181,6 +195,7 @@ static u64 __init reset_tod_clock(void)
181 u64 time; 195 u64 time;
182 196
183 etr_reset(); 197 etr_reset();
198 stp_reset();
184 if (store_clock(&time) == 0) 199 if (store_clock(&time) == 0)
185 return time; 200 return time;
186 /* TOD clock not running. Set the clock to Unix Epoch. */ 201 /* TOD clock not running. Set the clock to Unix Epoch. */
@@ -231,8 +246,9 @@ void __init time_init(void)
231 if (clocksource_register(&clocksource_tod) != 0) 246 if (clocksource_register(&clocksource_tod) != 0)
232 panic("Could not register TOD clock source"); 247 panic("Could not register TOD clock source");
233 248
234 /* request the etr external interrupt */ 249 /* request the timing alert external interrupt */
235 if (register_early_external_interrupt(0x1406, etr_ext_handler, 250 if (register_early_external_interrupt(0x1406,
251 timing_alert_interrupt,
236 &ext_int_etr_cc) != 0) 252 &ext_int_etr_cc) != 0)
237 panic("Couldn't request external interrupt 0x1406"); 253 panic("Couldn't request external interrupt 0x1406");
238 254
@@ -245,10 +261,112 @@ void __init time_init(void)
245} 261}
246 262
247/* 263/*
264 * The time is "clock". old is what we think the time is.
265 * Adjust the value by a multiple of jiffies and add the delta to ntp.
266 * "delay" is an approximation how long the synchronization took. If
267 * the time correction is positive, then "delay" is subtracted from
268 * the time difference and only the remaining part is passed to ntp.
269 */
270static unsigned long long adjust_time(unsigned long long old,
271 unsigned long long clock,
272 unsigned long long delay)
273{
274 unsigned long long delta, ticks;
275 struct timex adjust;
276
277 if (clock > old) {
278 /* It is later than we thought. */
279 delta = ticks = clock - old;
280 delta = ticks = (delta < delay) ? 0 : delta - delay;
281 delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
282 adjust.offset = ticks * (1000000 / HZ);
283 } else {
284 /* It is earlier than we thought. */
285 delta = ticks = old - clock;
286 delta -= do_div(ticks, CLK_TICKS_PER_JIFFY);
287 delta = -delta;
288 adjust.offset = -ticks * (1000000 / HZ);
289 }
290 jiffies_timer_cc += delta;
291 if (adjust.offset != 0) {
292 printk(KERN_NOTICE "etr: time adjusted by %li micro-seconds\n",
293 adjust.offset);
294 adjust.modes = ADJ_OFFSET_SINGLESHOT;
295 do_adjtimex(&adjust);
296 }
297 return delta;
298}
299
300static DEFINE_PER_CPU(atomic_t, clock_sync_word);
301static unsigned long clock_sync_flags;
302
303#define CLOCK_SYNC_HAS_ETR 0
304#define CLOCK_SYNC_HAS_STP 1
305#define CLOCK_SYNC_ETR 2
306#define CLOCK_SYNC_STP 3
307
308/*
309 * The synchronous get_clock function. It will write the current clock
310 * value to the clock pointer and return 0 if the clock is in sync with
311 * the external time source. If the clock mode is local it will return
312 * -ENOSYS and -EAGAIN if the clock is not in sync with the external
313 * reference.
314 */
315int get_sync_clock(unsigned long long *clock)
316{
317 atomic_t *sw_ptr;
318 unsigned int sw0, sw1;
319
320 sw_ptr = &get_cpu_var(clock_sync_word);
321 sw0 = atomic_read(sw_ptr);
322 *clock = get_clock();
323 sw1 = atomic_read(sw_ptr);
324 put_cpu_var(clock_sync_sync);
325 if (sw0 == sw1 && (sw0 & 0x80000000U))
326 /* Success: time is in sync. */
327 return 0;
328 if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&
329 !test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
330 return -ENOSYS;
331 if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&
332 !test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
333 return -EACCES;
334 return -EAGAIN;
335}
336EXPORT_SYMBOL(get_sync_clock);
337
338/*
339 * Make get_sync_clock return -EAGAIN.
340 */
341static void disable_sync_clock(void *dummy)
342{
343 atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
344 /*
345 * Clear the in-sync bit 2^31. All get_sync_clock calls will
346 * fail until the sync bit is turned back on. In addition
347 * increase the "sequence" counter to avoid the race of an
348 * etr event and the complete recovery against get_sync_clock.
349 */
350 atomic_clear_mask(0x80000000, sw_ptr);
351 atomic_inc(sw_ptr);
352}
353
354/*
355 * Make get_sync_clock return 0 again.
356 * Needs to be called from a context disabled for preemption.
357 */
358static void enable_sync_clock(void)
359{
360 atomic_t *sw_ptr = &__get_cpu_var(clock_sync_word);
361 atomic_set_mask(0x80000000, sw_ptr);
362}
363
364/*
248 * External Time Reference (ETR) code. 365 * External Time Reference (ETR) code.