blob: 7678bcf830bae011f52e62eb4d804ae1f22fabcc [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 -070014/*
15 * Scheduler clock - returns current time in nanosec units.
16 */
17unsigned long long sched_clock(void)
18{
19 return (unsigned long long)jiffies_64 * (1000000000 / HZ);
20}
21
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070023static unsigned long long prev_nsecs[NR_CPUS];
Jeff Dike490ba172007-02-10 01:44:12 -080024static long long delta[NR_CPUS]; /* Deviation per interval */
Linus Torvalds1da177e2005-04-16 15:20:36 -070025#endif
26
Jeff Dike77bf4402007-10-16 01:26:58 -070027void timer_irq(struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070028{
29 unsigned long long ticks = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070030#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dike490ba172007-02-10 01:44:12 -080031 int c = cpu();
Jeff Dikeba180fd2007-10-16 01:27:00 -070032 if (prev_nsecs[c]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070033 /* We've had 1 tick */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080034 unsigned long long nsecs = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070035
Jeff Dike490ba172007-02-10 01:44:12 -080036 delta[c] += nsecs - prev_nsecs[c];
37 prev_nsecs[c] = nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070038
39 /* Protect against the host clock being set backwards */
Jeff Dikeba180fd2007-10-16 01:27:00 -070040 if (delta[c] < 0)
Jeff Dike490ba172007-02-10 01:44:12 -080041 delta[c] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070042
Jeff Dike490ba172007-02-10 01:44:12 -080043 ticks += (delta[c] * HZ) / BILLION;
44 delta[c] -= (ticks * BILLION) / HZ;
Jeff Dike872aaa62006-07-10 04:45:08 -070045 }
Jeff Dike490ba172007-02-10 01:44:12 -080046 else prev_nsecs[c] = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#else
Jeff Dike872aaa62006-07-10 04:45:08 -070048 ticks = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#endif
Jeff Dikeba180fd2007-10-16 01:27:00 -070050 while (ticks > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070051 do_IRQ(TIMER_IRQ, regs);
52 ticks--;
53 }
54}
55
Jeff Dike490ba172007-02-10 01:44:12 -080056/* Protects local_offset */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080057static DEFINE_SPINLOCK(timer_spinlock);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080058static unsigned long long local_offset = 0;
59
60static inline unsigned long long get_time(void)
61{
62 unsigned long long nsecs;
63 unsigned long flags;
64
65 spin_lock_irqsave(&timer_spinlock, flags);
66 nsecs = os_nsecs();
67 nsecs += local_offset;
68 spin_unlock_irqrestore(&timer_spinlock, flags);
69
70 return nsecs;
71}
72
Al Viro7bea96f2006-10-08 22:49:34 +010073irqreturn_t um_timer(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070074{
Gennady Sharapovcff65c42006-01-18 17:42:42 -080075 unsigned long long nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070076 unsigned long flags;
77
Jeff Dike572e6142006-06-30 01:55:56 -070078 write_seqlock_irqsave(&xtime_lock, flags);
79
Atsushi Nemoto3171a032006-09-29 02:00:32 -070080 do_timer(1);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080081
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070082#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikec1b40982006-09-27 01:50:42 -070083 nsecs = get_time();
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070084#else
85 nsecs = (unsigned long long) xtime.tv_sec * BILLION + xtime.tv_nsec +
86 BILLION / HZ;
87#endif
Gennady Sharapovcff65c42006-01-18 17:42:42 -080088 xtime.tv_sec = nsecs / NSEC_PER_SEC;
89 xtime.tv_nsec = nsecs - xtime.tv_sec * NSEC_PER_SEC;
Jeff Dike572e6142006-06-30 01:55:56 -070090
Linus Torvalds1da177e2005-04-16 15:20:36 -070091 write_sequnlock_irqrestore(&xtime_lock, flags);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080092
Jeff Dike572e6142006-06-30 01:55:56 -070093 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070094}
95
Jeff Dikeaceb34342006-07-10 04:45:05 -070096static void register_timer(void)
97{
98 int err;
99
Jeff Dike78a26e22007-10-16 01:27:23 -0700100 timer_init();
101
Jeff Dikeaceb34342006-07-10 04:45:05 -0700102 err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700103 if (err != 0)
Jeff Dike537ae942006-09-25 23:33:05 -0700104 printk(KERN_ERR "register_timer : request_irq failed - "
Jeff Dikeaceb34342006-07-10 04:45:05 -0700105 "errno = %d\n", -err);
106
Jeff Dikea2f018b2007-10-16 01:27:22 -0700107 err = set_interval();
Jeff Dikeba180fd2007-10-16 01:27:00 -0700108 if (err != 0)
Jeff Dike537ae942006-09-25 23:33:05 -0700109 printk(KERN_ERR "register_timer : set_interval failed - "
110 "errno = %d\n", -err);
Jeff Dikeaceb34342006-07-10 04:45:05 -0700111}
112
113extern void (*late_time_init)(void);
114
115void time_init(void)
116{
117 long long nsecs;
118
119 nsecs = os_nsecs();
120 set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
121 -nsecs % BILLION);
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700122 set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
Jeff Dikeaceb34342006-07-10 04:45:05 -0700123 late_time_init = register_timer;
124}
125
Jeff Dike77bf4402007-10-16 01:26:58 -0700126void timer_handler(int sig, struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127{
Jeff Dikeba180fd2007-10-16 01:27:00 -0700128 if (current_thread->cpu == 0)
Jeff Dikec83e4482007-05-08 00:23:22 -0700129 timer_irq(regs);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130 local_irq_disable();
Jeff Dike7e1f49d2005-07-28 21:16:09 -0700131 irq_enter();
Jeff Dike77bf4402007-10-16 01:26:58 -0700132 update_process_times(regs->is_user);
Jeff Dike7e1f49d2005-07-28 21:16:09 -0700133 irq_exit();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134 local_irq_enable();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135}