blob: 4fc8c2586b70b91a02bec5562eb4cee8ef904b61 [file] [log] [blame]
Gennady Sharapovcff65c42006-01-18 17:42:42 -08001/*
Jeff Dikeba180fd2007-10-16 01:27:00 -07002 * Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 * Licensed under the GPL
4 */
5
Linus Torvalds1da177e2005-04-16 15:20:36 -07006#include "linux/interrupt.h"
Jeff Dikeba180fd2007-10-16 01:27:00 -07007#include "linux/jiffies.h"
8#include "linux/threads.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -07009#include "asm/irq.h"
10#include "asm/param.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070011#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include "os.h"
13
Linus Torvalds1da177e2005-04-16 15:20:36 -070014int hz(void)
15{
Jeff Dikeba180fd2007-10-16 01:27:00 -070016 return HZ;
Linus Torvalds1da177e2005-04-16 15:20:36 -070017}
18
19/*
20 * Scheduler clock - returns current time in nanosec units.
21 */
22unsigned long long sched_clock(void)
23{
24 return (unsigned long long)jiffies_64 * (1000000000 / HZ);
25}
26
Linus Torvalds1da177e2005-04-16 15:20:36 -070027#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070028static unsigned long long prev_nsecs[NR_CPUS];
Jeff Dike490ba172007-02-10 01:44:12 -080029static long long delta[NR_CPUS]; /* Deviation per interval */
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#endif
31
Jeff Dike77bf4402007-10-16 01:26:58 -070032void timer_irq(struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070033{
34 unsigned long long ticks = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dike490ba172007-02-10 01:44:12 -080036 int c = cpu();
Jeff Dikeba180fd2007-10-16 01:27:00 -070037 if (prev_nsecs[c]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070038 /* We've had 1 tick */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080039 unsigned long long nsecs = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
Jeff Dike490ba172007-02-10 01:44:12 -080041 delta[c] += nsecs - prev_nsecs[c];
42 prev_nsecs[c] = nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44 /* Protect against the host clock being set backwards */
Jeff Dikeba180fd2007-10-16 01:27:00 -070045 if (delta[c] < 0)
Jeff Dike490ba172007-02-10 01:44:12 -080046 delta[c] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070047
Jeff Dike490ba172007-02-10 01:44:12 -080048 ticks += (delta[c] * HZ) / BILLION;
49 delta[c] -= (ticks * BILLION) / HZ;
Jeff Dike872aaa62006-07-10 04:45:08 -070050 }
Jeff Dike490ba172007-02-10 01:44:12 -080051 else prev_nsecs[c] = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070052#else
Jeff Dike872aaa62006-07-10 04:45:08 -070053 ticks = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#endif
Jeff Dikeba180fd2007-10-16 01:27:00 -070055 while (ticks > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070056 do_IRQ(TIMER_IRQ, regs);
57 ticks--;
58 }
59}
60
Jeff Dike490ba172007-02-10 01:44:12 -080061/* Protects local_offset */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080062static DEFINE_SPINLOCK(timer_spinlock);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080063static unsigned long long local_offset = 0;
64
65static 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
Al Viro7bea96f2006-10-08 22:49:34 +010078irqreturn_t um_timer(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079{
Gennady Sharapovcff65c42006-01-18 17:42:42 -080080 unsigned long long nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 unsigned long flags;
82
Jeff Dike572e6142006-06-30 01:55:56 -070083 write_seqlock_irqsave(&xtime_lock, flags);
84
Atsushi Nemoto3171a032006-09-29 02:00:32 -070085 do_timer(1);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080086
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070087#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikec1b40982006-09-27 01:50:42 -070088 nsecs = get_time();
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070089#else
90 nsecs = (unsigned long long) xtime.tv_sec * BILLION + xtime.tv_nsec +
91 BILLION / HZ;
92#endif
Gennady Sharapovcff65c42006-01-18 17:42:42 -080093 xtime.tv_sec = nsecs / NSEC_PER_SEC;
94 xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
Jeff Dike572e6142006-06-30 01:55:56 -070095
Linus Torvalds1da177e2005-04-16 15:20:36 -070096 write_sequnlock_irqrestore(&xtime_lock, flags);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080097
Jeff Dike572e6142006-06-30 01:55:56 -070098 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070099}
100
Jeff Dikeaceb3432006-07-10 04:45:05 -0700101static void register_timer(void)
102{
103 int err;
104
105 err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700106 if (err != 0)
Jeff Dike537ae942006-09-25 23:33:05 -0700107 printk(KERN_ERR "register_timer : request_irq failed - "
Jeff Dikeaceb3432006-07-10 04:45:05 -0700108 "errno = %d\n", -err);
109
Jeff Dike537ae942006-09-25 23:33:05 -0700110 err = set_interval(1);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700111 if (err != 0)
Jeff Dike537ae942006-09-25 23:33:05 -0700112 printk(KERN_ERR "register_timer : set_interval failed - "
113 "errno = %d\n", -err);
Jeff Dikeaceb3432006-07-10 04:45:05 -0700114}
115
116extern void (*late_time_init)(void);
117
118void 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);
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700125 set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
Jeff Dikeaceb3432006-07-10 04:45:05 -0700126 late_time_init = register_timer;
127}
128
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800129void do_gettimeofday(struct timeval *tv)
130{
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700131#ifdef CONFIG_UML_REAL_TIME_CLOCK
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800132 unsigned long long nsecs = get_time();
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700133#else
134 unsigned long long nsecs = (unsigned long long) xtime.tv_sec * BILLION +
135 xtime.tv_nsec;
136#endif
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800137 tv->tv_sec = nsecs / NSEC_PER_SEC;
Jeff Dikeba180fd2007-10-16 01:27:00 -0700138 /*
139 * Careful about calculations here - this was originally done as
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800140 * (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
149static 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();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160}
161
Gennady Sharapovcff65c42006-01-18 17:42:42 -0800162int do_settimeofday(struct timespec *tv)
163{
164 set_time((unsigned long long) tv->tv_sec * NSEC_PER_SEC + tv->tv_nsec);
165
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 return 0;
167}
168
Jeff Dike77bf4402007-10-16 01:26:58 -0700169void timer_handler(int sig, struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700170{
Jeff Dikeba180fd2007-10-16 01:27:00 -0700171 if (current_thread->cpu == 0)
Jeff Dikec83e4482007-05-08 00:23:22 -0700172 timer_irq(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700173 local_irq_disable();
Jeff Dike7e1f49d2005-07-28 21:16:09 -0700174 irq_enter();
Jeff Dike77bf4402007-10-16 01:26:58 -0700175 update_process_times(regs->is_user);
Jeff Dike7e1f49d2005-07-28 21:16:09 -0700176 irq_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700177 local_irq_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700178}