http://downloads.netgear.com/files/GPL/DM111PSP_v3.61d_GPL.tar.gz
[bcm963xx.git] / kernel / linux / arch / s390 / kernel / vtime.c
1 /*
2  *  arch/s390/kernel/vtime.c
3  *    Virtual cpu timer based timer functions.
4  *
5  *  S390 version
6  *    Copyright (C) 2004 IBM Deutschland Entwicklung GmbH, IBM Corporation
7  *    Author(s): Jan Glauber <jan.glauber@de.ibm.com>
8  */
9
10 #include <linux/config.h>
11 #include <linux/module.h>
12 #include <linux/kernel.h>
13 #include <linux/time.h>
14 #include <linux/delay.h>
15 #include <linux/init.h>
16 #include <linux/smp.h>
17 #include <linux/types.h>
18 #include <linux/timex.h>
19 #include <linux/notifier.h>
20
21 #include <asm/s390_ext.h>
22 #include <asm/timer.h>
23
24 #define VTIMER_MAGIC (0x4b87ad6e + 1)
25 static ext_int_info_t ext_int_info_timer;
26 DEFINE_PER_CPU(struct vtimer_queue, virt_cpu_timer);
27
28 void start_cpu_timer(void)
29 {
30         struct vtimer_queue *vt_list;
31
32         vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
33         set_vtimer(vt_list->idle);
34 }
35
36 void stop_cpu_timer(void)
37 {
38         __u64 done;
39         struct vtimer_queue *vt_list;
40
41         vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
42
43         /* nothing to do */
44         if (list_empty(&vt_list->list)) {
45                 vt_list->idle = VTIMER_MAX_SLICE;
46                 goto fire;
47         }
48
49         /* store progress */
50         asm volatile ("STPT %0" : "=m" (done));
51
52         /*
53          * If done is negative we do not stop the CPU timer
54          * because we will get instantly an interrupt that
55          * will start the CPU timer again.
56          */
57         if (done & 1LL<<63)
58                 return;
59         else
60                 vt_list->offset += vt_list->to_expire - done;
61
62         /* save the actual expire value */
63         vt_list->idle = done;
64
65         /*
66          * We cannot halt the CPU timer, we just write a value that
67          * nearly never expires (only after 71 years) and re-write
68          * the stored expire value if we continue the timer
69          */
70  fire:
71         set_vtimer(VTIMER_MAX_SLICE);
72 }
73
74 void set_vtimer(__u64 expires)
75 {
76         asm volatile ("SPT %0" : : "m" (expires));
77
78         /* store expire time for this CPU timer */
79         per_cpu(virt_cpu_timer, smp_processor_id()).to_expire = expires;
80 }
81
82 /*
83  * Sorted add to a list. List is linear searched until first bigger
84  * element is found.
85  */
86 void list_add_sorted(struct vtimer_list *timer, struct list_head *head)
87 {
88         struct vtimer_list *event;
89
90         list_for_each_entry(event, head, entry) {
91                 if (event->expires > timer->expires) {
92                         list_add_tail(&timer->entry, &event->entry);
93                         return;
94                 }
95         }
96         list_add_tail(&timer->entry, head);
97 }
98
99 /*
100  * Do the callback functions of expired vtimer events.
101  * Called from within the interrupt handler.
102  */
103 static void do_callbacks(struct list_head *cb_list, struct pt_regs *regs)
104 {
105         struct vtimer_queue *vt_list;
106         struct vtimer_list *event, *tmp;
107         void (*fn)(unsigned long, struct pt_regs*);
108         unsigned long data;
109
110         if (list_empty(cb_list))
111                 return;
112
113         vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
114
115         list_for_each_entry_safe(event, tmp, cb_list, entry) {
116                 fn = event->function;
117                 data = event->data;
118                 fn(data, regs);
119
120                 if (!event->interval)
121                         /* delete one shot timer */
122                         list_del_init(&event->entry);
123                 else {
124                         /* move interval timer back to list */
125                         spin_lock(&vt_list->lock);
126                         list_del_init(&event->entry);
127                         list_add_sorted(event, &vt_list->list);
128                         spin_unlock(&vt_list->lock);
129                 }
130         }
131 }
132
133 /*
134  * Handler for the virtual CPU timer.
135  */
136 static void do_cpu_timer_interrupt(struct pt_regs *regs, __u16 error_code)
137 {
138         int cpu;
139         __u64 next, delta;
140         struct vtimer_queue *vt_list;
141         struct vtimer_list *event, *tmp;
142         struct list_head *ptr;
143         /* the callback queue */
144         struct list_head cb_list;
145
146         INIT_LIST_HEAD(&cb_list);
147         cpu = smp_processor_id();
148         vt_list = &per_cpu(virt_cpu_timer, cpu);
149
150         /* walk timer list, fire all expired events */
151         spin_lock(&vt_list->lock);
152
153         if (vt_list->to_expire < VTIMER_MAX_SLICE)
154                 vt_list->offset += vt_list->to_expire;
155
156         list_for_each_entry_safe(event, tmp, &vt_list->list, entry) {
157                 if (event->expires > vt_list->offset)
158                         /* found first unexpired event, leave */
159                         break;
160
161                 /* re-charge interval timer, we have to add the offset */
162                 if (event->interval)
163                         event->expires = event->interval + vt_list->offset;
164
165                 /* move expired timer to the callback queue */
166                 list_move_tail(&event->entry, &cb_list);
167         }
168         spin_unlock(&vt_list->lock);
169         do_callbacks(&cb_list, regs);
170
171         /* next event is first in list */
172         spin_lock(&vt_list->lock);
173         if (!list_empty(&vt_list->list)) {
174                 ptr = vt_list->list.next;
175                 event = list_entry(ptr, struct vtimer_list, entry);
176                 next = event->expires - vt_list->offset;
177
178                 /* add the expired time from this interrupt handler
179                  * and the callback functions
180                  */
181                 asm volatile ("STPT %0" : "=m" (delta));
182                 delta = 0xffffffffffffffffLL - delta + 1;
183                 vt_list->offset += delta;
184                 next -= delta;
185         } else {
186                 vt_list->offset = 0;
187                 next = VTIMER_MAX_SLICE;
188         }
189         spin_unlock(&vt_list->lock);
190         set_vtimer(next);
191 }
192
193 void init_virt_timer(struct vtimer_list *timer)
194 {
195         timer->magic = VTIMER_MAGIC;
196         timer->function = NULL;
197         INIT_LIST_HEAD(&timer->entry);
198         spin_lock_init(&timer->lock);
199 }
200 EXPORT_SYMBOL(init_virt_timer);
201
202 static inline int check_vtimer(struct vtimer_list *timer)
203 {
204         if (timer->magic != VTIMER_MAGIC)
205                 return -EINVAL;
206         return 0;
207 }
208
209 static inline int vtimer_pending(struct vtimer_list *timer)
210 {
211         return (!list_empty(&timer->entry));
212 }
213
214 /*
215  * this function should only run on the specified CPU
216  */
217 static void internal_add_vtimer(struct vtimer_list *timer)
218 {
219         unsigned long flags;
220         __u64 done;
221         struct vtimer_list *event;
222         struct vtimer_queue *vt_list;
223
224         vt_list = &per_cpu(virt_cpu_timer, timer->cpu);
225         spin_lock_irqsave(&vt_list->lock, flags);
226
227         if (timer->cpu != smp_processor_id())
228                 printk("internal_add_vtimer: BUG, running on wrong CPU");
229
230         /* if list is empty we only have to set the timer */
231         if (list_empty(&vt_list->list)) {
232                 /* reset the offset, this may happen if the last timer was
233                  * just deleted by mod_virt_timer and the interrupt
234                  * didn't happen until here
235                  */
236                 vt_list->offset = 0;
237                 goto fire;
238         }
239
240         /* save progress */
241         asm volatile ("STPT %0" : "=m" (done));
242
243         /* calculate completed work */
244         done = vt_list->to_expire - done + vt_list->offset;
245         vt_list->offset = 0;
246
247         list_for_each_entry(event, &vt_list->list, entry)
248                 event->expires -= done;
249
250  fire:
251         list_add_sorted(timer, &vt_list->list);
252
253         /* get first element, which is the next vtimer slice */
254         event = list_entry(vt_list->list.next, struct vtimer_list, entry);
255
256         set_vtimer(event->expires);
257         spin_unlock_irqrestore(&vt_list->lock, flags);
258         /* release CPU aquired in prepare_vtimer or mod_virt_timer() */
259         put_cpu();
260 }
261
262 static inline int prepare_vtimer(struct vtimer_list *timer)
263 {
264         if (check_vtimer(timer) || !timer->function) {
265                 printk("add_virt_timer: uninitialized timer\n");
266                 return -EINVAL;
267         }
268
269         if (!timer->expires || timer->expires > VTIMER_MAX_SLICE) {
270                 printk("add_virt_timer: invalid timer expire value!\n");
271                 return -EINVAL;
272         }
273
274         if (vtimer_pending(timer)) {
275                 printk("add_virt_timer: timer pending\n");
276                 return -EBUSY;
277         }
278
279         timer->cpu = get_cpu();
280         return 0;
281 }
282
283 /*
284  * add_virt_timer - add an oneshot virtual CPU timer
285  */
286 void add_virt_timer(void *new)
287 {
288         struct vtimer_list *timer;
289
290         timer = (struct vtimer_list *)new;
291
292         if (prepare_vtimer(timer) < 0)
293                 return;
294
295         timer->interval = 0;
296         internal_add_vtimer(timer);
297 }
298 EXPORT_SYMBOL(add_virt_timer);
299
300 /*
301  * add_virt_timer_int - add an interval virtual CPU timer
302  */
303 void add_virt_timer_periodic(void *new)
304 {
305         struct vtimer_list *timer;
306
307         timer = (struct vtimer_list *)new;
308
309         if (prepare_vtimer(timer) < 0)
310                 return;
311
312         timer->interval = timer->expires;
313         internal_add_vtimer(timer);
314 }
315 EXPORT_SYMBOL(add_virt_timer_periodic);
316
317 /*
318  * If we change a pending timer the function must be called on the CPU
319  * where the timer is running on, e.g. by smp_call_function_on()
320  *
321  * The original mod_timer adds the timer if it is not pending. For compatibility
322  * we do the same. The timer will be added on the current CPU as a oneshot timer.
323  *
324  * returns whether it has modified a pending timer (1) or not (0)
325  */
326 int mod_virt_timer(struct vtimer_list *timer, __u64 expires)
327 {
328         struct vtimer_queue *vt_list;
329         unsigned long flags;
330         int cpu;
331
332         if (check_vtimer(timer) || !timer->function) {
333                 printk("mod_virt_timer: uninitialized timer\n");
334                 return  -EINVAL;
335         }
336
337         if (!expires || expires > VTIMER_MAX_SLICE) {
338                 printk("mod_virt_timer: invalid expire range\n");
339                 return -EINVAL;
340         }
341
342         /*
343          * This is a common optimization triggered by the
344          * networking code - if the timer is re-modified
345          * to be the same thing then just return:
346          */
347         if (timer->expires == expires && vtimer_pending(timer))
348                 return 1;
349
350         cpu = get_cpu();
351         vt_list = &per_cpu(virt_cpu_timer, cpu);
352
353         /* disable interrupts before test if timer is pending */
354         spin_lock_irqsave(&vt_list->lock, flags);
355
356         /* if timer isn't pending add it on the current CPU */
357         if (!vtimer_pending(timer)) {
358                 spin_unlock_irqrestore(&vt_list->lock, flags);
359                 /* we do not activate an interval timer with mod_virt_timer */
360                 timer->interval = 0;
361                 timer->expires = expires;
362                 timer->cpu = cpu;
363                 internal_add_vtimer(timer);
364                 return 0;
365         }
366
367         /* check if we run on the right CPU */
368         if (timer->cpu != cpu) {
369                 printk("mod_virt_timer: running on wrong CPU, check your code\n");
370                 spin_unlock_irqrestore(&vt_list->lock, flags);
371                 put_cpu();
372                 return -EINVAL;
373         }
374
375         list_del_init(&timer->entry);
376         timer->expires = expires;
377
378         /* also change the interval if we have an interval timer */
379         if (timer->interval)
380                 timer->interval = expires;
381
382         /* the timer can't expire anymore so we can release the lock */
383         spin_unlock_irqrestore(&vt_list->lock, flags);
384         internal_add_vtimer(timer);
385         return 1;
386 }
387 EXPORT_SYMBOL(mod_virt_timer);
388
389 /*
390  * delete a virtual timer
391  *
392  * returns whether the deleted timer was pending (1) or not (0)
393  */
394 int del_virt_timer(struct vtimer_list *timer)
395 {
396         unsigned long flags;
397         struct vtimer_queue *vt_list;
398
399         if (check_vtimer(timer)) {
400                 printk("del_virt_timer: timer not initialized\n");
401                 return -EINVAL;
402         }
403
404         /* check if timer is pending */
405         if (!vtimer_pending(timer))
406                 return 0;
407
408         vt_list = &per_cpu(virt_cpu_timer, timer->cpu);
409         spin_lock_irqsave(&vt_list->lock, flags);
410
411         /* we don't interrupt a running timer, just let it expire! */
412         list_del_init(&timer->entry);
413
414         /* last timer removed */
415         if (list_empty(&vt_list->list)) {
416                 vt_list->to_expire = 0;
417                 vt_list->offset = 0;
418         }
419
420         spin_unlock_irqrestore(&vt_list->lock, flags);
421         return 1;
422 }
423 EXPORT_SYMBOL(del_virt_timer);
424
425 /*
426  * Start the virtual CPU timer on the current CPU.
427  */
428 void init_cpu_vtimer(void)
429 {
430         struct vtimer_queue *vt_list;
431         unsigned long cr0;
432         __u64 timer;
433
434         /* kick the virtual timer */
435         timer = VTIMER_MAX_SLICE;
436         asm volatile ("SPT %0" : : "m" (timer));
437         __ctl_store(cr0, 0, 0);
438         cr0 |= 0x400;
439         __ctl_load(cr0, 0, 0);
440
441         vt_list = &per_cpu(virt_cpu_timer, smp_processor_id());
442         INIT_LIST_HEAD(&vt_list->list);
443         spin_lock_init(&vt_list->lock);
444         vt_list->to_expire = 0;
445         vt_list->offset = 0;
446         vt_list->idle = 0;
447
448 }
449
450 static int vtimer_idle_notify(struct notifier_block *self,
451                               unsigned long action, void *hcpu)
452 {
453         switch (action) {
454         case CPU_IDLE:
455                 stop_cpu_timer();
456                 break;
457         case CPU_NOT_IDLE:
458                 start_cpu_timer();
459                 break;
460         }
461         return NOTIFY_OK;
462 }
463
464 static struct notifier_block vtimer_idle_nb = {
465         .notifier_call = vtimer_idle_notify,
466 };
467
468 void __init vtime_init(void)
469 {
470         /* request the cpu timer external interrupt */
471         if (register_early_external_interrupt(0x1005, do_cpu_timer_interrupt,
472                                               &ext_int_info_timer) != 0)
473                 panic("Couldn't request external interrupt 0x1005");
474
475         if (register_idle_notifier(&vtimer_idle_nb))
476                 panic("Couldn't register idle notifier");
477
478         init_cpu_vtimer();
479 }
480