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