diff options
Diffstat (limited to 'arch/alpha/kernel/time.c')
| -rw-r--r-- | arch/alpha/kernel/time.c | 405 | 
1 files changed, 175 insertions, 230 deletions
diff --git a/arch/alpha/kernel/time.c b/arch/alpha/kernel/time.c index ea339503655..ee39cee8064 100644 --- a/arch/alpha/kernel/time.c +++ b/arch/alpha/kernel/time.c @@ -3,13 +3,7 @@   *   *  Copyright (C) 1991, 1992, 1995, 1999, 2000  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 + * This file contains the clocksource time handling.   * 1997-09-10	Updated NTP code according to technical memorandum Jan '96   *		"A Kernel Model for Precision Timekeeping" by Dave Mills   * 1997-01-09    Adrian Sun @@ -21,9 +15,6 @@   * 1999-04-16	Thorsten Kranzkowski (dl8bcu@gmx.net)   *	fixed algorithm in do_gettimeofday() for calculating the precise time   *	from processor cycle counter (now taking lost_ticks into account) - * 2000-08-13	Jan-Benedict Glaw <jbglaw@lug-owl.de> - * 	Fixed time_init to be aware of epoches != 1900. This prevents - * 	booting up in 2048 for me;) Code is stolen from rtc.c.   * 2003-06-03	R. Scott Bailey <scott.bailey@eds.com>   *	Tighten sanity in time_init from 1% (10,000 PPM) to 250 PPM   */ @@ -46,40 +37,19 @@  #include <asm/uaccess.h>  #include <asm/io.h>  #include <asm/hwrpb.h> -#include <asm/rtc.h>  #include <linux/mc146818rtc.h>  #include <linux/time.h>  #include <linux/timex.h>  #include <linux/clocksource.h> +#include <linux/clockchips.h>  #include "proto.h"  #include "irq_impl.h" -static int set_rtc_mmss(unsigned long); -  DEFINE_SPINLOCK(rtc_lock);  EXPORT_SYMBOL(rtc_lock); -#define TICK_SIZE (tick_nsec / 1000) - -/* - * Shift amount by which scaled_ticks_per_cycle is scaled.  Shifting - * by 48 gives us 16 bits for HZ while keeping the accuracy good even - * for large CPU clock rates. - */ -#define FIX_SHIFT	48 - -/* lump static variables together for more efficient access: */ -static struct { -	/* cycle counter last time it got invoked */ -	__u32 last_time; -	/* ticks/cycle * 2^48 */ -	unsigned long scaled_ticks_per_cycle; -	/* partial unused tick */ -	unsigned long partial_tick; -} state; -  unsigned long est_cycle_freq;  #ifdef CONFIG_IRQ_WORK @@ -108,109 +78,156 @@ static inline __u32 rpcc(void)  	return __builtin_alpha_rpcc();  } -int update_persistent_clock(struct timespec now) -{ -	return set_rtc_mmss(now.tv_sec); -} -void read_persistent_clock(struct timespec *ts) + +/* + * The RTC as a clock_event_device primitive. + */ + +static DEFINE_PER_CPU(struct clock_event_device, cpu_ce); + +irqreturn_t +rtc_timer_interrupt(int irq, void *dev)  { -	unsigned int year, mon, day, hour, min, sec, epoch; - -	sec = CMOS_READ(RTC_SECONDS); -	min = CMOS_READ(RTC_MINUTES); -	hour = CMOS_READ(RTC_HOURS); -	day = CMOS_READ(RTC_DAY_OF_MONTH); -	mon = CMOS_READ(RTC_MONTH); -	year = CMOS_READ(RTC_YEAR); - -	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { -		sec = bcd2bin(sec); -		min = bcd2bin(min); -		hour = bcd2bin(hour); -		day = bcd2bin(day); -		mon = bcd2bin(mon); -		year = bcd2bin(year); -	} +	int cpu = smp_processor_id(); +	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); -	/* PC-like is standard; used for year >= 70 */ -	epoch = 1900; -	if (year < 20) -		epoch = 2000; -	else if (year >= 20 && year < 48) -		/* NT epoch */ -		epoch = 1980; -	else if (year >= 48 && year < 70) -		/* Digital UNIX epoch */ -		epoch = 1952; +	/* Don't run the hook for UNUSED or SHUTDOWN.  */ +	if (likely(ce->mode == CLOCK_EVT_MODE_PERIODIC)) +		ce->event_handler(ce); -	printk(KERN_INFO "Using epoch = %d\n", epoch); +	if (test_irq_work_pending()) { +		clear_irq_work_pending(); +		irq_work_run(); +	} -	if ((year += epoch) < 1970) -		year += 100; +	return IRQ_HANDLED; +} -	ts->tv_sec = mktime(year, mon, day, hour, min, sec); -	ts->tv_nsec = 0; +static void +rtc_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce) +{ +	/* The mode member of CE is updated in generic code. +	   Since we only support periodic events, nothing to do.  */ +} + +static int +rtc_ce_set_next_event(unsigned long evt, struct clock_event_device *ce) +{ +	/* This hook is for oneshot mode, which we don't support.  */ +	return -EINVAL;  } +static void __init +init_rtc_clockevent(void) +{ +	int cpu = smp_processor_id(); +	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); + +	*ce = (struct clock_event_device){ +		.name = "rtc", +		.features = CLOCK_EVT_FEAT_PERIODIC, +		.rating = 100, +		.cpumask = cpumask_of(cpu), +		.set_mode = rtc_ce_set_mode, +		.set_next_event = rtc_ce_set_next_event, +	}; +	clockevents_config_and_register(ce, CONFIG_HZ, 0, 0); +} +  /* - * timer_interrupt() needs to keep up the real-time clock, - * as well as call the "xtime_update()" routine every clocktick + * The QEMU clock as a clocksource primitive.   */ -irqreturn_t timer_interrupt(int irq, void *dev) + +static cycle_t +qemu_cs_read(struct clocksource *cs)  { -	unsigned long delta; -	__u32 now; -	long nticks; +	return qemu_get_vmtime(); +} -#ifndef CONFIG_SMP -	/* Not SMP, do kernel PC profiling here.  */ -	profile_tick(CPU_PROFILING); -#endif +static struct clocksource qemu_cs = { +	.name                   = "qemu", +	.rating                 = 400, +	.read                   = qemu_cs_read, +	.mask                   = CLOCKSOURCE_MASK(64), +	.flags                  = CLOCK_SOURCE_IS_CONTINUOUS, +	.max_idle_ns		= LONG_MAX +}; -	/* -	 * Calculate how many ticks have passed since the last update, -	 * including any previous partial leftover.  Save any resulting -	 * fraction for the next pass. -	 */ -	now = rpcc(); -	delta = now - state.last_time; -	state.last_time = now; -	delta = delta * state.scaled_ticks_per_cycle + state.partial_tick; -	state.partial_tick = delta & ((1UL << FIX_SHIFT) - 1);  -	nticks = delta >> FIX_SHIFT; -	if (nticks) -		xtime_update(nticks); +/* + * The QEMU alarm as a clock_event_device primitive. + */ -	if (test_irq_work_pending()) { -		clear_irq_work_pending(); -		irq_work_run(); -	} +static void +qemu_ce_set_mode(enum clock_event_mode mode, struct clock_event_device *ce) +{ +	/* The mode member of CE is updated for us in generic code. +	   Just make sure that the event is disabled.  */ +	qemu_set_alarm_abs(0); +} -#ifndef CONFIG_SMP -	while (nticks--) -		update_process_times(user_mode(get_irq_regs())); -#endif +static int +qemu_ce_set_next_event(unsigned long evt, struct clock_event_device *ce) +{ +	qemu_set_alarm_rel(evt); +	return 0; +} +static irqreturn_t +qemu_timer_interrupt(int irq, void *dev) +{ +	int cpu = smp_processor_id(); +	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); + +	ce->event_handler(ce);  	return IRQ_HANDLED;  } +static void __init +init_qemu_clockevent(void) +{ +	int cpu = smp_processor_id(); +	struct clock_event_device *ce = &per_cpu(cpu_ce, cpu); + +	*ce = (struct clock_event_device){ +		.name = "qemu", +		.features = CLOCK_EVT_FEAT_ONESHOT, +		.rating = 400, +		.cpumask = cpumask_of(cpu), +		.set_mode = qemu_ce_set_mode, +		.set_next_event = qemu_ce_set_next_event, +	}; + +	clockevents_config_and_register(ce, NSEC_PER_SEC, 1000, LONG_MAX); +} + +  void __init  common_init_rtc(void)  { -	unsigned char x; +	unsigned char x, sel = 0;  	/* Reset periodic interrupt frequency.  */ -	x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f; -        /* Test includes known working values on various platforms -           where 0x26 is wrong; we refuse to change those. */ -	if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) { -		printk("Setting RTC_FREQ to 1024 Hz (%x)\n", x); -		CMOS_WRITE(0x26, RTC_FREQ_SELECT); +#if CONFIG_HZ == 1024 || CONFIG_HZ == 1200 + 	x = CMOS_READ(RTC_FREQ_SELECT) & 0x3f; +	/* Test includes known working values on various platforms +	   where 0x26 is wrong; we refuse to change those. */ + 	if (x != 0x26 && x != 0x25 && x != 0x19 && x != 0x06) { +		sel = RTC_REF_CLCK_32KHZ + 6;  	} +#elif CONFIG_HZ == 256 || CONFIG_HZ == 128 || CONFIG_HZ == 64 || CONFIG_HZ == 32 +	sel = RTC_REF_CLCK_32KHZ + __builtin_ffs(32768 / CONFIG_HZ); +#else +# error "Unknown HZ from arch/alpha/Kconfig" +#endif +	if (sel) { +		printk(KERN_INFO "Setting RTC_FREQ to %d Hz (%x)\n", +		       CONFIG_HZ, sel); +		CMOS_WRITE(sel, RTC_FREQ_SELECT); + 	}  	/* Turn on periodic interrupts.  */  	x = CMOS_READ(RTC_CONTROL); @@ -233,16 +250,37 @@ common_init_rtc(void)  	init_rtc_irq();  } -unsigned int common_get_rtc_time(struct rtc_time *time) -{ -	return __get_rtc_time(time); -} + +#ifndef CONFIG_ALPHA_WTINT +/* + * The RPCC as a clocksource primitive. + * + * While we have free-running timecounters running on all CPUs, and we make + * a half-hearted attempt in init_rtc_rpcc_info to sync the timecounter + * with the wall clock, that initialization isn't kept up-to-date across + * different time counters in SMP mode.  Therefore we can only use this + * method when there's only one CPU enabled. + * + * When using the WTINT PALcall, the RPCC may shift to a lower frequency, + * or stop altogether, while waiting for the interrupt.  Therefore we cannot + * use this method when WTINT is in use. + */ -int common_set_rtc_time(struct rtc_time *time) +static cycle_t read_rpcc(struct clocksource *cs)  { -	return __set_rtc_time(time); +	return rpcc();  } +static struct clocksource clocksource_rpcc = { +	.name                   = "rpcc", +	.rating                 = 300, +	.read                   = read_rpcc, +	.mask                   = CLOCKSOURCE_MASK(32), +	.flags                  = CLOCK_SOURCE_IS_CONTINUOUS +}; +#endif /* ALPHA_WTINT */ + +  /* Validate a computed cycle counter result against the known bounds for     the given processor core.  There's too much brokenness in the way of     timing hardware for any one method to work everywhere.  :-( @@ -353,33 +391,6 @@ rpcc_after_update_in_progress(void)  	return rpcc();  } -#ifndef CONFIG_SMP -/* Until and unless we figure out how to get cpu cycle counters -   in sync and keep them there, we can't use the rpcc.  */ -static cycle_t read_rpcc(struct clocksource *cs) -{ -	cycle_t ret = (cycle_t)rpcc(); -	return ret; -} - -static struct clocksource clocksource_rpcc = { -	.name                   = "rpcc", -	.rating                 = 300, -	.read                   = read_rpcc, -	.mask                   = CLOCKSOURCE_MASK(32), -	.flags                  = CLOCK_SOURCE_IS_CONTINUOUS -}; - -static inline void register_rpcc_clocksource(long cycle_freq) -{ -	clocksource_register_hz(&clocksource_rpcc, cycle_freq); -} -#else /* !CONFIG_SMP */ -static inline void register_rpcc_clocksource(long cycle_freq) -{ -} -#endif /* !CONFIG_SMP */ -  void __init  time_init(void)  { @@ -387,6 +398,15 @@ time_init(void)  	unsigned long cycle_freq, tolerance;  	long diff; +	if (alpha_using_qemu) { +		clocksource_register_hz(&qemu_cs, NSEC_PER_SEC); +		init_qemu_clockevent(); + +		timer_irqaction.handler = qemu_timer_interrupt; +		init_rtc_irq(); +		return; +	} +  	/* Calibrate CPU clock -- attempt #1.  */  	if (!est_cycle_freq)  		est_cycle_freq = validate_cc_value(calibrate_cc_with_pit()); @@ -421,100 +441,25 @@ time_init(void)  		       "and unable to estimate a proper value!\n");  	} -	/* From John Bowman <bowman@math.ualberta.ca>: allow the values -	   to settle, as the Update-In-Progress bit going low isn't good -	   enough on some hardware.  2ms is our guess; we haven't found  -	   bogomips yet, but this is close on a 500Mhz box.  */ -	__delay(1000000); - - -	if (HZ > (1<<16)) { -		extern void __you_loose (void); -		__you_loose(); -	} - -	register_rpcc_clocksource(cycle_freq); - -	state.last_time = cc1; -	state.scaled_ticks_per_cycle -		= ((unsigned long) HZ << FIX_SHIFT) / cycle_freq; -	state.partial_tick = 0L; +	/* See above for restrictions on using clocksource_rpcc.  */ +#ifndef CONFIG_ALPHA_WTINT +	if (hwrpb->nr_processors == 1) +		clocksource_register_hz(&clocksource_rpcc, cycle_freq); +#endif  	/* Startup the timer source. */  	alpha_mv.init_rtc(); +	init_rtc_clockevent();  } -/* - * In order to set the CMOS clock precisely, set_rtc_mmss has to be - * called 500 ms after the second nowtime has started, because when - * nowtime is written into the registers of the CMOS clock, it will - * jump to the next second precisely 500 ms later. Check the Motorola - * MC146818A or Dallas DS12887 data sheet for details. - * - * BUG: This routine does not handle hour overflow properly; it just - *      sets the minutes. Usually you won't notice until after reboot! - */ - - -static int -set_rtc_mmss(unsigned long nowtime) +/* Initialize the clock_event_device for secondary cpus.  */ +#ifdef CONFIG_SMP +void __init +init_clockevent(void)  { -	int retval = 0; -	int real_seconds, real_minutes, cmos_minutes; -	unsigned char save_control, save_freq_select; - -	/* irq are locally disabled here */ -	spin_lock(&rtc_lock); -	/* Tell the clock it's being set */ -	save_control = CMOS_READ(RTC_CONTROL); -	CMOS_WRITE((save_control|RTC_SET), RTC_CONTROL); - -	/* Stop and reset prescaler */ -	save_freq_select = CMOS_READ(RTC_FREQ_SELECT); -	CMOS_WRITE((save_freq_select|RTC_DIV_RESET2), RTC_FREQ_SELECT); - -	cmos_minutes = CMOS_READ(RTC_MINUTES); -	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) -		cmos_minutes = bcd2bin(cmos_minutes); - -	/* -	 * since we're only adjusting minutes and seconds, -	 * don't interfere with hour overflow. This avoids -	 * messing with unknown time zones but requires your -	 * RTC not to be off by more than 15 minutes -	 */ -	real_seconds = nowtime % 60; -	real_minutes = nowtime / 60; -	if (((abs(real_minutes - cmos_minutes) + 15)/30) & 1) { -		/* correct for half hour time zone */ -		real_minutes += 30; -	} -	real_minutes %= 60; - -	if (abs(real_minutes - cmos_minutes) < 30) { -		if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { -			real_seconds = bin2bcd(real_seconds); -			real_minutes = bin2bcd(real_minutes); -		} -		CMOS_WRITE(real_seconds,RTC_SECONDS); -		CMOS_WRITE(real_minutes,RTC_MINUTES); -	} else { -		printk_once(KERN_NOTICE -		       "set_rtc_mmss: can't update from %d to %d\n", -		       cmos_minutes, real_minutes); - 		retval = -1; -	} - -	/* The following flags have to be released exactly in this order, -	 * otherwise the DS12887 (popular MC146818A clone with integrated -	 * battery and quartz) will not reset the oscillator and will not -	 * update precisely 500 ms later. You won't find this mentioned in -	 * the Dallas Semiconductor data sheets, but who believes data -	 * sheets anyway ...                           -- Markus Kuhn -	 */ -	CMOS_WRITE(save_control, RTC_CONTROL); -	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT); -	spin_unlock(&rtc_lock); - -	return retval; +	if (alpha_using_qemu) +		init_qemu_clockevent(); +	else +		init_rtc_clockevent();  } +#endif  | 
