time_32.c 6.2 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
/*
 *  linux/arch/i386/kernel/time.c
 *
 *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
 *
 * This file contains the PC-specific time handling details:
 * reading the RTC at bootup, etc..
 * 1994-07-02    Alan Modra
 *	fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
 * 1995-03-26    Markus Kuhn
 *      fixed 500 ms bug at call to set_rtc_mmss, fixed DS12887
 *      precision CMOS clock update
 * 1996-05-03    Ingo Molnar
 *      fixed time warps in do_[slow|fast]_gettimeoffset()
 * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
 *		"A Kernel Model for Precision Timekeeping" by Dave Mills
 * 1998-09-05    (Various)
 *	More robust do_fast_gettimeoffset() algorithm implemented
 *	(works with APM, Cyrix 6x86MX and Centaur C6),
 *	monotonic gettimeofday() with fast_get_timeoffset(),
 *	drift-proof precision TSC calibration on boot
 *	(C. Scott Ananian <cananian@alumni.princeton.edu>, Andrew D.
 *	Balsa <andrebalsa@altern.org>, Philip Gladstone <philip@raptor.com>;
 *	ported from 2.0.35 Jumbo-9 by Michael Krause <m.krause@tu-harburg.de>).
 * 1998-12-16    Andrea Arcangeli
 *	Fixed Jumbo-9 code in 2.1.131: do_gettimeofday was missing 1 jiffy
 *	because was not accounting lost_ticks.
 * 1998-12-24 Copyright (C) 1998  Andrea Arcangeli
 *	Fixed a xtime SMP race (we need the xtime_lock rw spinlock to
 *	serialize accesses to xtime/lost_ticks).
 */

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/param.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/delay.h>
#include <linux/init.h>
#include <linux/smp.h>
#include <linux/module.h>
#include <linux/sysdev.h>
#include <linux/bcd.h>
#include <linux/efi.h>
#include <linux/mca.h>

#include <asm/io.h>
#include <asm/smp.h>
#include <asm/irq.h>
#include <asm/msr.h>
#include <asm/delay.h>
#include <asm/mpspec.h>
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/timer.h>
59
#include <asm/time.h>
L
Linus Torvalds 已提交
60 61 62 63 64 65 66 67 68 69 70

#include "mach_time.h"

#include <linux/timex.h>

#include <asm/hpet.h>

#include <asm/arch_hooks.h>

#include "io_ports.h"

71 72
#include <asm/i8259.h>

L
Linus Torvalds 已提交
73 74
#include "do_timer.h"

A
Andrew Morton 已提交
75
unsigned int cpu_khz;	/* Detected as we calibrate the TSC */
76
EXPORT_SYMBOL(cpu_khz);
L
Linus Torvalds 已提交
77 78

DEFINE_SPINLOCK(rtc_lock);
79
EXPORT_SYMBOL(rtc_lock);
L
Linus Torvalds 已提交
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112

/*
 * This is a special lock that is owned by the CPU and holds the index
 * register we are working with.  It is required for NMI access to the
 * CMOS/RTC registers.  See include/asm-i386/mc146818rtc.h for details.
 */
volatile unsigned long cmos_lock = 0;
EXPORT_SYMBOL(cmos_lock);

/* Routines for accessing the CMOS RAM/RTC. */
unsigned char rtc_cmos_read(unsigned char addr)
{
	unsigned char val;
	lock_cmos_prefix(addr);
	outb_p(addr, RTC_PORT(0));
	val = inb_p(RTC_PORT(1));
	lock_cmos_suffix(addr);
	return val;
}
EXPORT_SYMBOL(rtc_cmos_read);

void rtc_cmos_write(unsigned char val, unsigned char addr)
{
	lock_cmos_prefix(addr);
	outb_p(addr, RTC_PORT(0));
	outb_p(val, RTC_PORT(1));
	lock_cmos_suffix(addr);
}
EXPORT_SYMBOL(rtc_cmos_write);

static int set_rtc_mmss(unsigned long nowtime)
{
	int retval;
113
	unsigned long flags;
L
Linus Torvalds 已提交
114 115

	/* gets recalled with irq locally disabled */
116 117
	/* XXX - does irqsave resolve this? -johnstul */
	spin_lock_irqsave(&rtc_lock, flags);
118
	retval = set_wallclock(nowtime);
119
	spin_unlock_irqrestore(&rtc_lock, flags);
L
Linus Torvalds 已提交
120 121 122 123 124 125 126 127 128 129 130

	return retval;
}


int timer_ack;

