summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Schwidefsky <schwidefsky@de.ibm.com>2016-10-27 06:41:39 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2017-07-26 02:25:14 -0400
commit6e2ef5e4f6cc57344762932d70d38ba4ec65fa8b (patch)
tree51e148a3b1e6991260af2ac51e6b1a6d5eb0f83a
parent45be0a02f8110a13502fac6167a893c7c0eff432 (diff)
s390/time: add support for the TOD clock epoch extension
The TOD epoch extension adds 8 epoch bits to the TOD clock to provide a continuous clock after 2042/09/17. The store-clock-extended (STCKE) instruction will store the epoch index in the first byte of the 16 bytes stored by the instruction. The read_boot_clock64 and the read_presistent_clock64 functions need to take the additional bits into account to give the correct result after 2042/09/17. The clock-comparator register will stay 64 bit wide. The comparison of the clock-comparator with the TOD clock is limited to bytes 1 to 8 of the extended TOD format. To deal with the overflow problem due to an epoch change the clock-comparator sign control in CR0 can be used to switch the comparison of the 64-bit TOD clock with the clock-comparator to a signed comparison. The decision between the signed vs. unsigned clock-comparator comparisons is done at boot time. Only if the TOD clock is in the second half of a 142 year epoch the signed comparison is used. This solves the epoch overflow issue as long as the machine is booted at least once in an epoch. Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
-rw-r--r--arch/s390/include/asm/lowcore.h48
-rw-r--r--arch/s390/include/asm/setup.h2
-rw-r--r--arch/s390/include/asm/timex.h38
-rw-r--r--arch/s390/kernel/asm-offsets.c1
-rw-r--r--arch/s390/kernel/debug.c9
-rw-r--r--arch/s390/kernel/early.c15
-rw-r--r--arch/s390/kernel/head.S3
-rw-r--r--arch/s390/kernel/head64.S4
-rw-r--r--arch/s390/kernel/irq.c3
-rw-r--r--arch/s390/kernel/setup.c2
-rw-r--r--arch/s390/kernel/time.c65
-rw-r--r--arch/s390/lib/delay.c2
12 files changed, 130 insertions, 62 deletions
diff --git a/arch/s390/include/asm/lowcore.h b/arch/s390/include/asm/lowcore.h
index 8a5b082797f8..a6870ea6ea8b 100644
--- a/arch/s390/include/asm/lowcore.h
+++ b/arch/s390/include/asm/lowcore.h
@@ -95,46 +95,46 @@ struct lowcore {
95 __u64 int_clock; /* 0x0310 */ 95 __u64 int_clock; /* 0x0310 */
96 __u64 mcck_clock; /* 0x0318 */ 96 __u64 mcck_clock; /* 0x0318 */
97 __u64 clock_comparator; /* 0x0320 */ 97 __u64 clock_comparator; /* 0x0320 */
98 __u64 boot_clock[2]; /* 0x0328 */
98 99
99 /* Current process. */ 100 /* Current process. */
100 __u64 current_task; /* 0x0328 */ 101 __u64 current_task; /* 0x0338 */
101 __u8 pad_0x318[0x320-0x318]; /* 0x0330 */ 102 __u64 kernel_stack; /* 0x0340 */
102 __u64 kernel_stack; /* 0x0338 */
103 103
104 /* Interrupt, panic and restart stack. */ 104 /* Interrupt, panic and restart stack. */
105 __u64 async_stack; /* 0x0340 */ 105 __u64 async_stack; /* 0x0348 */
106 __u64 panic_stack; /* 0x0348 */ 106 __u64 panic_stack; /* 0x0350 */
107 __u64 restart_stack; /* 0x0350 */ 107 __u64 restart_stack; /* 0x0358 */
108 108
109 /* Restart function and parameter. */ 109 /* Restart function and parameter. */
110 __u64 restart_fn; /* 0x0358 */ 110 __u64 restart_fn; /* 0x0360 */
111 __u64 restart_data; /* 0x0360 */ 111 __u64 restart_data; /* 0x0368 */
112 __u64 restart_source; /* 0x0368 */ 112 __u64 restart_source; /* 0x0370 */
113 113
114 /* Address space pointer. */ 114 /* Address space pointer. */
115 __u64 kernel_asce; /* 0x0370 */ 115 __u64 kernel_asce; /* 0x0378 */
116 __u64 user_asce; /* 0x0378 */ 116 __u64 user_asce; /* 0x0380 */
117 117
118 /* 118 /*
119 * The lpp and current_pid fields form a 119 * The lpp and current_pid fields form a
120 * 64-bit value that is set as program 120 * 64-bit value that is set as program
121 * parameter with the LPP instruction. 121 * parameter with the LPP instruction.
122 */ 122 */
123 __u32 lpp; /* 0x0380 */ 123 __u32 lpp; /* 0x0388 */
124 __u32 current_pid; /* 0x0384 */ 124 __u32 current_pid; /* 0x038c */
125 125
126 /* SMP info area */ 126 /* SMP info area */
127 __u32 cpu_nr; /* 0x0388 */ 127 __u32 cpu_nr; /* 0x0390 */
128 __u32 softirq_pending; /* 0x038c */ 128 __u32 softirq_pending; /* 0x0394 */
129 __u64 percpu_offset; /* 0x0390 */ 129 __u64 percpu_offset; /* 0x0398 */
130 __u64 vdso_per_cpu_data; /* 0x0398 */ 130 __u64 vdso_per_cpu_data; /* 0x03a0 */
131 __u64 machine_flags; /* 0x03a0 */ 131 __u64 machine_flags; /* 0x03a8 */
132 __u32 preempt_count; /* 0x03a8 */ 132 __u32 preempt_count; /* 0x03b0 */
133 __u8 pad_0x03ac[0x03b0-0x03ac]; /* 0x03ac */ 133 __u8 pad_0x03b4[0x03b8-0x03b4]; /* 0x03b4 */
134 __u64 gmap; /* 0x03b0 */ 134 __u64 gmap; /* 0x03b8 */
135 __u32 spinlock_lockval; /* 0x03b8 */ 135 __u32 spinlock_lockval; /* 0x03c0 */
136 __u32 fpu_flags; /* 0x03bc */ 136 __u32 fpu_flags; /* 0x03c4 */
137 __u8 pad_0x03c0[0x0400-0x03c0]; /* 0x03c0 */ 137 __u8 pad_0x03c8[0x0400-0x03c8]; /* 0x03c8 */
138 138
139 /* Per cpu primary space access list */ 139 /* Per cpu primary space access list */
140 __u32 paste[16]; /* 0x0400 */ 140 __u32 paste[16]; /* 0x0400 */
diff --git a/arch/s390/include/asm/setup.h b/arch/s390/include/asm/setup.h
index 49c425903894..61da4bd6edad 100644
--- a/arch/s390/include/asm/setup.h
+++ b/arch/s390/include/asm/setup.h
@@ -32,6 +32,7 @@
32#define MACHINE_FLAG_TLB_GUEST _BITUL(14) 32#define MACHINE_FLAG_TLB_GUEST _BITUL(14)
33#define MACHINE_FLAG_NX _BITUL(15) 33#define MACHINE_FLAG_NX _BITUL(15)
34#define MACHINE_FLAG_GS _BITUL(16) 34#define MACHINE_FLAG_GS _BITUL(16)
35#define MACHINE_FLAG_SCC _BITUL(17)
35 36
36#define LPP_MAGIC _BITUL(31) 37#define LPP_MAGIC _BITUL(31)
37#define LPP_PFAULT_PID_MASK _AC(0xffffffff, UL) 38#define LPP_PFAULT_PID_MASK _AC(0xffffffff, UL)
@@ -72,6 +73,7 @@ extern void detect_memory_memblock(void);
72#define MACHINE_HAS_TLB_GUEST (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_GUEST) 73#define MACHINE_HAS_TLB_GUEST (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_GUEST)
73#define MACHINE_HAS_NX (S390_lowcore.machine_flags & MACHINE_FLAG_NX) 74#define MACHINE_HAS_NX (S390_lowcore.machine_flags & MACHINE_FLAG_NX)
74#define MACHINE_HAS_GS (S390_lowcore.machine_flags & MACHINE_FLAG_GS) 75#define MACHINE_HAS_GS (S390_lowcore.machine_flags & MACHINE_FLAG_GS)
76#define MACHINE_HAS_SCC (S390_lowcore.machine_flags & MACHINE_FLAG_SCC)
75 77
76/* 78/*
77 * Console mode. Override with conmode= 79 * Console mode. Override with conmode=
diff --git a/arch/s390/include/asm/timex.h b/arch/s390/include/asm/timex.h
index 118535123f34..0ea03c11458d 100644
--- a/arch/s390/include/asm/timex.h
+++ b/arch/s390/include/asm/timex.h
@@ -15,6 +15,8 @@
15/* The value of the TOD clock for 1.1.1970. */ 15/* The value of the TOD clock for 1.1.1970. */
16#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL 16#define TOD_UNIX_EPOCH 0x7d91048bca000000ULL
17 17
18extern u64 clock_comparator_max;
19
18/* Inline functions for clock register access. */ 20/* Inline functions for clock register access. */
19static inline int set_tod_clock(__u64 time) 21static inline int set_tod_clock(__u64 time)
20{ 22{
@@ -126,7 +128,7 @@ static inline unsigned long long local_tick_disable(void)
126 unsigned long long old; 128 unsigned long long old;
127 129
128 old = S390_lowcore.clock_comparator; 130 old = S390_lowcore.clock_comparator;
129 S390_lowcore.clock_comparator = -1ULL; 131 S390_lowcore.clock_comparator = clock_comparator_max;
130 set_clock_comparator(S390_lowcore.clock_comparator); 132 set_clock_comparator(S390_lowcore.clock_comparator);
131 return old; 133 return old;
132} 134}
@@ -178,20 +180,20 @@ int get_phys_clock(unsigned long long *clock);
178void init_cpu_timer(void); 180void init_cpu_timer(void);
179unsigned long long monotonic_clock(void); 181unsigned long long monotonic_clock(void);
180 182
181extern u64 sched_clock_base_cc; 183extern unsigned char tod_clock_base[16] __aligned(8);
182 184
183/** 185/**
184 * get_clock_monotonic - returns current time in clock rate units 186 * get_clock_monotonic - returns current time in clock rate units
185 * 187 *
186 * The caller must ensure that preemption is disabled. 188 * The caller must ensure that preemption is disabled.
187 * The clock and sched_clock_base get changed via stop_machine. 189 * The clock and tod_clock_base get changed via stop_machine.
188 * Therefore preemption must be disabled when calling this 190 * Therefore preemption must be disabled when calling this
189 * function, otherwise the returned value is not guaranteed to 191 * function, otherwise the returned value is not guaranteed to
190 * be monotonic. 192 * be monotonic.
191 */ 193 */
192static inline unsigned long long get_tod_clock_monotonic(void) 194static inline unsigned long long get_tod_clock_monotonic(void)
193{ 195{
194 return get_tod_clock() - sched_clock_base_cc; 196 return get_tod_clock() - *(unsigned long long *) &tod_clock_base[1];
195} 197}
196 198
197/** 199/**
@@ -218,4 +220,32 @@ static inline unsigned long long tod_to_ns(unsigned long long todval)
218 return ((todval >> 9) * 125) + (((todval & 0x1ff) * 125) >> 9); 220 return ((todval >> 9) * 125) + (((todval & 0x1ff) * 125) >> 9);
219} 221}
220 222
223/**
224 * tod_after - compare two 64 bit TOD values
225 * @a: first 64 bit TOD timestamp
226 * @b: second 64 bit TOD timestamp
227 *
228 * Returns: true if a is later than b
229 */
230static inline int tod_after(unsigned long long a, unsigned long long b)
231{
232 if (MACHINE_HAS_SCC)
233 return (long long) a > (long long) b;
234 return a > b;
235}
236
237/**
238 * tod_after_eq - compare two 64 bit TOD values
239 * @a: first 64 bit TOD timestamp
240 * @b: second 64 bit TOD timestamp
241 *
242 * Returns: true if a is later than b
243 */
244static inline int tod_after_eq(unsigned long long a, unsigned long long b)
245{
246 if (MACHINE_HAS_SCC)
247 return (long long) a >= (long long) b;
248 return a >= b;
249}
250
221#endif 251#endif
diff --git a/arch/s390/kernel/asm-offsets.c b/arch/s390/kernel/asm-offsets.c
index b65c414b6c0e..3d42f91c95fd 100644
--- a/arch/s390/kernel/asm-offsets.c
+++ b/arch/s390/kernel/asm-offsets.c
@@ -158,6 +158,7 @@ int main(void)
158 OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock); 158 OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
159 OFFSET(__LC_INT_CLOCK, lowcore, int_clock); 159 OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
160 OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock); 160 OFFSET(__LC_MCCK_CLOCK, lowcore, mcck_clock);
161 OFFSET(__LC_BOOT_CLOCK, lowcore, boot_clock);
161 OFFSET(__LC_CURRENT, lowcore, current_task); 162 OFFSET(__LC_CURRENT, lowcore, current_task);
162 OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack); 163 OFFSET(__LC_KERNEL_STACK, lowcore, kernel_stack);
163 OFFSET(__LC_ASYNC_STACK, lowcore, async_stack); 164 OFFSET(__LC_ASYNC_STACK, lowcore, async_stack);
diff --git a/arch/s390/kernel/debug.c b/arch/s390/kernel/debug.c
index 86b3e74f569e..1d9e83c401fc 100644
--- a/arch/s390/kernel/debug.c
+++ b/arch/s390/kernel/debug.c
@@ -866,7 +866,8 @@ static inline void
866debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level, 866debug_finish_entry(debug_info_t * id, debug_entry_t* active, int level,
867 int exception) 867 int exception)
868{ 868{
869 active->id.stck = get_tod_clock_fast() - sched_clock_base_cc; 869 active->id.stck = get_tod_clock_fast() -
870 *(unsigned long long *) &tod_clock_base[1];
870 active->id.fields.cpuid = smp_processor_id(); 871 active->id.fields.cpuid = smp_processor_id();
871 active->caller = __builtin_return_address(0); 872 active->caller = __builtin_return_address(0);
872 active->id.fields.exception = exception; 873 active->id.fields.exception = exception;
@@ -1455,15 +1456,15 @@ int
1455debug_dflt_header_fn(debug_info_t * id, struct debug_view *view, 1456debug_dflt_header_fn(debug_info_t * id, struct debug_view *view,
1456 int area, debug_entry_t * entry, char *out_buf) 1457 int area, debug_entry_t * entry, char *out_buf)
1457{ 1458{
1458 unsigned long sec, usec; 1459 unsigned long base, sec, usec;
1459 char *except_str; 1460 char *except_str;
1460 unsigned long caller; 1461 unsigned long caller;
1461 int rc = 0; 1462 int rc = 0;
1462 unsigned int level; 1463 unsigned int level;
1463 1464
1464 level = entry->id.fields.level; 1465 level = entry->id.fields.level;
1465 sec = (entry->id.stck >> 12) + (sched_clock_base_cc >> 12); 1466 base = (*(unsigned long *) &tod_clock_base[0]) >> 4;
1466 sec = sec - (TOD_UNIX_EPOCH >> 12); 1467 sec = (entry->id.stck >> 12) + base - (TOD_UNIX_EPOCH >> 12);
1467 usec = do_div(sec, USEC_PER_SEC); 1468 usec = do_div(sec, USEC_PER_SEC);
1468 1469
1469 if (entry->id.fields.exception) 1470 if (entry->id.fields.exception)
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 5d20182ee8ae..added6790460 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -53,8 +53,9 @@ static void __init reset_tod_clock(void)
53 if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0) 53 if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0)
54 disabled_wait(0); 54 disabled_wait(0);
55 55
56 sched_clock_base_cc = TOD_UNIX_EPOCH; 56 memset(tod_clock_base, 0, 16);
57 S390_lowcore.last_update_clock = sched_clock_base_cc; 57 *(__u64 *) &tod_clock_base[1] = TOD_UNIX_EPOCH;
58 S390_lowcore.last_update_clock = TOD_UNIX_EPOCH;
58} 59}
59 60
60#ifdef CONFIG_SHARED_KERNEL 61#ifdef CONFIG_SHARED_KERNEL
@@ -165,8 +166,8 @@ static noinline __init void create_kernel_nss(void)
165 } 166 }
166 167
167 /* re-initialize cputime accounting. */ 168 /* re-initialize cputime accounting. */
168 sched_clock_base_cc = get_tod_clock(); 169 get_tod_clock_ext(tod_clock_base);
169 S390_lowcore.last_update_clock = sched_clock_base_cc; 170 S390_lowcore.last_update_clock = *(__u64 *) &tod_clock_base[1];
170 S390_lowcore.last_update_timer = 0x7fffffffffffffffULL; 171 S390_lowcore.last_update_timer = 0x7fffffffffffffffULL;
171 S390_lowcore.user_timer = 0; 172 S390_lowcore.user_timer = 0;
172 S390_lowcore.system_timer = 0; 173 S390_lowcore.system_timer = 0;
@@ -387,6 +388,12 @@ static __init void detect_machine_facilities(void)
387 } 388 }
388 if (test_facility(133)) 389 if (test_facility(133))
389 S390_lowcore.machine_flags |= MACHINE_FLAG_GS; 390 S390_lowcore.machine_flags |= MACHINE_FLAG_GS;
391 if (test_facility(139) && (tod_clock_base[1] & 0x80)) {
392 /* Enabled signed clock comparator comparisons */
393 S390_lowcore.machine_flags |= MACHINE_FLAG_SCC;
394 clock_comparator_max = -1ULL >> 1;
395 __ctl_set_bit(0, 53);
396 }
390} 397}
391 398
392static inline void save_vector_registers(void) 399static inline void save_vector_registers(void)
diff --git a/arch/s390/kernel/head.S b/arch/s390/kernel/head.S
index eff5b31671d4..8ed753c72d9b 100644
--- a/arch/s390/kernel/head.S
+++ b/arch/s390/kernel/head.S
@@ -302,7 +302,8 @@ ENTRY(startup_kdump)
302 xc 0xe00(256),0xe00 302 xc 0xe00(256),0xe00
303 xc 0xf00(256),0xf00 303 xc 0xf00(256),0xf00
304 lctlg %c0,%c15,0x200(%r0) # initialize control registers 304 lctlg %c0,%c15,0x200(%r0) # initialize control registers
305 stck __LC_LAST_UPDATE_CLOCK 305 stcke __LC_BOOT_CLOCK
306 mvc __LC_LAST_UPDATE_CLOCK(8),__LC_BOOT_CLOCK+1
306 spt 6f-.LPG0(%r13) 307 spt 6f-.LPG0(%r13)
307 mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13) 308 mvc __LC_LAST_UPDATE_TIMER(8),6f-.LPG0(%r13)
308 l %r15,.Lstack-.LPG0(%r13) 309 l %r15,.Lstack-.LPG0(%r13)
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index 31c91f24e562..0d8f2a858ced 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -21,8 +21,8 @@ ENTRY(startup_continue)
21 xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid 21 xc __LC_LPP+1(7,0),__LC_LPP+1 # clear lpp and current_pid
22 mvi __LC_LPP,0x80 # and set LPP_MAGIC 22 mvi __LC_LPP,0x80 # and set LPP_MAGIC
23 .insn s,0xb2800000,__LC_LPP # load program parameter 23 .insn s,0xb2800000,__LC_LPP # load program parameter
240: larl %r1,sched_clock_base_cc 240: larl %r1,tod_clock_base
25 mvc 0(8,%r1),__LC_LAST_UPDATE_CLOCK 25 mvc 0(16,%r1),__LC_BOOT_CLOCK
26 larl %r13,.LPG1 # get base 26 larl %r13,.LPG1 # get base
27 lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers 27 lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
28 lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area 28 lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c
index 6dca93b29bed..a2fdff0e730b 100644
--- a/arch/s390/kernel/irq.c
+++ b/arch/s390/kernel/irq.c
@@ -105,7 +105,8 @@ void do_IRQ(struct pt_regs *regs, int irq)
105 105
106 old_regs = set_irq_regs(regs); 106 old_regs = set_irq_regs(regs);
107 irq_enter(); 107 irq_enter();
108 if (S390_lowcore.int_clock >= S390_lowcore.clock_comparator) 108 if (tod_after_eq(S390_lowcore.int_clock,
109 S390_lowcore.clock_comparator))
109 /* Serve timer interrupts first. */ 110 /* Serve timer interrupts first. */
110 clock_comparator_work(); 111 clock_comparator_work();
111 generic_handle_irq(irq); 112 generic_handle_irq(irq);
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index bc1c95b7a4bd..e8b84894b650 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -323,7 +323,7 @@ static void __init setup_lowcore(void)
323 lc->io_new_psw.mask = PSW_KERNEL_BITS | 323 lc->io_new_psw.mask = PSW_KERNEL_BITS |
324 PSW_MASK_DAT | PSW_MASK_MCHECK; 324 PSW_MASK_DAT | PSW_MASK_MCHECK;
325 lc->io_new_psw.addr = (unsigned long) io_int_handler; 325 lc->io_new_psw.addr = (unsigned long) io_int_handler;
326 lc->clock_comparator = -1ULL; 326 lc->clock_comparator = clock_comparator_max;
327 lc->kernel_stack = ((unsigned long) &init_thread_union) 327 lc->kernel_stack = ((unsigned long) &init_thread_union)
328 + THREAD_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs); 328 + THREAD_SIZE - STACK_FRAME_OVERHEAD - sizeof(struct pt_regs);
329 lc->async_stack = (unsigned long) 329 lc->async_stack = (unsigned long)
diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c
index 192efdfac918..15abecba068e 100644
--- a/arch/s390/kernel/time.c
+++ b/arch/s390/kernel/time.c
@@ -51,8 +51,15 @@
51#include <asm/cio.h> 51#include <asm/cio.h>
52#include "entry.h" 52#include "entry.h"
53 53
54u64 sched_clock_base_cc = -1; /* Force to data section. */ 54unsigned char tod_clock_base[16] __aligned(8) = {
55EXPORT_SYMBOL_GPL(sched_clock_base_cc); 55 /* Force to data section. */
56 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
57 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
58};
59EXPORT_SYMBOL_GPL(tod_clock_base);
60
61u64 clock_comparator_max = -1ULL;
62EXPORT_SYMBOL_GPL(clock_comparator_max);
56 63
57static DEFINE_PER_CPU(struct clock_event_device, comparators); 64static DEFINE_PER_CPU(struct clock_event_device, comparators);
58 65
@@ -75,7 +82,7 @@ void __init time_early_init(void)
75 struct ptff_qui qui; 82 struct ptff_qui qui;
76 83
77 /* Initialize TOD steering parameters */ 84 /* Initialize TOD steering parameters */
78 tod_steering_end = sched_clock_base_cc; 85 tod_steering_end = *(unsigned long long *) &tod_clock_base[1];
79 vdso_data->ts_end = tod_steering_end; 86 vdso_data->ts_end = tod_steering_end;
80 87
81 if (!test_facility(28)) 88 if (!test_facility(28))
@@ -111,22 +118,27 @@ unsigned long long monotonic_clock(void)
111} 118}
112EXPORT_SYMBOL(monotonic_clock); 119EXPORT_SYMBOL(monotonic_clock);
113 120
114static void tod_to_timeval(__u64 todval, struct timespec64 *xt) 121static void ext_to_timespec64(unsigned char *clk, struct timespec64 *xt)
115{ 122{
116 unsigned long long sec; 123 unsigned long long high, low, rem, sec, nsec;
124
125 /* Split extendnd TOD clock to micro-seconds and sub-micro-seconds */
126 high = (*(unsigned long long *) clk) >> 4;
127 low = (*(unsigned long long *)&clk[7]) << 4;
128 /* Calculate seconds and nano-seconds */
129 sec = high;
130 rem = do_div(sec, 1000000);
131 nsec = (((low >> 32) + (rem << 32)) * 1000) >> 32;
117 132
118 sec = todval >> 12;
119 do_div(sec, 1000000);
120 xt->tv_sec = sec; 133 xt->tv_sec = sec;
121 todval -= (sec * 1000000) << 12; 134 xt->tv_nsec = nsec;
122 xt->tv_nsec = ((todval * 1000) >> 12);
123} 135}
124 136
125void clock_comparator_work(void) 137void clock_comparator_work(void)
126{ 138{
127 struct clock_event_device *cd; 139 struct clock_event_device *cd;
128 140
129 S390_lowcore.clock_comparator = -1ULL; 141 S390_lowcore.clock_comparator = clock_comparator_max;
130 cd = this_cpu_ptr(&comparators); 142 cd = this_cpu_ptr(&comparators);
131 cd->event_handler(cd); 143 cd->event_handler(cd);
132} 144}
@@ -148,7 +160,7 @@ void init_cpu_timer(void)
148 struct clock_event_device *cd; 160 struct clock_event_device *cd;
149 int cpu; 161 int cpu;
150 162
151 S390_lowcore.clock_comparator = -1ULL; 163 S390_lowcore.clock_comparator = clock_comparator_max;
152 set_clock_comparator(S390_lowcore.clock_comparator); 164 set_clock_comparator(S390_lowcore.clock_comparator);
153 165
154 cpu = smp_processor_id(); 166 cpu = smp_processor_id();
@@ -179,7 +191,7 @@ static void clock_comparator_interrupt(struct ext_code ext_code,
179 unsigned long param64) 191 unsigned long param64)
180{ 192{
181 inc_irq_stat(IRQEXT_CLK); 193 inc_irq_stat(IRQEXT_CLK);
182 if (S390_lowcore.clock_comparator == -1ULL) 194 if (S390_lowcore.clock_comparator == clock_comparator_max)
183 set_clock_comparator(S390_lowcore.clock_comparator); 195 set_clock_comparator(S390_lowcore.clock_comparator);
184} 196}
185 197
@@ -197,18 +209,28 @@ static void stp_reset(void);
197 209
198void read_persistent_clock64(struct timespec64 *ts) 210void read_persistent_clock64(struct timespec64 *ts)
199{ 211{
200 __u64 clock; 212 unsigned char clk[STORE_CLOCK_EXT_SIZE];
213 __u64 delta;
201 214
202 clock = get_tod_clock() - initial_leap_seconds; 215 delta = initial_leap_seconds + TOD_UNIX_EPOCH;
203 tod_to_timeval(clock - TOD_UNIX_EPOCH, ts); 216 get_tod_clock_ext(clk);
217 *(__u64 *) &clk[1] -= delta;
218 if (*(__u64 *) &clk[1] > delta)
219 clk[0]--;
220 ext_to_timespec64(clk, ts);
204} 221}
205 222
206void read_boot_clock64(struct timespec64 *ts) 223void read_boot_clock64(struct timespec64 *ts)
207{ 224{
208 __u64 clock; 225 unsigned char clk[STORE_CLOCK_EXT_SIZE];
226 __u64 delta;
209 227
210 clock = sched_clock_base_cc - initial_leap_seconds; 228 delta = initial_leap_seconds + TOD_UNIX_EPOCH;
211 tod_to_timeval(clock - TOD_UNIX_EPOCH, ts); 229 memcpy(clk, tod_clock_base, 16);
230 *(__u64 *) &clk[1] -= delta;
231 if (*(__u64 *) &clk[1] > delta)
232 clk[0]--;
233 ext_to_timespec64(clk, ts);
212} 234}
213 235
214static u64 read_tod_clock(struct clocksource *cs) 236static u64 read_tod_clock(struct clocksource *cs)
@@ -406,7 +428,10 @@ static void clock_sync_global(unsigned long long delta)
406 struct ptff_qto qto; 428 struct ptff_qto qto;
407 429
408 /* Fixup the monotonic sched clock. */ 430 /* Fixup the monotonic sched clock. */
409 sched_clock_base_cc += delta; 431 *(unsigned long long *) &tod_clock_base[1] += delta;
432 if (*(unsigned long long *) &tod_clock_base[1] < delta)
433 /* Epoch overflow */
434 tod_clock_base[0]++;
410 /* Adjust TOD steering parameters. */ 435 /* Adjust TOD steering parameters. */
411 vdso_data->tb_update_count++; 436 vdso_data->tb_update_count++;
412 now = get_tod_clock(); 437 now = get_tod_clock();
@@ -437,7 +462,7 @@ static void clock_sync_global(unsigned long long delta)
437static void clock_sync_local(unsigned long long delta) 462static void clock_sync_local(unsigned long long delta)
438{ 463{
439 /* Add the delta to the clock comparator. */ 464 /* Add the delta to the clock comparator. */
440 if (S390_lowcore.clock_comparator != -1ULL) { 465 if (S390_lowcore.clock_comparator != clock_comparator_max) {
441 S390_lowcore.clock_comparator += delta; 466 S390_lowcore.clock_comparator += delta;
442 set_clock_comparator(S390_lowcore.clock_comparator); 467 set_clock_comparator(S390_lowcore.clock_comparator);
443 } 468 }
diff --git a/arch/s390/lib/delay.c b/arch/s390/lib/delay.c
index 92e90e40b6fb..7f17555ad4d5 100644
--- a/arch/s390/lib/delay.c
+++ b/arch/s390/lib/delay.c
@@ -57,7 +57,7 @@ static void __udelay_enabled(unsigned long long usecs)
57 end = get_tod_clock_fast() + (usecs << 12); 57 end = get_tod_clock_fast() + (usecs << 12);
58 do { 58 do {
59 clock_saved = 0; 59 clock_saved = 0;
60 if (end < S390_lowcore.clock_comparator) { 60 if (tod_after(S390_lowcore.clock_comparator, end)) {
61 clock_saved = local_tick_disable(); 61 clock_saved = local_tick_disable();
62 set_clock_comparator(end); 62 set_clock_comparator(end);
63 } 63 }