diff options
Diffstat (limited to 'arch/x86/kernel/time_32.c')
-rw-r--r-- | arch/x86/kernel/time_32.c | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/arch/x86/kernel/time_32.c b/arch/x86/kernel/time_32.c new file mode 100644 index 000000000000..19a6c678d02e --- /dev/null +++ b/arch/x86/kernel/time_32.c | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * linux/arch/i386/kernel/time.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | ||
5 | * | ||
6 | * This file contains the PC-specific time handling details: | ||
7 | * reading the RTC at bootup, etc.. | ||
8 | * 1994-07-02 Alan Modra | ||
9 | * fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime | ||
10 | * 1995-03-26 Markus Kuhn | ||
11 | * fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887 | ||
12 | * precision CMOS clock update | ||
13 | * 1996-05-03 Ingo Molnar | ||
14 | * fixed time warps in do_[slow|fast]_gettimeoffset() | ||
15 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 | ||
16 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | ||
17 | * 1998-09-05 (Various) | ||
18 | * More robust do_fast_gettimeoffset() algorithm implemented | ||
19 | * (works with APM, Cyrix 6x86MX and Centaur C6), | ||
20 | * monotonic gettimeofday() with fast_get_timeoffset(), | ||
21 | * drift-proof precision TSC calibration on boot | ||
22 | * (C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D. | ||
23 | * Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>; | ||
24 | * ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>). | ||
25 | * 1998-12-16 Andrea Arcangeli | ||
26 | * Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy | ||
27 | * because was not accounting lost_ticks. | ||
28 | * 1998-12-24 Copyright (C) 1998 Andrea Arcangeli | ||
29 | * Fixed a xtime SMP race (we need the xtime_lock rw spinlock to | ||
30 | * serialize accesses to xtime/lost_ticks). | ||
31 | */ | ||
32 | |||
33 | #include <linux/errno.h> | ||
34 | #include <linux/sched.h> | ||
35 | #include <linux/kernel.h> | ||
36 | #include <linux/param.h> | ||
37 | #include <linux/string.h> | ||
38 | #include <linux/mm.h> | ||
39 | #include <linux/interrupt.h> | ||
40 | #include <linux/time.h> | ||
41 | #include <linux/delay.h> | ||
42 | #include <linux/init.h> | ||
43 | #include <linux/smp.h> | ||
44 | #include <linux/module.h> | ||
45 | #include <linux/sysdev.h> | ||
46 | #include <linux/bcd.h> | ||
47 | #include <linux/efi.h> | ||
48 | #include <linux/mca.h> | ||
49 | |||
50 | #include <asm/io.h> | ||
51 | #include <asm/smp.h> | ||
52 | #include <asm/irq.h> | ||
53 | #include <asm/msr.h> | ||
54 | #include <asm/delay.h> | ||
55 | #include <asm/mpspec.h> | ||
56 | #include <asm/uaccess.h> | ||
57 | #include <asm/processor.h> | ||
58 | #include <asm/timer.h> | ||
59 | #include <asm/time.h> | ||
60 | |||
61 | #include "mach_time.h" | ||
62 | |||
63 | #include <linux/timex.h> | ||
64 | |||
65 | #include <asm/hpet.h> | ||
66 | |||
67 | #include <asm/arch_hooks.h> | ||
68 | |||
69 | #include "io_ports.h" | ||
70 | |||
71 | #include <asm/i8259.h> | ||
72 | |||
73 | #include "do_timer.h" | ||
74 | |||
75 | unsigned int cpu_khz; /* Detected as we calibrate the TSC */ | ||
76 | EXPORT_SYMBOL(cpu_khz); | ||
77 | |||
78 | DEFINE_SPINLOCK(rtc_lock); | ||
79 | EXPORT_SYMBOL(rtc_lock); | ||
80 | |||
81 | /* | ||
82 | * This is a special lock that is owned by the CPU and holds the index | ||
83 | * register we are working with. It is required for NMI access to the | ||
84 | * CMOS/RTC registers. See include/asm-i386/mc146818rtc.h for details. | ||
85 | */ | ||
86 | volatile unsigned long cmos_lock = 0; | ||
87 | EXPORT_SYMBOL(cmos_lock); | ||
88 | |||
89 | /* Routines for accessing the CMOS RAM/RTC. */ | ||
90 | unsigned char rtc_cmos_read(unsigned char addr) | ||
91 | { | ||
92 | unsigned char val; | ||
93 | lock_cmos_prefix(addr); | ||
94 | outb_p(addr, RTC_PORT(0)); | ||
95 | val = inb_p(RTC_PORT(1)); | ||
96 | lock_cmos_suffix(addr); | ||
97 | return val; | ||
98 | } | ||
99 | EXPORT_SYMBOL(rtc_cmos_read); | ||
100 | |||
101 | void rtc_cmos_write(unsigned char val, unsigned char addr) | ||
102 | { | ||
103 | lock_cmos_prefix(addr); | ||
104 | outb_p(addr, RTC_PORT(0)); | ||
105 | outb_p(val, RTC_PORT(1)); | ||
106 | lock_cmos_suffix(addr); | ||
107 | } | ||
108 | EXPORT_SYMBOL(rtc_cmos_write); | ||
109 | |||
110 | static int set_rtc_mmss(unsigned long nowtime) | ||
111 | { | ||
112 | int retval; | ||
113 | unsigned long flags; | ||
114 | |||
115 | /* gets recalled with irq locally disabled */ | ||
116 | /* XXX - does irqsave resolve this? -johnstul */ | ||
117 | spin_lock_irqsave(&rtc_lock, flags); | ||
118 | retval = set_wallclock(nowtime); | ||
119 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
120 | |||
121 | return retval; | ||
122 | } | ||
123 | |||
124 | |||
125 | int timer_ack; | ||
126 | |||
127 | unsigned long profile_pc(struct pt_regs *regs) | ||
128 | { | ||
129 | unsigned long pc = instruction_pointer(regs); | ||
130 | |||
131 | #ifdef CONFIG_SMP | ||
132 | if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->xcs) && | ||
133 | in_lock_functions(pc)) { | ||
134 | #ifdef CONFIG_FRAME_POINTER | ||
135 | return *(unsigned long *)(regs->ebp + 4); | ||
136 | #else | ||
137 | unsigned long *sp = (unsigned long *)®s->esp; | ||
138 | |||
139 | /* Return address is either directly at stack pointer | ||
140 | or above a saved eflags. Eflags has bits 22-31 zero, | ||
141 | kernel addresses don't. */ | ||
142 | if (sp[0] >> 22) | ||
143 | return sp[0]; | ||
144 | if (sp[1] >> 22) | ||
145 | return sp[1]; | ||
146 | #endif | ||
147 | } | ||
148 | #endif | ||
149 | return pc; | ||
150 | } | ||
151 | EXPORT_SYMBOL(profile_pc); | ||
152 | |||
153 | /* | ||
154 | * This is the same as the above, except we _also_ save the current | ||
155 | * Time Stamp Counter value at the time of the timer interrupt, so that | ||
156 | * we later on can estimate the time of day more exactly. | ||
157 | */ | ||
158 | irqreturn_t timer_interrupt(int irq, void *dev_id) | ||
159 | { | ||
160 | #ifdef CONFIG_X86_IO_APIC | ||
161 | if (timer_ack) { | ||
162 | /* | ||
163 | * Subtle, when I/O APICs are used we have to ack timer IRQ | ||
164 | * manually to reset the IRR bit for do_slow_gettimeoffset(). | ||
165 | * This will also deassert NMI lines for the watchdog if run | ||
166 | * on an 82489DX-based system. | ||
167 | */ | ||
168 | spin_lock(&i8259A_lock); | ||
169 | outb(0x0c, PIC_MASTER_OCW3); | ||
170 | /* Ack the IRQ; AEOI will end it automatically. */ | ||
171 | inb(PIC_MASTER_POLL); | ||
172 | spin_unlock(&i8259A_lock); | ||
173 | } | ||
174 | #endif | ||
175 | |||
176 | do_timer_interrupt_hook(); | ||
177 | |||
178 | if (MCA_bus) { | ||
179 | /* The PS/2 uses level-triggered interrupts. You can't | ||
180 | turn them off, nor would you want to (any attempt to | ||
181 | enable edge-triggered interrupts usually gets intercepted by a | ||
182 | special hardware circuit). Hence we have to acknowledge | ||
183 | the timer interrupt. Through some incredibly stupid | ||
184 | design idea, the reset for IRQ 0 is done by setting the | ||
185 | high bit of the PPI port B (0x61). Note that some PS/2s, | ||
186 | notably the 55SX, work fine if this is removed. */ | ||
187 | |||
188 | u8 irq_v = inb_p( 0x61 ); /* read the current state */ | ||
189 | outb_p( irq_v|0x80, 0x61 ); /* reset the IRQ */ | ||
190 | } | ||
191 | |||
192 | return IRQ_HANDLED; | ||
193 | } | ||
194 | |||
195 | /* not static: needed by APM */ | ||
196 | unsigned long read_persistent_clock(void) | ||
197 | { | ||
198 | unsigned long retval; | ||
199 | unsigned long flags; | ||
200 | |||
201 | spin_lock_irqsave(&rtc_lock, flags); | ||
202 | |||
203 | retval = get_wallclock(); | ||
204 | |||
205 | spin_unlock_irqrestore(&rtc_lock, flags); | ||
206 | |||
207 | return retval; | ||
208 | } | ||
209 | |||
210 | int update_persistent_clock(struct timespec now) | ||
211 | { | ||
212 | return set_rtc_mmss(now.tv_sec); | ||
213 | } | ||
214 | |||
215 | extern void (*late_time_init)(void); | ||
216 | /* Duplicate of time_init() below, with hpet_enable part added */ | ||
217 | void __init hpet_time_init(void) | ||
218 | { | ||
219 | if (!hpet_enable()) | ||
220 | setup_pit_timer(); | ||
221 | time_init_hook(); | ||
222 | } | ||
223 | |||
224 | /* | ||
225 | * This is called directly from init code; we must delay timer setup in the | ||
226 | * HPET case as we can't make the decision to turn on HPET this early in the | ||
227 | * boot process. | ||
228 | * | ||
229 | * The chosen time_init function will usually be hpet_time_init, above, but | ||
230 | * in the case of virtual hardware, an alternative function may be substituted. | ||
231 | */ | ||
232 | void __init time_init(void) | ||
233 | { | ||
234 | tsc_init(); | ||
235 | late_time_init = choose_time_init(); | ||
236 | } | ||