unsigned long profile_pc(struct pt_regs *regs)
{
	unsigned long pc = instruction_pointer(regs);

131
#ifdef CONFIG_SMP
132 133
	if (!v8086_mode(regs) && SEGMENT_IS_KERNEL_CODE(regs->xcs) &&
	    in_lock_functions(pc)) {
134
#ifdef CONFIG_FRAME_POINTER
L
Linus Torvalds 已提交
135
		return *(unsigned long *)(regs->ebp + 4);
136
#else
137 138
		unsigned long *sp = (unsigned long *)&regs->esp;

139 140 141 142 143 144 145 146 147 148
		/* Return address is either directly at stack pointer
		   or above a saved eflags. Eflags has bits 22-31 zero,
		   kernel addresses don't. */
 		if (sp[0] >> 22)
			return sp[0];
		if (sp[1] >> 22)
			return sp[1];
#endif
	}
#endif
L
Linus Torvalds 已提交
149 150 151 152 153
	return pc;
}
EXPORT_SYMBOL(profile_pc);

/*
154 155 156
 * This is the same as the above, except we _also_ save the current
 * Time Stamp Counter value at the time of the timer interrupt, so that
 * we later on can estimate the time of day more exactly.
L
Linus Torvalds 已提交
157
 */
158
irqreturn_t timer_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
{
#ifdef CONFIG_X86_IO_APIC
	if (timer_ack) {
		/*
		 * Subtle, when I/O APICs are used we have to ack timer IRQ
		 * manually to reset the IRR bit for do_slow_gettimeoffset().
		 * This will also deassert NMI lines for the watchdog if run
		 * on an 82489DX-based system.
		 */
		spin_lock(&i8259A_lock);
		outb(0x0c, PIC_MASTER_OCW3);
		/* Ack the IRQ; AEOI will end it automatically. */
		inb(PIC_MASTER_POLL);
		spin_unlock(&i8259A_lock);
	}
#endif

176
	do_timer_interrupt_hook();
L
Linus Torvalds 已提交
177 178 179 180 181 182 183 184 185 186 187

	if (MCA_bus) {
		/* The PS/2 uses level-triggered interrupts.  You can't
		turn them off, nor would you want to (any attempt to
		enable edge-triggered interrupts usually gets intercepted by a
		special hardware circuit).  Hence we have to acknowledge
		the timer interrupt.  Through some incredibly stupid
		design idea, the reset for IRQ 0 is done by setting the
		high bit of the PPI port B (0x61).  Note that some PS/2s,
		notably the 55SX, work fine if this is removed.  */

188 189
		u8 irq_v = inb_p( 0x61 );	/* read the current state */
		outb_p( irq_v|0x80, 0x61 );	/* reset the IRQ */
L
Linus Torvalds 已提交
190 191 192 193 194 195
	}

	return IRQ_HANDLED;
}

/* not static: needed by APM */
196
unsigned long read_persistent_clock(void)
L
Linus Torvalds 已提交
197 198
{
	unsigned long retval;
199
	unsigned long flags;
L
Linus Torvalds 已提交
200

201
	spin_lock_irqsave(&rtc_lock, flags);
L
Linus Torvalds 已提交
202

203
	retval = get_wallclock();
L
Linus Torvalds 已提交
204

205
	spin_unlock_irqrestore(&rtc_lock, flags);
L
Linus Torvalds 已提交
206 207 208

	return retval;
}
209

210
int update_persistent_clock(struct timespec now)
L
Linus Torvalds 已提交
211
{
212
	return set_rtc_mmss(now.tv_sec);
L
Linus Torvalds 已提交
213 214
}

215
extern void (*late_time_init)(void);
L
Linus Torvalds 已提交
216
/* Duplicate of time_init() below, with hpet_enable part added */
Z
Zachary Amsden 已提交
217
void __init hpet_time_init(void)
L
Linus Torvalds 已提交
218
{
219 220
	if (!hpet_enable())
		setup_pit_timer();
Z
Zachary Amsden 已提交
221
	time_init_hook();
L
Linus Torvalds 已提交
222 223
}

Z
Zachary Amsden 已提交
224 225 226 227 228 229 230 231
/*
 * This is called directly from init code; we must delay timer setup in the
 * HPET case as we can't make the decision to turn on HPET this early in the
 * boot process.
 *
 * The chosen time_init function will usually be hpet_time_init, above, but
 * in the case of virtual hardware, an alternative function may be substituted.
 */
L
Linus Torvalds 已提交
232 233
void __init time_init(void)
{
234
	tsc_init();
Z
Zachary Amsden 已提交
235
	late_time_init = choose_time_init();
L
Linus Torvalds 已提交
236
}