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