diff options
| author | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-07-14 03:58:56 -0400 |
|---|---|---|
| committer | Heiko Carstens <heiko.carstens@de.ibm.com> | 2008-07-14 04:02:09 -0400 |
| commit | d2fec595511b5718bdb65645b3d5d99800d97943 (patch) | |
| tree | a94c3560fc2ad6aa89d61d646f73f4d7c1dfcc9b | |
| parent | 761cdf6aacdb76f819050f4938cdab1f4cdcb945 (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.c | 634 | ||||
| -rw-r--r-- | drivers/s390/cio/chsc.c | 49 | ||||
| -rw-r--r-- | drivers/s390/s390mach.c | 4 | ||||
| -rw-r--r-- | drivers/s390/s390mach.h | 3 | ||||
| -rw-r--r-- | include/asm-s390/etr.h | 45 |
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 | ||
| 174 | static void etr_timing_alert(struct etr_irq_parm *); | ||
| 175 | static void stp_timing_alert(struct stp_irq_parm *); | ||
| 176 | |||
| 177 | static 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 | |||
| 173 | static void etr_reset(void); | 187 | static void etr_reset(void); |
| 174 | static void etr_ext_handler(__u16); | 188 | static 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 | */ | ||
| 270 | static 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 | |||
| 300 | static DEFINE_PER_CPU(atomic_t, clock_sync_word); | ||
| 301 | static 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 | */ | ||
| 315 | int 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 | } | ||
| 336 | EXPORT_SYMBOL(get_sync_clock); | ||
| 337 | |||
| 338 | /* | ||
| 339 | * Make get_sync_clock return -EAGAIN. | ||
| 340 | */ | ||
| 341 | static 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 | */ | ||
| 358 | static 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. |
