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