blob: 90e24e2dbeaa230c6b9b9491cd4a8559631e9197 [file] [log] [blame]
Gennady Sharapovcff65c42006-01-18 17:42:42 -08001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * 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"
Gennady Sharapovcff65c42006-01-18 17:42:42 -080016#include "linux/hrtimer.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include "asm/irq.h"
18#include "asm/param.h"
19#include "asm/current.h"
20#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070021#include "os.h"
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023int hz(void)
24{
25 return(HZ);
26}
27
28/*
29 * Scheduler clock - returns current time in nanosec units.
30 */
31unsigned long long sched_clock(void)
32{
33 return (unsigned long long)jiffies_64 * (1000000000 / HZ);
34}
35
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070037static unsigned long long prev_nsecs[NR_CPUS];
Jeff Dike490ba172007-02-10 01:44:12 -080038static long long delta[NR_CPUS]; /* Deviation per interval */
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#endif
40
Jeff Dike77bf4402007-10-16 01:26:58 -070041void timer_irq(struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070042{
43 unsigned long long ticks = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dike490ba172007-02-10 01:44:12 -080045 int c = cpu();
46 if(prev_nsecs[c]){
Linus Torvalds1da177e2005-04-16 15:20:36 -070047 /* We've had 1 tick */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080048 unsigned long long nsecs = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070049
Jeff Dike490ba172007-02-10 01:44:12 -080050 delta[c] += nsecs - prev_nsecs[c];
51 prev_nsecs[c] = nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070052
53 /* Protect against the host clock being set backwards */
Jeff Dike490ba172007-02-10 01:44:12 -080054 if(delta[c] < 0)
55 delta[c] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
Jeff Dike490ba172007-02-10 01:44:12 -080057 ticks += (delta[c] * HZ) / BILLION;
58 delta[c] -= (ticks * BILLION) / HZ;
Jeff Dike872aaa62006-07-10 04:45:08 -070059 }
Jeff Dike490ba172007-02-10 01:44:12 -080060 else prev_nsecs[c] = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070061#else
Jeff Dike872aaa62006-07-10 04:45:08 -070062 ticks = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070064 while(ticks > 0){
65 do_IRQ(TIMER_IRQ, regs);
66 ticks--;
67 }
68}
69
Jeff Dike490ba172007-02-10 01:44:12 -080070/* Protects local_offset */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080071static DEFINE_SPINLOCK(timer_spinlock);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080072static unsigned long long local_offset = 0;
73
74static 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
Al Viro7bea96f2006-10-08 22:49:34 +010087irqreturn_t um_timer(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
Gennady Sharapovcff65c42006-01-18 17:42:42 -080089 unsigned long long nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 unsigned long flags;
91
Jeff Dike572e6142006-06-30 01:55:56 -070092 write_seqlock_irqsave(&xtime_lock, flags);
93
Atsushi Nemoto3171a032006-09-29 02:00:32 -070094 do_timer(1);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080095
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070096#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikec1b40982006-09-27 01:50:42 -070097 nsecs = get_time();
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070098#else
99 nsecs = (unsigned long long) xtime.tv_sec * BILLION + xtime.tv_nsec +
100 BILLION / HZ;
101#endif
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800102 xtime.tv_sec = nsecs / NSEC_PER_SEC;
103 xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
Jeff Dike572e6142006-06-30 01:55:56 -0700104
Linus Torvalds1da177e2005-04-16 15:20:36 -0700105 write_sequnlock_irqrestore(&xtime_lock, flags);
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800106
Jeff Dike572e6142006-06-30 01:55:56 -0700107 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108}
109
Jeff Dikeaceb3432006-07-10 04:45:05 -0700110static 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)
Jeff Dike537ae942006-09-25 23:33:05 -0700116 printk(KERN_ERR "register_timer : request_irq failed - "
Jeff Dikeaceb3432006-07-10 04:45:05 -0700117 "errno = %d\n", -err);
118
Jeff Dike537ae942006-09-25 23:33:05 -0700119 err = set_interval(1);
120 if(err != 0)
121 printk(KERN_ERR "register_timer : set_interval failed - "
122 "errno = %d\n", -err);
Jeff Dikeaceb3432006-07-10 04:45:05 -0700123}
124
125extern void (*late_time_init)(void);
126
127void 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);
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700134 set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
Jeff Dikeaceb3432006-07-10 04:45:05 -0700135 late_time_init = register_timer;
136}
137
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800138void do_gettimeofday(struct timeval *tv)
139{
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700140#ifdef CONFIG_UML_REAL_TIME_CLOCK
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800141 unsigned long long nsecs = get_time();
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700142#else
143 unsigned long long nsecs = (unsigned long long) xtime.tv_sec * BILLION +
144 xtime.tv_nsec;
145#endif
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800146 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
157static 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();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700168}
169
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800170int do_settimeofday(struct timespec *tv)
171{
172 set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec);
173
Linus Torvalds1da177e2005-04-16 15:20:36 -0700174 return 0;
175}
176
Jeff Dike77bf4402007-10-16 01:26:58 -0700177void timer_handler(int sig, struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178{
Jeff Dikec83e4482007-05-08 00:23:22 -0700179 if(current_thread->cpu == 0)
180 timer_irq(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181 local_irq_disable();
Jeff Dike7e1f49d2005-07-28 21:16:09 -0700182 irq_enter();
Jeff Dike77bf4402007-10-16 01:26:58 -0700183 update_process_times(regs->is_user);
Jeff Dike7e1f49d2005-07-28 21:16:09 -0700184 irq_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 local_irq_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186}