xref: /openbmc/linux/arch/alpha/kernel/time.c (revision 7c0f6ba6)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  linux/arch/alpha/kernel/time.c
31da177e4SLinus Torvalds  *
41da177e4SLinus Torvalds  *  Copyright (C) 1991, 1992, 1995, 1999, 2000  Linus Torvalds
51da177e4SLinus Torvalds  *
685d0b3a5SRichard Henderson  * This file contains the clocksource time handling.
71da177e4SLinus Torvalds  * 1997-09-10	Updated NTP code according to technical memorandum Jan '96
81da177e4SLinus Torvalds  *		"A Kernel Model for Precision Timekeeping" by Dave Mills
91da177e4SLinus Torvalds  * 1997-01-09    Adrian Sun
101da177e4SLinus Torvalds  *      use interval timer if CONFIG_RTC=y
111da177e4SLinus Torvalds  * 1997-10-29    John Bowman (bowman@math.ualberta.ca)
121da177e4SLinus Torvalds  *      fixed tick loss calculation in timer_interrupt
131da177e4SLinus Torvalds  *      (round system clock to nearest tick instead of truncating)
141da177e4SLinus Torvalds  *      fixed algorithm in time_init for getting time from CMOS clock
151da177e4SLinus Torvalds  * 1999-04-16	Thorsten Kranzkowski (dl8bcu@gmx.net)
161da177e4SLinus Torvalds  *	fixed algorithm in do_gettimeofday() for calculating the precise time
171da177e4SLinus Torvalds  *	from processor cycle counter (now taking lost_ticks into account)
181da177e4SLinus Torvalds  * 2003-06-03	R. Scott Bailey <scott.bailey@eds.com>
191da177e4SLinus Torvalds  *	Tighten sanity in time_init from 1% (10,000 PPM) to 250 PPM
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds #include <linux/errno.h>
221da177e4SLinus Torvalds #include <linux/module.h>
231da177e4SLinus Torvalds #include <linux/sched.h>
241da177e4SLinus Torvalds #include <linux/kernel.h>
251da177e4SLinus Torvalds #include <linux/param.h>
261da177e4SLinus Torvalds #include <linux/string.h>
271da177e4SLinus Torvalds #include <linux/mm.h>
281da177e4SLinus Torvalds #include <linux/delay.h>
291da177e4SLinus Torvalds #include <linux/ioport.h>
301da177e4SLinus Torvalds #include <linux/irq.h>
311da177e4SLinus Torvalds #include <linux/interrupt.h>
321da177e4SLinus Torvalds #include <linux/init.h>
331da177e4SLinus Torvalds #include <linux/bcd.h>
341da177e4SLinus Torvalds #include <linux/profile.h>
35e360adbeSPeter Zijlstra #include <linux/irq_work.h>
361da177e4SLinus Torvalds 
377c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
381da177e4SLinus Torvalds #include <asm/io.h>
391da177e4SLinus Torvalds #include <asm/hwrpb.h>
401da177e4SLinus Torvalds 
411da177e4SLinus Torvalds #include <linux/mc146818rtc.h>
421da177e4SLinus Torvalds #include <linux/time.h>
431da177e4SLinus Torvalds #include <linux/timex.h>
449ce34c8fSJohn Stultz #include <linux/clocksource.h>
45a1659d6dSRichard Henderson #include <linux/clockchips.h>
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds #include "proto.h"
481da177e4SLinus Torvalds #include "irq_impl.h"
491da177e4SLinus Torvalds 
501da177e4SLinus Torvalds DEFINE_SPINLOCK(rtc_lock);
51cff52dafSAl Viro EXPORT_SYMBOL(rtc_lock);
521da177e4SLinus Torvalds 
531da177e4SLinus Torvalds unsigned long est_cycle_freq;
541da177e4SLinus Torvalds 
55e360adbeSPeter Zijlstra #ifdef CONFIG_IRQ_WORK
56979f8671SMichael Cree 
57e360adbeSPeter Zijlstra DEFINE_PER_CPU(u8, irq_work_pending);
58979f8671SMichael Cree 
592999a4b3SChristoph Lameter #define set_irq_work_pending_flag()  __this_cpu_write(irq_work_pending, 1)
602999a4b3SChristoph Lameter #define test_irq_work_pending()      __this_cpu_read(irq_work_pending)
612999a4b3SChristoph Lameter #define clear_irq_work_pending()     __this_cpu_write(irq_work_pending, 0)
62979f8671SMichael Cree 
630f933625SPeter Zijlstra void arch_irq_work_raise(void)
64979f8671SMichael Cree {
65e360adbeSPeter Zijlstra 	set_irq_work_pending_flag();
66979f8671SMichael Cree }
67979f8671SMichael Cree 
68e360adbeSPeter Zijlstra #else  /* CONFIG_IRQ_WORK */
69979f8671SMichael Cree 
70e360adbeSPeter Zijlstra #define test_irq_work_pending()      0
71e360adbeSPeter Zijlstra #define clear_irq_work_pending()
72979f8671SMichael Cree 
73e360adbeSPeter Zijlstra #endif /* CONFIG_IRQ_WORK */
74979f8671SMichael Cree 
751da177e4SLinus Torvalds 
761da177e4SLinus Torvalds static inline __u32 rpcc(void)
771da177e4SLinus Torvalds {
7891531b05SRichard Henderson 	return __builtin_alpha_rpcc();
791da177e4SLinus Torvalds }
801da177e4SLinus Torvalds 
81a1659d6dSRichard Henderson 
82a1659d6dSRichard Henderson 
831da177e4SLinus Torvalds /*
84a1659d6dSRichard Henderson  * The RTC as a clock_event_device primitive.
851da177e4SLinus Torvalds  */
86a1659d6dSRichard Henderson 
87a1659d6dSRichard Henderson static DEFINE_PER_CPU(struct clock_event_device, cpu_ce);
88a1659d6dSRichard Henderson 
89a1659d6dSRichard Henderson irqreturn_t
904914d7b4SRichard Henderson rtc_timer_interrupt(int irq, void *dev)
911da177e4SLinus Torvalds {
92a1659d6dSRichard Henderson 	int cpu = smp_processor_id();
93a1659d6dSRichard Henderson 	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
941da177e4SLinus Torvalds 
95a1659d6dSRichard Henderson 	/* Don't run the hook for UNUSED or SHUTDOWN.  */
966ec81932SViresh Kumar 	if (likely(clockevent_state_periodic(ce)))
97a1659d6dSRichard Henderson 		ce->event_handler(ce);
98aa02cd2dSPeter Zijlstra 
99e360adbeSPeter Zijlstra 	if (test_irq_work_pending()) {
100e360adbeSPeter Zijlstra 		clear_irq_work_pending();
101e360adbeSPeter Zijlstra 		irq_work_run();
102979f8671SMichael Cree 	}
103979f8671SMichael Cree 
1041da177e4SLinus Torvalds 	return IRQ_HANDLED;
1051da177e4SLinus Torvalds }
1061da177e4SLinus Torvalds 
107a1659d6dSRichard Henderson static int
108a1659d6dSRichard Henderson rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
109a1659d6dSRichard Henderson {
110a1659d6dSRichard Henderson 	/* This hook is for oneshot mode, which we don't support.  */
111a1659d6dSRichard Henderson 	return -EINVAL;
112a1659d6dSRichard Henderson }
113a1659d6dSRichard Henderson 
1144914d7b4SRichard Henderson static void __init
1154914d7b4SRichard Henderson init_rtc_clockevent(void)
116a1659d6dSRichard Henderson {
117a1659d6dSRichard Henderson 	int cpu = smp_processor_id();
118a1659d6dSRichard Henderson 	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
119a1659d6dSRichard Henderson 
120a1659d6dSRichard Henderson 	*ce = (struct clock_event_device){
121a1659d6dSRichard Henderson 		.name = "rtc",
122a1659d6dSRichard Henderson 		.features = CLOCK_EVT_FEAT_PERIODIC,
123a1659d6dSRichard Henderson 		.rating = 100,
124a1659d6dSRichard Henderson 		.cpumask = cpumask_of(cpu),
125a1659d6dSRichard Henderson 		.set_next_event = rtc_ce_set_next_event,
126a1659d6dSRichard Henderson 	};
127a1659d6dSRichard Henderson 
128a1659d6dSRichard Henderson 	clockevents_config_and_register(ce, CONFIG_HZ, 0, 0);
129a1659d6dSRichard Henderson }
130a1659d6dSRichard Henderson 
1314914d7b4SRichard Henderson 
1324914d7b4SRichard Henderson /*
1334914d7b4SRichard Henderson  * The QEMU clock as a clocksource primitive.
1344914d7b4SRichard Henderson  */
1354914d7b4SRichard Henderson 
1364914d7b4SRichard Henderson static cycle_t
1374914d7b4SRichard Henderson qemu_cs_read(struct clocksource *cs)
1384914d7b4SRichard Henderson {
1394914d7b4SRichard Henderson 	return qemu_get_vmtime();
1404914d7b4SRichard Henderson }
1414914d7b4SRichard Henderson 
1424914d7b4SRichard Henderson static struct clocksource qemu_cs = {
1434914d7b4SRichard Henderson 	.name                   = "qemu",
1444914d7b4SRichard Henderson 	.rating                 = 400,
1454914d7b4SRichard Henderson 	.read                   = qemu_cs_read,
1464914d7b4SRichard Henderson 	.mask                   = CLOCKSOURCE_MASK(64),
1474914d7b4SRichard Henderson 	.flags                  = CLOCK_SOURCE_IS_CONTINUOUS,
1484914d7b4SRichard Henderson 	.max_idle_ns		= LONG_MAX
1494914d7b4SRichard Henderson };
1504914d7b4SRichard Henderson 
1514914d7b4SRichard Henderson 
1524914d7b4SRichard Henderson /*
1534914d7b4SRichard Henderson  * The QEMU alarm as a clock_event_device primitive.
1544914d7b4SRichard Henderson  */
1554914d7b4SRichard Henderson 
1566ec81932SViresh Kumar static int qemu_ce_shutdown(struct clock_event_device *ce)
1574914d7b4SRichard Henderson {
1584914d7b4SRichard Henderson 	/* The mode member of CE is updated for us in generic code.
1594914d7b4SRichard Henderson 	   Just make sure that the event is disabled.  */
1604914d7b4SRichard Henderson 	qemu_set_alarm_abs(0);
1616ec81932SViresh Kumar 	return 0;
1624914d7b4SRichard Henderson }
1634914d7b4SRichard Henderson 
1644914d7b4SRichard Henderson static int
1654914d7b4SRichard Henderson qemu_ce_set_next_event(unsigned long evt, struct clock_event_device *ce)
1664914d7b4SRichard Henderson {
1674914d7b4SRichard Henderson 	qemu_set_alarm_rel(evt);
1684914d7b4SRichard Henderson 	return 0;
1694914d7b4SRichard Henderson }
1704914d7b4SRichard Henderson 
1714914d7b4SRichard Henderson static irqreturn_t
1724914d7b4SRichard Henderson qemu_timer_interrupt(int irq, void *dev)
1734914d7b4SRichard Henderson {
1744914d7b4SRichard Henderson 	int cpu = smp_processor_id();
1754914d7b4SRichard Henderson 	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
1764914d7b4SRichard Henderson 
1774914d7b4SRichard Henderson 	ce->event_handler(ce);
1784914d7b4SRichard Henderson 	return IRQ_HANDLED;
1794914d7b4SRichard Henderson }
1804914d7b4SRichard Henderson 
1814914d7b4SRichard Henderson static void __init
1824914d7b4SRichard Henderson init_qemu_clockevent(void)
1834914d7b4SRichard Henderson {
1844914d7b4SRichard Henderson 	int cpu = smp_processor_id();
1854914d7b4SRichard Henderson 	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu);
1864914d7b4SRichard Henderson 
1874914d7b4SRichard Henderson 	*ce = (struct clock_event_device){
1884914d7b4SRichard Henderson 		.name = "qemu",
1894914d7b4SRichard Henderson 		.features = CLOCK_EVT_FEAT_ONESHOT,
1904914d7b4SRichard Henderson 		.rating = 400,
1914914d7b4SRichard Henderson 		.cpumask = cpumask_of(cpu),
1926ec81932SViresh Kumar 		.set_state_shutdown = qemu_ce_shutdown,
1936ec81932SViresh Kumar 		.set_state_oneshot = qemu_ce_shutdown,
1946ec81932SViresh Kumar 		.tick_resume = qemu_ce_shutdown,
1954914d7b4SRichard Henderson 		.set_next_event = qemu_ce_set_next_event,
1964914d7b4SRichard Henderson 	};
1974914d7b4SRichard Henderson 
1984914d7b4SRichard Henderson 	clockevents_config_and_register(ce, NSEC_PER_SEC, 1000, LONG_MAX);
1994914d7b4SRichard Henderson }
2004914d7b4SRichard Henderson 
2014914d7b4SRichard Henderson 
202ebaf4fc1SSam Ravnborg void __init
2031da177e4SLinus Torvalds common_init_rtc(void)
2041da177e4SLinus Torvalds {
205fddd87d6SRichard Henderson 	unsigned char x, sel = 0;
2061da177e4SLinus Torvalds 
2071da177e4SLinus Torvalds 	/* Reset periodic interrupt frequency.  */
208fddd87d6SRichard Henderson #if CONFIG_HZ == 1024 || CONFIG_HZ == 1200
2091da177e4SLinus Torvalds  	x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f;
2101da177e4SLinus Torvalds 	/* Test includes known working values on various platforms
2111da177e4SLinus Torvalds 	   where 0x26 is wrong; we refuse to change those. */
2121da177e4SLinus Torvalds  	if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) {
213fddd87d6SRichard Henderson 		sel = RTC_REF_CLCK_32KHZ + 6;
214fddd87d6SRichard Henderson 	}
215fddd87d6SRichard Henderson #elif CONFIG_HZ == 256 || CONFIG_HZ == 128 || CONFIG_HZ == 64 || CONFIG_HZ == 32
216fddd87d6SRichard Henderson 	sel = RTC_REF_CLCK_32KHZ + __builtin_ffs(32768 / CONFIG_HZ);
217fddd87d6SRichard Henderson #else
218fddd87d6SRichard Henderson # error "Unknown HZ from arch/alpha/Kconfig"
219fddd87d6SRichard Henderson #endif
220fddd87d6SRichard Henderson 	if (sel) {
221fddd87d6SRichard Henderson 		printk(KERN_INFO "Setting RTC_FREQ to %d Hz (%x)\n",
222fddd87d6SRichard Henderson 		       CONFIG_HZ, sel);
223fddd87d6SRichard Henderson 		CMOS_WRITE(sel, RTC_FREQ_SELECT);
2241da177e4SLinus Torvalds  	}
2251da177e4SLinus Torvalds 
2261da177e4SLinus Torvalds 	/* Turn on periodic interrupts.  */
2271da177e4SLinus Torvalds 	x = CMOS_READ(RTC_CONTROL);
2281da177e4SLinus Torvalds 	if (!(x & RTC_PIE)) {
2291da177e4SLinus Torvalds 		printk("Turning on RTC interrupts.\n");
2301da177e4SLinus Torvalds 		x |= RTC_PIE;
2311da177e4SLinus Torvalds 		x &= ~(RTC_AIE | RTC_UIE);
2321da177e4SLinus Torvalds 		CMOS_WRITE(x, RTC_CONTROL);
2331da177e4SLinus Torvalds 	}
2341da177e4SLinus Torvalds 	(void) CMOS_READ(RTC_INTR_FLAGS);
2351da177e4SLinus Torvalds 
2361da177e4SLinus Torvalds 	outb(0x36, 0x43);	/* pit counter 0: system timer */
2371da177e4SLinus Torvalds 	outb(0x00, 0x40);
2381da177e4SLinus Torvalds 	outb(0x00, 0x40);
2391da177e4SLinus Torvalds 
2401da177e4SLinus Torvalds 	outb(0xb6, 0x43);	/* pit counter 2: speaker */
2411da177e4SLinus Torvalds 	outb(0x31, 0x42);
2421da177e4SLinus Torvalds 	outb(0x13, 0x42);
2431da177e4SLinus Torvalds 
2441da177e4SLinus Torvalds 	init_rtc_irq();
2451da177e4SLinus Torvalds }
2461da177e4SLinus Torvalds 
247db2d3260SRichard Henderson 
248db2d3260SRichard Henderson #ifndef CONFIG_ALPHA_WTINT
249db2d3260SRichard Henderson /*
250db2d3260SRichard Henderson  * The RPCC as a clocksource primitive.
251db2d3260SRichard Henderson  *
252db2d3260SRichard Henderson  * While we have free-running timecounters running on all CPUs, and we make
253db2d3260SRichard Henderson  * a half-hearted attempt in init_rtc_rpcc_info to sync the timecounter
254db2d3260SRichard Henderson  * with the wall clock, that initialization isn't kept up-to-date across
255db2d3260SRichard Henderson  * different time counters in SMP mode.  Therefore we can only use this
256db2d3260SRichard Henderson  * method when there's only one CPU enabled.
257db2d3260SRichard Henderson  *
258db2d3260SRichard Henderson  * When using the WTINT PALcall, the RPCC may shift to a lower frequency,
259db2d3260SRichard Henderson  * or stop altogether, while waiting for the interrupt.  Therefore we cannot
260db2d3260SRichard Henderson  * use this method when WTINT is in use.
261db2d3260SRichard Henderson  */
262db2d3260SRichard Henderson 
263db2d3260SRichard Henderson static cycle_t read_rpcc(struct clocksource *cs)
264db2d3260SRichard Henderson {
265db2d3260SRichard Henderson 	return rpcc();
266db2d3260SRichard Henderson }
267db2d3260SRichard Henderson 
268db2d3260SRichard Henderson static struct clocksource clocksource_rpcc = {
269db2d3260SRichard Henderson 	.name                   = "rpcc",
270db2d3260SRichard Henderson 	.rating                 = 300,
271db2d3260SRichard Henderson 	.read                   = read_rpcc,
272db2d3260SRichard Henderson 	.mask                   = CLOCKSOURCE_MASK(32),
273db2d3260SRichard Henderson 	.flags                  = CLOCK_SOURCE_IS_CONTINUOUS
274db2d3260SRichard Henderson };
275db2d3260SRichard Henderson #endif /* ALPHA_WTINT */
276db2d3260SRichard Henderson 
277db2d3260SRichard Henderson 
2781da177e4SLinus Torvalds /* Validate a computed cycle counter result against the known bounds for
2791da177e4SLinus Torvalds    the given processor core.  There's too much brokenness in the way of
2801da177e4SLinus Torvalds    timing hardware for any one method to work everywhere.  :-(
2811da177e4SLinus Torvalds 
2821da177e4SLinus Torvalds    Return 0 if the result cannot be trusted, otherwise return the argument.  */
2831da177e4SLinus Torvalds 
2841da177e4SLinus Torvalds static unsigned long __init
2851da177e4SLinus Torvalds validate_cc_value(unsigned long cc)
2861da177e4SLinus Torvalds {
2871da177e4SLinus Torvalds 	static struct bounds {
2881da177e4SLinus Torvalds 		unsigned int min, max;
2891da177e4SLinus Torvalds 	} cpu_hz[] __initdata = {
2901da177e4SLinus Torvalds 		[EV3_CPU]    = {   50000000,  200000000 },	/* guess */
2911da177e4SLinus Torvalds 		[EV4_CPU]    = {  100000000,  300000000 },
2921da177e4SLinus Torvalds 		[LCA4_CPU]   = {  100000000,  300000000 },	/* guess */
2931da177e4SLinus Torvalds 		[EV45_CPU]   = {  200000000,  300000000 },
2941da177e4SLinus Torvalds 		[EV5_CPU]    = {  250000000,  433000000 },
2951da177e4SLinus Torvalds 		[EV56_CPU]   = {  333000000,  667000000 },
2961da177e4SLinus Torvalds 		[PCA56_CPU]  = {  400000000,  600000000 },	/* guess */
2971da177e4SLinus Torvalds 		[PCA57_CPU]  = {  500000000,  600000000 },	/* guess */
2981da177e4SLinus Torvalds 		[EV6_CPU]    = {  466000000,  600000000 },
2991da177e4SLinus Torvalds 		[EV67_CPU]   = {  600000000,  750000000 },
3001da177e4SLinus Torvalds 		[EV68AL_CPU] = {  750000000,  940000000 },
3011da177e4SLinus Torvalds 		[EV68CB_CPU] = { 1000000000, 1333333333 },
3021da177e4SLinus Torvalds 		/* None of the following are shipping as of 2001-11-01.  */
3031da177e4SLinus Torvalds 		[EV68CX_CPU] = { 1000000000, 1700000000 },	/* guess */
3041da177e4SLinus Torvalds 		[EV69_CPU]   = { 1000000000, 1700000000 },	/* guess */
3051da177e4SLinus Torvalds 		[EV7_CPU]    = {  800000000, 1400000000 },	/* guess */
3061da177e4SLinus Torvalds 		[EV79_CPU]   = { 1000000000, 2000000000 },	/* guess */
3071da177e4SLinus Torvalds 	};
3081da177e4SLinus Torvalds 
3091da177e4SLinus Torvalds 	/* Allow for some drift in the crystal.  10MHz is more than enough.  */
3101da177e4SLinus Torvalds 	const unsigned int deviation = 10000000;
3111da177e4SLinus Torvalds 
3121da177e4SLinus Torvalds 	struct percpu_struct *cpu;
3131da177e4SLinus Torvalds 	unsigned int index;
3141da177e4SLinus Torvalds 
3151da177e4SLinus Torvalds 	cpu = (struct percpu_struct *)((char*)hwrpb + hwrpb->processor_offset);
3161da177e4SLinus Torvalds 	index = cpu->type & 0xffffffff;
3171da177e4SLinus Torvalds 
3181da177e4SLinus Torvalds 	/* If index out of bounds, no way to validate.  */
31925c8716cSTobias Klauser 	if (index >= ARRAY_SIZE(cpu_hz))
3201da177e4SLinus Torvalds 		return cc;
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds 	/* If index contains no data, no way to validate.  */
3231da177e4SLinus Torvalds 	if (cpu_hz[index].max == 0)
3241da177e4SLinus Torvalds 		return cc;
3251da177e4SLinus Torvalds 
3261da177e4SLinus Torvalds 	if (cc < cpu_hz[index].min - deviation
3271da177e4SLinus Torvalds 	    || cc > cpu_hz[index].max + deviation)
3281da177e4SLinus Torvalds 		return 0;
3291da177e4SLinus Torvalds 
3301da177e4SLinus Torvalds 	return cc;
3311da177e4SLinus Torvalds }
3321da177e4SLinus Torvalds 
3331da177e4SLinus Torvalds 
3341da177e4SLinus Torvalds /*
3351da177e4SLinus Torvalds  * Calibrate CPU clock using legacy 8254 timer/counter. Stolen from
3361da177e4SLinus Torvalds  * arch/i386/time.c.
3371da177e4SLinus Torvalds  */
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds #define CALIBRATE_LATCH	0xffff
3401da177e4SLinus Torvalds #define TIMEOUT_COUNT	0x100000
3411da177e4SLinus Torvalds 
3421da177e4SLinus Torvalds static unsigned long __init
3431da177e4SLinus Torvalds calibrate_cc_with_pit(void)
3441da177e4SLinus Torvalds {
3451da177e4SLinus Torvalds 	int cc, count = 0;
3461da177e4SLinus Torvalds 
3471da177e4SLinus Torvalds 	/* Set the Gate high, disable speaker */
3481da177e4SLinus Torvalds 	outb((inb(0x61) & ~0x02) | 0x01, 0x61);
3491da177e4SLinus Torvalds 
3501da177e4SLinus Torvalds 	/*
3511da177e4SLinus Torvalds 	 * Now let's take care of CTC channel 2
3521da177e4SLinus Torvalds 	 *
3531da177e4SLinus Torvalds 	 * Set the Gate high, program CTC channel 2 for mode 0,
3541da177e4SLinus Torvalds 	 * (interrupt on terminal count mode), binary count,
3551da177e4SLinus Torvalds 	 * load 5 * LATCH count, (LSB and MSB) to begin countdown.
3561da177e4SLinus Torvalds 	 */
3571da177e4SLinus Torvalds 	outb(0xb0, 0x43);		/* binary, mode 0, LSB/MSB, Ch 2 */
3581da177e4SLinus Torvalds 	outb(CALIBRATE_LATCH & 0xff, 0x42);	/* LSB of count */
3591da177e4SLinus Torvalds 	outb(CALIBRATE_LATCH >> 8, 0x42);	/* MSB of count */
3601da177e4SLinus Torvalds 
3611da177e4SLinus Torvalds 	cc = rpcc();
3621da177e4SLinus Torvalds 	do {
3631da177e4SLinus Torvalds 		count++;
3641da177e4SLinus Torvalds 	} while ((inb(0x61) & 0x20) == 0 && count < TIMEOUT_COUNT);
3651da177e4SLinus Torvalds 	cc = rpcc() - cc;
3661da177e4SLinus Torvalds 
3671da177e4SLinus Torvalds 	/* Error: ECTCNEVERSET or ECPUTOOFAST.  */
3681da177e4SLinus Torvalds 	if (count <= 1 || count == TIMEOUT_COUNT)
3691da177e4SLinus Torvalds 		return 0;
3701da177e4SLinus Torvalds 
3711da177e4SLinus Torvalds 	return ((long)cc * PIT_TICK_RATE) / (CALIBRATE_LATCH + 1);
3721da177e4SLinus Torvalds }
3731da177e4SLinus Torvalds 
3741da177e4SLinus Torvalds /* The Linux interpretation of the CMOS clock register contents:
3751da177e4SLinus Torvalds    When the Update-In-Progress (UIP) flag goes from 1 to 0, the
3761da177e4SLinus Torvalds    RTC registers show the second which has precisely just started.
3771da177e4SLinus Torvalds    Let's hope other operating systems interpret the RTC the same way.  */
3781da177e4SLinus Torvalds 
3791da177e4SLinus Torvalds static unsigned long __init
3801da177e4SLinus Torvalds rpcc_after_update_in_progress(void)
3811da177e4SLinus Torvalds {
3821da177e4SLinus Torvalds 	do { } while (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP));
3831da177e4SLinus Torvalds 	do { } while (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP);
3841da177e4SLinus Torvalds 
3851da177e4SLinus Torvalds 	return rpcc();
3861da177e4SLinus Torvalds }
3871da177e4SLinus Torvalds 
3881da177e4SLinus Torvalds void __init
3891da177e4SLinus Torvalds time_init(void)
3901da177e4SLinus Torvalds {
3911e871be1SJohn Stultz 	unsigned int cc1, cc2;
3921da177e4SLinus Torvalds 	unsigned long cycle_freq, tolerance;
3931da177e4SLinus Torvalds 	long diff;
3941da177e4SLinus Torvalds 
3954914d7b4SRichard Henderson 	if (alpha_using_qemu) {
3964914d7b4SRichard Henderson 		clocksource_register_hz(&qemu_cs, NSEC_PER_SEC);
3974914d7b4SRichard Henderson 		init_qemu_clockevent();
3984914d7b4SRichard Henderson 
3994914d7b4SRichard Henderson 		timer_irqaction.handler = qemu_timer_interrupt;
4004914d7b4SRichard Henderson 		init_rtc_irq();
4014914d7b4SRichard Henderson 		return;
4024914d7b4SRichard Henderson 	}
4034914d7b4SRichard Henderson 
4041da177e4SLinus Torvalds 	/* Calibrate CPU clock -- attempt #1.  */
4051da177e4SLinus Torvalds 	if (!est_cycle_freq)
4061da177e4SLinus Torvalds 		est_cycle_freq = validate_cc_value(calibrate_cc_with_pit());
4071da177e4SLinus Torvalds 
4084c2e6f6aSMatt Mackall 	cc1 = rpcc();
4091da177e4SLinus Torvalds 
4101da177e4SLinus Torvalds 	/* Calibrate CPU clock -- attempt #2.  */
4111da177e4SLinus Torvalds 	if (!est_cycle_freq) {
4124c2e6f6aSMatt Mackall 		cc1 = rpcc_after_update_in_progress();
4131da177e4SLinus Torvalds 		cc2 = rpcc_after_update_in_progress();
4141da177e4SLinus Torvalds 		est_cycle_freq = validate_cc_value(cc2 - cc1);
4151da177e4SLinus Torvalds 		cc1 = cc2;
4161da177e4SLinus Torvalds 	}
4171da177e4SLinus Torvalds 
4181da177e4SLinus Torvalds 	cycle_freq = hwrpb->cycle_freq;
4191da177e4SLinus Torvalds 	if (est_cycle_freq) {
4201da177e4SLinus Torvalds 		/* If the given value is within 250 PPM of what we calculated,
4211da177e4SLinus Torvalds 		   accept it.  Otherwise, use what we found.  */
4221da177e4SLinus Torvalds 		tolerance = cycle_freq / 4000;
4231da177e4SLinus Torvalds 		diff = cycle_freq - est_cycle_freq;
4241da177e4SLinus Torvalds 		if (diff < 0)
4251da177e4SLinus Torvalds 			diff = -diff;
4261da177e4SLinus Torvalds 		if ((unsigned long)diff > tolerance) {
4271da177e4SLinus Torvalds 			cycle_freq = est_cycle_freq;
4281da177e4SLinus Torvalds 			printk("HWRPB cycle frequency bogus.  "
4291da177e4SLinus Torvalds 			       "Estimated %lu Hz\n", cycle_freq);
4301da177e4SLinus Torvalds 		} else {
4311da177e4SLinus Torvalds 			est_cycle_freq = 0;
4321da177e4SLinus Torvalds 		}
4331da177e4SLinus Torvalds 	} else if (! validate_cc_value (cycle_freq)) {
4341da177e4SLinus Torvalds 		printk("HWRPB cycle frequency bogus, "
4351da177e4SLinus Torvalds 		       "and unable to estimate a proper value!\n");
4361da177e4SLinus Torvalds 	}
4371da177e4SLinus Torvalds 
438db2d3260SRichard Henderson 	/* See above for restrictions on using clocksource_rpcc.  */
439db2d3260SRichard Henderson #ifndef CONFIG_ALPHA_WTINT
440db2d3260SRichard Henderson 	if (hwrpb->nr_processors == 1)
441db2d3260SRichard Henderson 		clocksource_register_hz(&clocksource_rpcc, cycle_freq);
442db2d3260SRichard Henderson #endif
443db2d3260SRichard Henderson 
4441da177e4SLinus Torvalds 	/* Startup the timer source. */
4451da177e4SLinus Torvalds 	alpha_mv.init_rtc();
4464914d7b4SRichard Henderson 	init_rtc_clockevent();
4471da177e4SLinus Torvalds }
4484914d7b4SRichard Henderson 
4494914d7b4SRichard Henderson /* Initialize the clock_event_device for secondary cpus.  */
4504914d7b4SRichard Henderson #ifdef CONFIG_SMP
4514914d7b4SRichard Henderson void __init
4524914d7b4SRichard Henderson init_clockevent(void)
4534914d7b4SRichard Henderson {
4544914d7b4SRichard Henderson 	if (alpha_using_qemu)
4554914d7b4SRichard Henderson 		init_qemu_clockevent();
4564914d7b4SRichard Henderson 	else
4574914d7b4SRichard Henderson 		init_rtc_clockevent();
4584914d7b4SRichard Henderson }
4594914d7b4SRichard Henderson #endif
460