uml: GENERIC_CLOCKEVENTS support
[linux-2.6.git] / arch / um / kernel / time.c
1 /*
2  * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
3  * Licensed under the GPL
4  */
5
6 #include "linux/clockchips.h"
7 #include "linux/interrupt.h"
8 #include "linux/jiffies.h"
9 #include "linux/threads.h"
10 #include "asm/irq.h"
11 #include "asm/param.h"
12 #include "kern_util.h"
13 #include "os.h"
14
15 /*
16  * Scheduler clock - returns current time in nanosec units.
17  */
18 unsigned long long sched_clock(void)
19 {
20         return (unsigned long long)jiffies_64 * (1000000000 / HZ);
21 }
22
23 #ifdef CONFIG_UML_REAL_TIME_CLOCK
24 static unsigned long long prev_nsecs[NR_CPUS];
25 static long long delta[NR_CPUS];                /* Deviation per interval */
26 #endif
27
28 void timer_handler(int sig, struct uml_pt_regs *regs)
29 {
30         unsigned long long ticks = 0;
31         unsigned long flags;
32 #ifdef CONFIG_UML_REAL_TIME_CLOCK
33         int c = cpu();
34         if (prev_nsecs[c]) {
35                 /* We've had 1 tick */
36                 unsigned long long nsecs = os_nsecs();
37
38                 delta[c] += nsecs - prev_nsecs[c];
39                 prev_nsecs[c] = nsecs;
40
41                 /* Protect against the host clock being set backwards */
42                 if (delta[c] < 0)
43                         delta[c] = 0;
44
45                 ticks += (delta[c] * HZ) / BILLION;
46                 delta[c] -= (ticks * BILLION) / HZ;
47         }
48         else prev_nsecs[c] = os_nsecs();
49 #else
50         ticks = 1;
51 #endif
52
53         local_irq_save(flags);
54         while (ticks > 0) {
55                 do_IRQ(TIMER_IRQ, regs);
56                 ticks--;
57         }
58         local_irq_restore(flags);
59 }
60
61 static void itimer_set_mode(enum clock_event_mode mode,
62                             struct clock_event_device *evt)
63 {
64         switch(mode) {
65         case CLOCK_EVT_MODE_PERIODIC:
66                 set_interval();
67                 break;
68
69         case CLOCK_EVT_MODE_SHUTDOWN:
70         case CLOCK_EVT_MODE_UNUSED:
71                 disable_timer();
72                 break;
73         case CLOCK_EVT_MODE_ONESHOT:
74                 BUG();
75                 break;
76
77         case CLOCK_EVT_MODE_RESUME:
78                 break;
79         }
80 }
81
82 static struct clock_event_device itimer_clockevent = {
83         .name           = "itimer",
84         .rating         = 250,
85         .cpumask        = CPU_MASK_ALL,
86         .features       = CLOCK_EVT_FEAT_PERIODIC,
87         .set_mode       = itimer_set_mode,
88         .set_next_event = NULL,
89         .shift          = 32,
90         .irq            = 0,
91 };
92
93 static irqreturn_t um_timer(int irq, void *dev)
94 {
95         (*itimer_clockevent.event_handler)(&itimer_clockevent);
96
97         return IRQ_HANDLED;
98 }
99
100 static void __init setup_itimer(void)
101 {
102         int err;
103
104         err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
105         if (err != 0)
106                 printk(KERN_ERR "register_timer : request_irq failed - "
107                        "errno = %d\n", -err);
108
109         itimer_clockevent.mult = div_sc(HZ, NSEC_PER_SEC, 32);
110         itimer_clockevent.max_delta_ns =
111                 clockevent_delta2ns(60 * HZ, &itimer_clockevent);
112         itimer_clockevent.min_delta_ns =
113                 clockevent_delta2ns(1, &itimer_clockevent);
114         clockevents_register_device(&itimer_clockevent);
115 }
116
117 extern void (*late_time_init)(void);
118
119 void __init time_init(void)
120 {
121         long long nsecs;
122
123         timer_init();
124
125         nsecs = os_nsecs();
126         set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
127                                 -nsecs % BILLION);
128         set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
129         late_time_init = setup_itimer;
130 }