1
2
3
4
5
6
7
8
9
10
11
12
13
14#include <linux/errno.h>
15#include <linux/sched.h>
16#include <linux/kernel.h>
17#include <linux/param.h>
18#include <linux/string.h>
19#include <linux/mm.h>
20#include <linux/interrupt.h>
21#include <linux/time.h>
22#include <linux/delay.h>
23#include <linux/init.h>
24#include <linux/smp.h>
25#include <linux/types.h>
26
27#include <asm/uaccess.h>
28#include <asm/delay.h>
29
30#include <linux/timex.h>
31#include <linux/config.h>
32
33#include <asm/irq.h>
34#include <asm/s390_ext.h>
35
36
37#define USECS_PER_JIFFY ((unsigned long) 1000000/HZ)
38#define CLK_TICKS_PER_JIFFY ((unsigned long) USECS_PER_JIFFY << 12)
39
40
41
42
43
44#define CPU_DEVIATION (smp_processor_id() << 12)
45
46#define TICK_SIZE tick
47
48static ext_int_info_t ext_int_info_timer;
49static u64 init_timer_cc;
50static u64 xtime_cc;
51
52extern rwlock_t xtime_lock;
53extern unsigned long wall_jiffies;
54
55void tod_to_timeval(__u64 todval, struct timeval *xtime)
56{
57 todval >>= 12;
58 xtime->tv_sec = todval / 1000000;
59 xtime->tv_usec = todval % 1000000;
60}
61
62static inline unsigned long do_gettimeoffset(void)
63{
64 __u64 now;
65
66 asm volatile ("STCK 0(%0)" : : "a" (&now) : "memory", "cc");
67 now = (now - init_timer_cc) >> 12;
68
69 now -= (__u64) wall_jiffies*USECS_PER_JIFFY;
70 return (unsigned long) now;
71}
72
73
74
75
76void do_gettimeofday(struct timeval *tv)
77{
78 unsigned long flags;
79 unsigned long usec, sec;
80
81 read_lock_irqsave(&xtime_lock, flags);
82 sec = xtime.tv_sec;
83 usec = xtime.tv_usec + do_gettimeoffset();
84 read_unlock_irqrestore(&xtime_lock, flags);
85
86 while (usec >= 1000000) {
87 usec -= 1000000;
88 sec++;
89 }
90
91 tv->tv_sec = sec;
92 tv->tv_usec = usec;
93}
94
95void do_settimeofday(struct timeval *tv)
96{
97
98 write_lock_irq(&xtime_lock);
99
100
101
102
103
104
105 tv->tv_usec -= do_gettimeoffset();
106
107 while (tv->tv_usec < 0) {
108 tv->tv_usec += 1000000;
109 tv->tv_sec--;
110 }
111
112 xtime = *tv;
113 time_adjust = 0;
114 time_status |= STA_UNSYNC;
115 time_maxerror = NTP_PHASE_LIMIT;
116 time_esterror = NTP_PHASE_LIMIT;
117 write_unlock_irq(&xtime_lock);
118}
119
120
121
122
123
124void account_ticks(struct pt_regs *regs)
125{
126 int cpu = smp_processor_id();
127 __u64 tmp;
128 __u32 ticks;
129
130
131 tmp = S390_lowcore.int_clock - S390_lowcore.jiffy_timer;
132 if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
133 ticks = tmp / CLK_TICKS_PER_JIFFY + 1;
134 S390_lowcore.jiffy_timer +=
135 CLK_TICKS_PER_JIFFY * (__u64) ticks;
136 } else if (tmp > CLK_TICKS_PER_JIFFY) {
137 ticks = 2;
138 S390_lowcore.jiffy_timer += 2*CLK_TICKS_PER_JIFFY;
139 } else {
140 ticks = 1;
141 S390_lowcore.jiffy_timer += CLK_TICKS_PER_JIFFY;
142 }
143
144
145 tmp = S390_lowcore.jiffy_timer + CPU_DEVIATION;
146 asm volatile ("SCKC %0" : : "m" (tmp));
147
148 irq_enter(cpu, 0);
149
150#ifdef CONFIG_SMP
151
152
153
154
155 write_lock(&xtime_lock);
156 if (S390_lowcore.jiffy_timer > xtime_cc) {
157 __u32 xticks;
158
159 tmp = S390_lowcore.jiffy_timer - xtime_cc;
160 if (tmp >= 2*CLK_TICKS_PER_JIFFY) {
161 xticks = tmp / CLK_TICKS_PER_JIFFY;
162 xtime_cc += (__u64) xticks * CLK_TICKS_PER_JIFFY;
163 } else {
164 xticks = 1;
165 xtime_cc += CLK_TICKS_PER_JIFFY;
166 }
167 while (xticks--)
168 do_timer(regs);
169 }
170 write_unlock(&xtime_lock);
171 while (ticks--)
172 update_process_times(user_mode(regs));
173#else
174 while (ticks--)
175 do_timer(regs);
176#endif
177
178 irq_exit(cpu, 0);
179}
180
181
182
183
184void init_cpu_timer(void)
185{
186 unsigned long cr0;
187 __u64 timer;
188
189 timer = init_timer_cc + (__u64) jiffies * CLK_TICKS_PER_JIFFY;
190 S390_lowcore.jiffy_timer = timer + CLK_TICKS_PER_JIFFY;
191 timer += CLK_TICKS_PER_JIFFY + CPU_DEVIATION;
192 asm volatile ("SCKC %0" : : "m" (timer));
193
194 asm volatile ("STCTG 0,0,%0" : "=m" (cr0) : : "memory");
195 cr0 |= 0x800;
196 asm volatile ("LCTLG 0,0,%0" : : "m" (cr0) : "memory");
197}
198
199
200
201
202
203void __init time_init(void)
204{
205 __u64 set_time_cc;
206 int cc;
207
208
209 asm volatile ("STCK 0(%1)\n\t"
210 "IPM %0\n\t"
211 "SRL %0,28" : "=r" (cc) : "a" (&init_timer_cc)
212 : "memory", "cc");
213 switch (cc) {
214 case 0:
215 break;
216 case 1:
217 printk("time_init: TOD clock in non-set state\n");
218 break;
219 case 2:
220 printk("time_init: TOD clock in error state\n");
221 break;
222 case 3:
223 printk("time_init: TOD clock stopped/non-operational\n");
224 break;
225 }
226
227
228 xtime_cc = init_timer_cc + CLK_TICKS_PER_JIFFY;
229 set_time_cc = init_timer_cc - 0x8126d60e46000000LL +
230 (0x3c26700LL*1000000*4096);
231 tod_to_timeval(set_time_cc, &xtime);
232
233
234 if (register_early_external_interrupt(0x1004, NULL,
235 &ext_int_info_timer) != 0)
236 panic("Couldn't request external interrupt 0x1004");
237
238
239 init_cpu_timer();
240}
241