blob: 3cb7135e5c47ad934ca4b50748f6ec4ea319012d [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
Jeff Dike31ccc1f2007-10-16 01:27:24 -07006#include "linux/clockchips.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -07007#include "linux/interrupt.h"
Jeff Dikeba180fd2007-10-16 01:27:00 -07008#include "linux/jiffies.h"
9#include "linux/threads.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070010#include "asm/irq.h"
11#include "asm/param.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070012#include "kern_util.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070013#include "os.h"
14
Linus Torvalds1da177e2005-04-16 15:20:36 -070015/*
16 * Scheduler clock - returns current time in nanosec units.
17 */
18unsigned long long sched_clock(void)
19{
20 return (unsigned long long)jiffies_64 * (1000000000 / HZ);
21}
22
Linus Torvalds1da177e2005-04-16 15:20:36 -070023#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dikeb7ec15b2007-05-06 14:51:51 -070024static unsigned long long prev_nsecs[NR_CPUS];
Jeff Dike490ba172007-02-10 01:44:12 -080025static long long delta[NR_CPUS]; /* Deviation per interval */
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#endif
27
Jeff Dike31ccc1f2007-10-16 01:27:24 -070028void timer_handler(int sig, struct uml_pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070029{
30 unsigned long long ticks = 0;
Jeff Dike31ccc1f2007-10-16 01:27:24 -070031 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -070032#ifdef CONFIG_UML_REAL_TIME_CLOCK
Jeff Dike490ba172007-02-10 01:44:12 -080033 int c = cpu();
Jeff Dikeba180fd2007-10-16 01:27:00 -070034 if (prev_nsecs[c]) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070035 /* We've had 1 tick */
Gennady Sharapovcff65c42006-01-18 17:42:42 -080036 unsigned long long nsecs = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070037
Jeff Dike490ba172007-02-10 01:44:12 -080038 delta[c] += nsecs - prev_nsecs[c];
39 prev_nsecs[c] = nsecs;
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41 /* Protect against the host clock being set backwards */
Jeff Dikeba180fd2007-10-16 01:27:00 -070042 if (delta[c] < 0)
Jeff Dike490ba172007-02-10 01:44:12 -080043 delta[c] = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070044
Jeff Dike490ba172007-02-10 01:44:12 -080045 ticks += (delta[c] * HZ) / BILLION;
46 delta[c] -= (ticks * BILLION) / HZ;
Jeff Dike872aaa62006-07-10 04:45:08 -070047 }
Jeff Dike490ba172007-02-10 01:44:12 -080048 else prev_nsecs[c] = os_nsecs();
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#else
Jeff Dike872aaa62006-07-10 04:45:08 -070050 ticks = 1;
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#endif
Jeff Dike31ccc1f2007-10-16 01:27:24 -070052
53 local_irq_save(flags);
Jeff Dikeba180fd2007-10-16 01:27:00 -070054 while (ticks > 0) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070055 do_IRQ(TIMER_IRQ, regs);
56 ticks--;
57 }
Jeff Dike31ccc1f2007-10-16 01:27:24 -070058 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -070059}
60
Jeff Dike31ccc1f2007-10-16 01:27:24 -070061static void itimer_set_mode(enum clock_event_mode mode,
62 struct clock_event_device *evt)
Gennady Sharapovcff65c42006-01-18 17:42:42 -080063{
Jeff Dike31ccc1f2007-10-16 01:27:24 -070064 switch(mode) {
65 case CLOCK_EVT_MODE_PERIODIC:
66 set_interval();
67 break;
Gennady Sharapovcff65c42006-01-18 17:42:42 -080068
Jeff Dike31ccc1f2007-10-16 01:27:24 -070069 case CLOCK_EVT_MODE_SHUTDOWN:
70 case CLOCK_EVT_MODE_UNUSED:
71 disable_timer();
72 break;
73 case CLOCK_EVT_MODE_ONESHOT:
74 BUG();
75 break;
Gennady Sharapovcff65c42006-01-18 17:42:42 -080076
Jeff Dike31ccc1f2007-10-16 01:27:24 -070077 case CLOCK_EVT_MODE_RESUME:
78 break;
79 }
Gennady Sharapovcff65c42006-01-18 17:42:42 -080080}
81
Jeff Dike31ccc1f2007-10-16 01:27:24 -070082static struct clock_event_device itimer_clockevent = {
83 .name = "itimer",
84 .rating = 250,
85 .cpumask = CPU_MASK_ALL,
86 .features = CLOCK_EVT_FEAT_PERIODIC,
87 .set_mode = itimer_set_mode,
88 .set_next_event = NULL,
89 .shift = 32,
90 .irq = 0,
91};
92
93static irqreturn_t um_timer(int irq, void *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -070094{
Jeff Dike31ccc1f2007-10-16 01:27:24 -070095 (*itimer_clockevent.event_handler)(&itimer_clockevent);
Gennady Sharapovcff65c42006-01-18 17:42:42 -080096
Jeff Dike572e6142006-06-30 01:55:56 -070097 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -070098}
99
Jeff Dike791a6442007-10-16 01:27:25 -0700100static cycle_t itimer_read(void)
101{
102 return os_nsecs();
103}
104
105static struct clocksource itimer_clocksource = {
106 .name = "itimer",
107 .rating = 300,
108 .read = itimer_read,
109 .mask = CLOCKSOURCE_MASK(64),
110 .mult = 1,
111 .shift = 0,
112 .flags = CLOCK_SOURCE_IS_CONTINUOUS,
113};
114
Jeff Dike31ccc1f2007-10-16 01:27:24 -0700115static void __init setup_itimer(void)
Jeff Dikeaceb3432006-07-10 04:45:05 -0700116{
117 int err;
118
119 err = request_irq(TIMER_IRQ, um_timer, IRQF_DISABLED, "timer", NULL);
Jeff Dikeba180fd2007-10-16 01:27:00 -0700120 if (err != 0)
Jeff Dike537ae942006-09-25 23:33:05 -0700121 printk(KERN_ERR "register_timer : request_irq failed - "
Jeff Dikeaceb3432006-07-10 04:45:05 -0700122 "errno = %d\n", -err);
123
Jeff Dike31ccc1f2007-10-16 01:27:24 -0700124 itimer_clockevent.mult = div_sc(HZ, NSEC_PER_SEC, 32);
125 itimer_clockevent.max_delta_ns =
126 clockevent_delta2ns(60 * HZ, &itimer_clockevent);
127 itimer_clockevent.min_delta_ns =
128 clockevent_delta2ns(1, &itimer_clockevent);
Jeff Dike791a6442007-10-16 01:27:25 -0700129 err = clocksource_register(&itimer_clocksource);
130 if (err) {
131 printk(KERN_ERR "clocksource_register returned %d\n", err);
132 return;
133 }
Jeff Dike31ccc1f2007-10-16 01:27:24 -0700134 clockevents_register_device(&itimer_clockevent);
Jeff Dikeaceb3432006-07-10 04:45:05 -0700135}
136
137extern void (*late_time_init)(void);
138
Jeff Dike31ccc1f2007-10-16 01:27:24 -0700139void __init time_init(void)
Jeff Dikeaceb3432006-07-10 04:45:05 -0700140{
141 long long nsecs;
142
Jeff Dike31ccc1f2007-10-16 01:27:24 -0700143 timer_init();
144
Jeff Dikeaceb3432006-07-10 04:45:05 -0700145 nsecs = os_nsecs();
146 set_normalized_timespec(&wall_to_monotonic, -nsecs / BILLION,
147 -nsecs % BILLION);
Jeff Dikeb7ec15b2007-05-06 14:51:51 -0700148 set_normalized_timespec(&xtime, nsecs / BILLION, nsecs % BILLION);
Jeff Dike31ccc1f2007-10-16 01:27:24 -0700149 late_time_init = setup_itimer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150}