time_32.c 6.3 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
/*
 *  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>
57
#include <asm/time.h>
L
Linus Torvalds 已提交
58 59 60 61 62 63 64 65 66 67 68

#include "mach_time.h"

#include <linux/timex.h>

#include <asm/hpet.h>

#include <asm/arch_hooks.h>

#include "io_ports.h"

69 70
#include <asm/i8259.h>

L
Linus Torvalds 已提交
71 72
#include "do_timer.h"

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

DEFINE_SPINLOCK(rtc_lock);
77
EXPORT_SYMBOL(rtc_lock);
L
Linus Torvalds 已提交
78 79 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

/*
 * 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;
111
	unsigned long flags;
L
Linus Torvalds 已提交
112 113

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

	return retval;
}


int timer_ack;

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

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

137 138 139 140 141 142 143 144 145 146
		/* 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 已提交
147 148 149 150 151
	return pc;
}
EXPORT_SYMBOL(profile_pc);

/*
152 153 154
 * 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 已提交
155
 */
156
irqreturn_t timer_interrupt(int irq, void *dev_id)
L
Linus Torvalds 已提交
157
{
158 159 160
	/* Keep nmi watchdog up to date */
	per_cpu(irq_stat, smp_processor_id()).irq0_irqs++;

L
Linus Torvalds 已提交
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
#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

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

	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.  */

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

	return IRQ_HANDLED;
}

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

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

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

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

	return retval;
}
210

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

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

Z
Zachary Amsden 已提交
225 226 227 228 229 230 231 232
/*
 * 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 已提交
233 234
void __init time_init(void)
{
235
	tsc_init();
Z
Zachary Amsden 已提交
236
	late_time_init = choose_time_init();
L
Linus Torvalds 已提交
237
}