xref: /openbmc/linux/arch/um/kernel/time.c (revision e868d61272caa648214046a096e5a6bfc068dc8c)
1 /*
2  * Copyright (C) 2000 Jeff Dike (jdike@karaya.com)
3  * Licensed under the GPL
4  */
5 
6 #include "linux/kernel.h"
7 #include "linux/module.h"
8 #include "linux/unistd.h"
9 #include "linux/stddef.h"
10 #include "linux/spinlock.h"
11 #include "linux/time.h"
12 #include "linux/sched.h"
13 #include "linux/interrupt.h"
14 #include "linux/init.h"
15 #include "linux/delay.h"
16 #include "linux/hrtimer.h"
17 #include "asm/irq.h"
18 #include "asm/param.h"
19 #include "asm/current.h"
20 #include "kern_util.h"
21 #include "mode.h"
22 #include "os.h"
23 
24 int hz(void)
25 {
26 	return(HZ);
27 }
28 
29 /*
30  * Scheduler clock - returns current time in nanosec units.
31  */
32 unsigned long long sched_clock(void)
33 {
34 	return (unsigned long long)jiffies_64 * (1000000000 / HZ);
35 }
36 
37 #ifdef CONFIG_UML_REAL_TIME_CLOCK
38 static unsigned long long prev_nsecs[NR_CPUS];
39 static long long delta[NR_CPUS];		/* Deviation per interval */
40 #endif
41 
42 void timer_irq(union uml_pt_regs *regs)
43 {
44 	unsigned long long ticks = 0;
45 #ifdef CONFIG_UML_REAL_TIME_CLOCK
46 	int c = cpu();
47 	if(prev_nsecs[c]){
48 		/* We've had 1 tick */
49 		unsigned long long nsecs = os_nsecs();
50 
51 		delta[c] += nsecs - prev_nsecs[c];
52 		prev_nsecs[c] = nsecs;
53 
54 		/* Protect against the host clock being set backwards */
55 		if(delta[c] < 0)
56 			delta[c] = 0;
57 
58 		ticks += (delta[c] * HZ) / BILLION;
59 		delta[c] -= (ticks * BILLION) / HZ;
60 	}
61 	else prev_nsecs[c] = os_nsecs();
62 #else
63 	ticks = 1;
64 #endif
65 	while(ticks > 0){
66 		do_IRQ(TIMER_IRQ, regs);
67 		ticks--;
68 	}
69 }
70 
71 /* Protects local_offset */
72 static DEFINE_SPINLOCK(timer_spinlock);
73 static unsigned long long local_offset = 0;
74 
75 static inline unsigned long long get_time(void)
76 {
77 	unsigned long long nsecs;
78 	unsigned long flags;
79 
80 	spin_lock_irqsave(&timer_spinlock, flags);
81 	nsecs = os_nsecs();
82 	nsecs += local_offset;
83 	spin_unlock_irqrestore(&timer_spinlock, flags);
84 
85 	return nsecs;
86 }
87 
88 irqreturn_t um_timer(int irq, void *dev)
89 {
90 	unsigned long long nsecs;
91 	unsigned long flags;
92 
93 	write_seqlock_irqsave(&xtime_lock, flags);
94 
95 	do_timer(1);
96 
97 #ifdef CONFIG_UML_REAL_TIME_CLOCK
98 	nsecs = get_time();
99 #else
100 	nsecs = (unsigned long long) xtime.tv_sec * BILLION + xtime.tv_nsec +
101 		BILLION / HZ;
102 #endif
103 	xtime.tv_sec = nsecs / NSEC_PER_SEC;
104 	xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
105 
106 	write_sequnlock_irqrestore(&xtime_lock, flags);
107 
108 	return IRQ_HANDLED;
109 }
110 
111 static void register_timer(void)
112 {
113 	int err;
114 
115 	err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
116 	if(err != 0)
117 		printk(KERN_ERR "register_timer : request_irq failed - "
118 		       "errno = %d\n", -err);
119 
120 	err = set_interval(1);
121 	if(err != 0)
122 		printk(KERN_ERR "register_timer : set_interval failed - "
123 		       "errno = %d\n", -err);
124 }
125 
126 extern void (*late_time_init)(void);
127 
128 void time_init(void)
129 {
130 	long long nsecs;
131 
132 	nsecs = os_nsecs();
133 	set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
134 				-nsecs % BILLION);
135 	set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
136 	late_time_init = register_timer;
137 }
138 
139 void do_gettimeofday(struct timeval *tv)
140 {
141 #ifdef CONFIG_UML_REAL_TIME_CLOCK
142 	unsigned long long nsecs = get_time();
143 #else
144 	unsigned long long nsecs = (unsigned long long) xtime.tv_sec * BILLION +
145 		xtime.tv_nsec;
146 #endif
147 	tv->tv_sec = nsecs / NSEC_PER_SEC;
148 	/* Careful about calculations here - this was originally done as
149 	 * (nsecs - tv->tv_sec * NSEC_PER_SEC) / NSEC_PER_USEC
150 	 * which gave bogus (> 1000000) values.  Dunno why, suspect gcc
151 	 * (4.0.0) miscompiled it, or there's a subtle 64/32-bit conversion
152 	 * problem that I missed.
153 	 */
154 	nsecs -= tv->tv_sec * NSEC_PER_SEC;
155 	tv->tv_usec = (unsigned long) nsecs / NSEC_PER_USEC;
156 }
157 
158 static inline void set_time(unsigned long long nsecs)
159 {
160 	unsigned long long now;
161 	unsigned long flags;
162 
163 	spin_lock_irqsave(&timer_spinlock, flags);
164 	now = os_nsecs();
165 	local_offset = nsecs - now;
166 	spin_unlock_irqrestore(&timer_spinlock, flags);
167 
168 	clock_was_set();
169 }
170 
171 int do_settimeofday(struct timespec *tv)
172 {
173 	set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec);
174 
175 	return 0;
176 }
177 
178 void timer_handler(int sig, union uml_pt_regs *regs)
179 {
180 	if(current_thread->cpu == 0)
181 		timer_irq(regs);
182 	local_irq_disable();
183 	irq_enter();
184 	update_process_times(CHOOSE_MODE(
185 	                     (UPT_SC(regs) && user_context(UPT_SP(regs))),
186 			     (regs)->skas.is_user));
187 	irq_exit();
188 	local_irq_enable();
189 }
190