| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* | 
 | 2 |  *  linux/arch/x86-64/kernel/time.c | 
 | 3 |  * | 
 | 4 |  *  "High Precision Event Timer" based timekeeping. | 
 | 5 |  * | 
 | 6 |  *  Copyright (c) 1991,1992,1995  Linus Torvalds | 
 | 7 |  *  Copyright (c) 1994  Alan Modra | 
 | 8 |  *  Copyright (c) 1995  Markus Kuhn | 
 | 9 |  *  Copyright (c) 1996  Ingo Molnar | 
 | 10 |  *  Copyright (c) 1998  Andrea Arcangeli | 
 | 11 |  *  Copyright (c) 2002  Vojtech Pavlik | 
 | 12 |  *  Copyright (c) 2003  Andi Kleen | 
 | 13 |  *  RTC support code taken from arch/i386/kernel/timers/time_hpet.c | 
 | 14 |  */ | 
 | 15 |  | 
 | 16 | #include <linux/kernel.h> | 
 | 17 | #include <linux/sched.h> | 
 | 18 | #include <linux/interrupt.h> | 
 | 19 | #include <linux/init.h> | 
 | 20 | #include <linux/mc146818rtc.h> | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | #include <linux/time.h> | 
 | 22 | #include <linux/ioport.h> | 
 | 23 | #include <linux/module.h> | 
 | 24 | #include <linux/device.h> | 
 | 25 | #include <linux/sysdev.h> | 
 | 26 | #include <linux/bcd.h> | 
 | 27 | #include <linux/kallsyms.h> | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 28 | #include <linux/acpi.h> | 
| Andi Kleen | 8d91640 | 2005-05-31 14:39:26 -0700 | [diff] [blame] | 29 | #ifdef CONFIG_ACPI | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 30 | #include <acpi/achware.h>	/* for PM timer frequency */ | 
| Andi Kleen | 8d91640 | 2005-05-31 14:39:26 -0700 | [diff] [blame] | 31 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 32 | #include <asm/8253pit.h> | 
 | 33 | #include <asm/pgtable.h> | 
 | 34 | #include <asm/vsyscall.h> | 
 | 35 | #include <asm/timex.h> | 
 | 36 | #include <asm/proto.h> | 
 | 37 | #include <asm/hpet.h> | 
 | 38 | #include <asm/sections.h> | 
 | 39 | #include <linux/cpufreq.h> | 
 | 40 | #include <linux/hpet.h> | 
 | 41 | #ifdef CONFIG_X86_LOCAL_APIC | 
 | 42 | #include <asm/apic.h> | 
 | 43 | #endif | 
 | 44 |  | 
 | 45 | u64 jiffies_64 = INITIAL_JIFFIES; | 
 | 46 |  | 
 | 47 | EXPORT_SYMBOL(jiffies_64); | 
 | 48 |  | 
 | 49 | #ifdef CONFIG_CPU_FREQ | 
 | 50 | static void cpufreq_delayed_get(void); | 
 | 51 | #endif | 
 | 52 | extern void i8254_timer_resume(void); | 
 | 53 | extern int using_apic_timer; | 
 | 54 |  | 
 | 55 | DEFINE_SPINLOCK(rtc_lock); | 
 | 56 | DEFINE_SPINLOCK(i8253_lock); | 
 | 57 |  | 
 | 58 | static int nohpet __initdata = 0; | 
 | 59 | static int notsc __initdata = 0; | 
 | 60 |  | 
 | 61 | #undef HPET_HACK_ENABLE_DANGEROUS | 
 | 62 |  | 
 | 63 | unsigned int cpu_khz;					/* TSC clocks / usec, not used here */ | 
 | 64 | static unsigned long hpet_period;			/* fsecs / HPET clock */ | 
 | 65 | unsigned long hpet_tick;				/* HPET clocks / interrupt */ | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 66 | static int hpet_use_timer; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 67 | unsigned long vxtime_hz = PIT_TICK_RATE; | 
 | 68 | int report_lost_ticks;				/* command line option */ | 
 | 69 | unsigned long long monotonic_base; | 
 | 70 |  | 
 | 71 | struct vxtime_data __vxtime __section_vxtime;	/* for vsyscalls */ | 
 | 72 |  | 
 | 73 | volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES; | 
 | 74 | unsigned long __wall_jiffies __section_wall_jiffies = INITIAL_JIFFIES; | 
 | 75 | struct timespec __xtime __section_xtime; | 
 | 76 | struct timezone __sys_tz __section_sys_tz; | 
 | 77 |  | 
 | 78 | static inline void rdtscll_sync(unsigned long *tsc) | 
 | 79 | { | 
 | 80 | #ifdef CONFIG_SMP | 
 | 81 | 	sync_core(); | 
 | 82 | #endif | 
 | 83 | 	rdtscll(*tsc); | 
 | 84 | } | 
 | 85 |  | 
 | 86 | /* | 
 | 87 |  * do_gettimeoffset() returns microseconds since last timer interrupt was | 
 | 88 |  * triggered by hardware. A memory read of HPET is slower than a register read | 
 | 89 |  * of TSC, but much more reliable. It's also synchronized to the timer | 
 | 90 |  * interrupt. Note that do_gettimeoffset() may return more than hpet_tick, if a | 
 | 91 |  * timer interrupt has happened already, but vxtime.trigger wasn't updated yet. | 
 | 92 |  * This is not a problem, because jiffies hasn't updated either. They are bound | 
 | 93 |  * together by xtime_lock. | 
 | 94 |  */ | 
 | 95 |  | 
 | 96 | static inline unsigned int do_gettimeoffset_tsc(void) | 
 | 97 | { | 
 | 98 | 	unsigned long t; | 
 | 99 | 	unsigned long x; | 
 | 100 | 	rdtscll_sync(&t); | 
 | 101 | 	if (t < vxtime.last_tsc) t = vxtime.last_tsc; /* hack */ | 
 | 102 | 	x = ((t - vxtime.last_tsc) * vxtime.tsc_quot) >> 32; | 
 | 103 | 	return x; | 
 | 104 | } | 
 | 105 |  | 
 | 106 | static inline unsigned int do_gettimeoffset_hpet(void) | 
 | 107 | { | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 108 | 	/* cap counter read to one tick to avoid inconsistencies */ | 
 | 109 | 	unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last; | 
 | 110 | 	return (min(counter,hpet_tick) * vxtime.quot) >> 32; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 111 | } | 
 | 112 |  | 
 | 113 | unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc; | 
 | 114 |  | 
 | 115 | /* | 
 | 116 |  * This version of gettimeofday() has microsecond resolution and better than | 
 | 117 |  * microsecond precision, as we're using at least a 10 MHz (usually 14.31818 | 
 | 118 |  * MHz) HPET timer. | 
 | 119 |  */ | 
 | 120 |  | 
 | 121 | void do_gettimeofday(struct timeval *tv) | 
 | 122 | { | 
 | 123 | 	unsigned long seq, t; | 
 | 124 |  	unsigned int sec, usec; | 
 | 125 |  | 
 | 126 | 	do { | 
 | 127 | 		seq = read_seqbegin(&xtime_lock); | 
 | 128 |  | 
 | 129 | 		sec = xtime.tv_sec; | 
 | 130 | 		usec = xtime.tv_nsec / 1000; | 
 | 131 |  | 
 | 132 | 		/* i386 does some correction here to keep the clock  | 
 | 133 | 		   monotonous even when ntpd is fixing drift. | 
 | 134 | 		   But they didn't work for me, there is a non monotonic | 
 | 135 | 		   clock anyways with ntp. | 
 | 136 | 		   I dropped all corrections now until a real solution can | 
 | 137 | 		   be found. Note when you fix it here you need to do the same | 
 | 138 | 		   in arch/x86_64/kernel/vsyscall.c and export all needed | 
 | 139 | 		   variables in vmlinux.lds. -AK */  | 
 | 140 |  | 
 | 141 | 		t = (jiffies - wall_jiffies) * (1000000L / HZ) + | 
 | 142 | 			do_gettimeoffset(); | 
 | 143 | 		usec += t; | 
 | 144 |  | 
 | 145 | 	} while (read_seqretry(&xtime_lock, seq)); | 
 | 146 |  | 
 | 147 | 	tv->tv_sec = sec + usec / 1000000; | 
 | 148 | 	tv->tv_usec = usec % 1000000; | 
 | 149 | } | 
 | 150 |  | 
 | 151 | EXPORT_SYMBOL(do_gettimeofday); | 
 | 152 |  | 
 | 153 | /* | 
 | 154 |  * settimeofday() first undoes the correction that gettimeofday would do | 
 | 155 |  * on the time, and then saves it. This is ugly, but has been like this for | 
 | 156 |  * ages already. | 
 | 157 |  */ | 
 | 158 |  | 
 | 159 | int do_settimeofday(struct timespec *tv) | 
 | 160 | { | 
 | 161 | 	time_t wtm_sec, sec = tv->tv_sec; | 
 | 162 | 	long wtm_nsec, nsec = tv->tv_nsec; | 
 | 163 |  | 
 | 164 | 	if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC) | 
 | 165 | 		return -EINVAL; | 
 | 166 |  | 
 | 167 | 	write_seqlock_irq(&xtime_lock); | 
 | 168 |  | 
 | 169 | 	nsec -= do_gettimeoffset() * 1000 + | 
 | 170 | 		(jiffies - wall_jiffies) * (NSEC_PER_SEC/HZ); | 
 | 171 |  | 
 | 172 | 	wtm_sec  = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec); | 
 | 173 | 	wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec); | 
 | 174 |  | 
 | 175 | 	set_normalized_timespec(&xtime, sec, nsec); | 
 | 176 | 	set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec); | 
 | 177 |  | 
| john stultz | b149ee2 | 2005-09-06 15:17:46 -0700 | [diff] [blame] | 178 | 	ntp_clear(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 179 |  | 
 | 180 | 	write_sequnlock_irq(&xtime_lock); | 
 | 181 | 	clock_was_set(); | 
 | 182 | 	return 0; | 
 | 183 | } | 
 | 184 |  | 
 | 185 | EXPORT_SYMBOL(do_settimeofday); | 
 | 186 |  | 
 | 187 | unsigned long profile_pc(struct pt_regs *regs) | 
 | 188 | { | 
 | 189 | 	unsigned long pc = instruction_pointer(regs); | 
 | 190 |  | 
 | 191 | 	/* Assume the lock function has either no stack frame or only a single word. | 
 | 192 | 	   This checks if the address on the stack looks like a kernel text address. | 
 | 193 | 	   There is a small window for false hits, but in that case the tick | 
 | 194 | 	   is just accounted to the spinlock function. | 
 | 195 | 	   Better would be to write these functions in assembler again | 
 | 196 | 	   and check exactly. */ | 
 | 197 | 	if (in_lock_functions(pc)) { | 
 | 198 | 		char *v = *(char **)regs->rsp; | 
 | 199 | 		if ((v >= _stext && v <= _etext) || | 
 | 200 | 			(v >= _sinittext && v <= _einittext) || | 
 | 201 | 			(v >= (char *)MODULES_VADDR  && v <= (char *)MODULES_END)) | 
 | 202 | 			return (unsigned long)v; | 
 | 203 | 		return ((unsigned long *)regs->rsp)[1]; | 
 | 204 | 	} | 
 | 205 | 	return pc; | 
 | 206 | } | 
 | 207 | EXPORT_SYMBOL(profile_pc); | 
 | 208 |  | 
 | 209 | /* | 
 | 210 |  * In order to set the CMOS clock precisely, set_rtc_mmss has to be called 500 | 
 | 211 |  * ms after the second nowtime has started, because when nowtime is written | 
 | 212 |  * into the registers of the CMOS clock, it will jump to the next second | 
 | 213 |  * precisely 500 ms later. Check the Motorola MC146818A or Dallas DS12887 data | 
 | 214 |  * sheet for details. | 
 | 215 |  */ | 
 | 216 |  | 
 | 217 | static void set_rtc_mmss(unsigned long nowtime) | 
 | 218 | { | 
 | 219 | 	int real_seconds, real_minutes, cmos_minutes; | 
 | 220 | 	unsigned char control, freq_select; | 
 | 221 |  | 
 | 222 | /* | 
 | 223 |  * IRQs are disabled when we're called from the timer interrupt, | 
 | 224 |  * no need for spin_lock_irqsave() | 
 | 225 |  */ | 
 | 226 |  | 
 | 227 | 	spin_lock(&rtc_lock); | 
 | 228 |  | 
 | 229 | /* | 
 | 230 |  * Tell the clock it's being set and stop it. | 
 | 231 |  */ | 
 | 232 |  | 
 | 233 | 	control = CMOS_READ(RTC_CONTROL); | 
 | 234 | 	CMOS_WRITE(control | RTC_SET, RTC_CONTROL); | 
 | 235 |  | 
 | 236 | 	freq_select = CMOS_READ(RTC_FREQ_SELECT); | 
 | 237 | 	CMOS_WRITE(freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT); | 
 | 238 |  | 
 | 239 | 	cmos_minutes = CMOS_READ(RTC_MINUTES); | 
 | 240 | 		BCD_TO_BIN(cmos_minutes); | 
 | 241 |  | 
 | 242 | /* | 
 | 243 |  * since we're only adjusting minutes and seconds, don't interfere with hour | 
 | 244 |  * overflow. This avoids messing with unknown time zones but requires your RTC | 
 | 245 |  * not to be off by more than 15 minutes. Since we're calling it only when | 
 | 246 |  * our clock is externally synchronized using NTP, this shouldn't be a problem. | 
 | 247 |  */ | 
 | 248 |  | 
 | 249 | 	real_seconds = nowtime % 60; | 
 | 250 | 	real_minutes = nowtime / 60; | 
 | 251 | 	if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1) | 
 | 252 | 		real_minutes += 30;		/* correct for half hour time zone */ | 
 | 253 | 	real_minutes %= 60; | 
 | 254 |  | 
 | 255 | #if 0 | 
 | 256 | 	/* AMD 8111 is a really bad time keeper and hits this regularly.  | 
 | 257 | 	   It probably was an attempt to avoid screwing up DST, but ignore | 
 | 258 | 	   that for now. */	    | 
 | 259 | 	if (abs(real_minutes - cmos_minutes) >= 30) { | 
 | 260 | 		printk(KERN_WARNING "time.c: can't update CMOS clock " | 
 | 261 | 		       "from %d to %d\n", cmos_minutes, real_minutes); | 
 | 262 | 	} else | 
 | 263 | #endif | 
 | 264 |  | 
 | 265 | 	{ | 
 | 266 | 			BIN_TO_BCD(real_seconds); | 
 | 267 | 			BIN_TO_BCD(real_minutes); | 
 | 268 | 		CMOS_WRITE(real_seconds, RTC_SECONDS); | 
 | 269 | 		CMOS_WRITE(real_minutes, RTC_MINUTES); | 
 | 270 | 	} | 
 | 271 |  | 
 | 272 | /* | 
 | 273 |  * The following flags have to be released exactly in this order, otherwise the | 
 | 274 |  * DS12887 (popular MC146818A clone with integrated battery and quartz) will | 
 | 275 |  * not reset the oscillator and will not update precisely 500 ms later. You | 
 | 276 |  * won't find this mentioned in the Dallas Semiconductor data sheets, but who | 
 | 277 |  * believes data sheets anyway ... -- Markus Kuhn | 
 | 278 |  */ | 
 | 279 |  | 
 | 280 | 	CMOS_WRITE(control, RTC_CONTROL); | 
 | 281 | 	CMOS_WRITE(freq_select, RTC_FREQ_SELECT); | 
 | 282 |  | 
 | 283 | 	spin_unlock(&rtc_lock); | 
 | 284 | } | 
 | 285 |  | 
 | 286 |  | 
 | 287 | /* monotonic_clock(): returns # of nanoseconds passed since time_init() | 
 | 288 |  *		Note: This function is required to return accurate | 
 | 289 |  *		time even in the absence of multiple timer ticks. | 
 | 290 |  */ | 
 | 291 | unsigned long long monotonic_clock(void) | 
 | 292 | { | 
 | 293 | 	unsigned long seq; | 
 | 294 |  	u32 last_offset, this_offset, offset; | 
 | 295 | 	unsigned long long base; | 
 | 296 |  | 
 | 297 | 	if (vxtime.mode == VXTIME_HPET) { | 
 | 298 | 		do { | 
 | 299 | 			seq = read_seqbegin(&xtime_lock); | 
 | 300 |  | 
 | 301 | 			last_offset = vxtime.last; | 
 | 302 | 			base = monotonic_base; | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 303 | 			this_offset = hpet_readl(HPET_COUNTER); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 304 |  | 
 | 305 | 		} while (read_seqretry(&xtime_lock, seq)); | 
 | 306 | 		offset = (this_offset - last_offset); | 
 | 307 | 		offset *=(NSEC_PER_SEC/HZ)/hpet_tick; | 
 | 308 | 		return base + offset; | 
 | 309 | 	}else{ | 
 | 310 | 		do { | 
 | 311 | 			seq = read_seqbegin(&xtime_lock); | 
 | 312 |  | 
 | 313 | 			last_offset = vxtime.last_tsc; | 
 | 314 | 			base = monotonic_base; | 
 | 315 | 		} while (read_seqretry(&xtime_lock, seq)); | 
 | 316 | 		sync_core(); | 
 | 317 | 		rdtscll(this_offset); | 
 | 318 | 		offset = (this_offset - last_offset)*1000/cpu_khz;  | 
 | 319 | 		return base + offset; | 
 | 320 | 	} | 
 | 321 |  | 
 | 322 |  | 
 | 323 | } | 
 | 324 | EXPORT_SYMBOL(monotonic_clock); | 
 | 325 |  | 
 | 326 | static noinline void handle_lost_ticks(int lost, struct pt_regs *regs) | 
 | 327 | { | 
 | 328 |     static long lost_count; | 
 | 329 |     static int warned; | 
 | 330 |  | 
 | 331 |     if (report_lost_ticks) { | 
 | 332 | 	    printk(KERN_WARNING "time.c: Lost %d timer " | 
 | 333 | 		   "tick(s)! ", lost); | 
 | 334 | 	    print_symbol("rip %s)\n", regs->rip); | 
 | 335 |     } | 
 | 336 |  | 
 | 337 |     if (lost_count == 1000 && !warned) { | 
 | 338 | 	    printk(KERN_WARNING | 
 | 339 | 		   "warning: many lost ticks.\n" | 
 | 340 | 		   KERN_WARNING "Your time source seems to be instable or " | 
 | 341 | 		   		"some driver is hogging interupts\n"); | 
 | 342 | 	    print_symbol("rip %s\n", regs->rip); | 
 | 343 | 	    if (vxtime.mode == VXTIME_TSC && vxtime.hpet_address) { | 
 | 344 | 		    printk(KERN_WARNING "Falling back to HPET\n"); | 
 | 345 | 		    vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick; | 
 | 346 | 		    vxtime.mode = VXTIME_HPET; | 
 | 347 | 		    do_gettimeoffset = do_gettimeoffset_hpet; | 
 | 348 | 	    } | 
 | 349 | 	    /* else should fall back to PIT, but code missing. */ | 
 | 350 | 	    warned = 1; | 
 | 351 |     } else | 
 | 352 | 	    lost_count++; | 
 | 353 |  | 
 | 354 | #ifdef CONFIG_CPU_FREQ | 
 | 355 |     /* In some cases the CPU can change frequency without us noticing | 
 | 356 |        (like going into thermal throttle) | 
 | 357 |        Give cpufreq a change to catch up. */ | 
 | 358 |     if ((lost_count+1) % 25 == 0) { | 
 | 359 | 	    cpufreq_delayed_get(); | 
 | 360 |     } | 
 | 361 | #endif | 
 | 362 | } | 
 | 363 |  | 
 | 364 | static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 
 | 365 | { | 
 | 366 | 	static unsigned long rtc_update = 0; | 
 | 367 | 	unsigned long tsc; | 
 | 368 | 	int delay, offset = 0, lost = 0; | 
 | 369 |  | 
 | 370 | /* | 
 | 371 |  * Here we are in the timer irq handler. We have irqs locally disabled (so we | 
 | 372 |  * don't need spin_lock_irqsave()) but we don't know if the timer_bh is running | 
 | 373 |  * on the other CPU, so we need a lock. We also need to lock the vsyscall | 
 | 374 |  * variables, because both do_timer() and us change them -arca+vojtech | 
 | 375 |  */ | 
 | 376 |  | 
 | 377 | 	write_seqlock(&xtime_lock); | 
 | 378 |  | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 379 | 	if (vxtime.hpet_address) | 
 | 380 | 		offset = hpet_readl(HPET_COUNTER); | 
 | 381 |  | 
 | 382 | 	if (hpet_use_timer) { | 
 | 383 | 		/* if we're using the hpet timer functionality, | 
 | 384 | 		 * we can more accurately know the counter value | 
 | 385 | 		 * when the timer interrupt occured. | 
 | 386 | 		 */ | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 387 | 		offset = hpet_readl(HPET_T0_CMP) - hpet_tick; | 
 | 388 | 		delay = hpet_readl(HPET_COUNTER) - offset; | 
 | 389 | 	} else { | 
 | 390 | 		spin_lock(&i8253_lock); | 
 | 391 | 		outb_p(0x00, 0x43); | 
 | 392 | 		delay = inb_p(0x40); | 
 | 393 | 		delay |= inb(0x40) << 8; | 
 | 394 | 		spin_unlock(&i8253_lock); | 
 | 395 | 		delay = LATCH - 1 - delay; | 
 | 396 | 	} | 
 | 397 |  | 
 | 398 | 	rdtscll_sync(&tsc); | 
 | 399 |  | 
 | 400 | 	if (vxtime.mode == VXTIME_HPET) { | 
 | 401 | 		if (offset - vxtime.last > hpet_tick) { | 
 | 402 | 			lost = (offset - vxtime.last) / hpet_tick - 1; | 
 | 403 | 		} | 
 | 404 |  | 
 | 405 | 		monotonic_base +=  | 
 | 406 | 			(offset - vxtime.last)*(NSEC_PER_SEC/HZ) / hpet_tick; | 
 | 407 |  | 
 | 408 | 		vxtime.last = offset; | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 409 | #ifdef CONFIG_X86_PM_TIMER | 
 | 410 | 	} else if (vxtime.mode == VXTIME_PMTMR) { | 
 | 411 | 		lost = pmtimer_mark_offset(); | 
 | 412 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 413 | 	} else { | 
 | 414 | 		offset = (((tsc - vxtime.last_tsc) * | 
 | 415 | 			   vxtime.tsc_quot) >> 32) - (USEC_PER_SEC / HZ); | 
 | 416 |  | 
 | 417 | 		if (offset < 0) | 
 | 418 | 			offset = 0; | 
 | 419 |  | 
 | 420 | 		if (offset > (USEC_PER_SEC / HZ)) { | 
 | 421 | 			lost = offset / (USEC_PER_SEC / HZ); | 
 | 422 | 			offset %= (USEC_PER_SEC / HZ); | 
 | 423 | 		} | 
 | 424 |  | 
 | 425 | 		monotonic_base += (tsc - vxtime.last_tsc)*1000000/cpu_khz ; | 
 | 426 |  | 
 | 427 | 		vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot; | 
 | 428 |  | 
 | 429 | 		if ((((tsc - vxtime.last_tsc) * | 
 | 430 | 		      vxtime.tsc_quot) >> 32) < offset) | 
 | 431 | 			vxtime.last_tsc = tsc - | 
 | 432 | 				(((long) offset << 32) / vxtime.tsc_quot) - 1; | 
 | 433 | 	} | 
 | 434 |  | 
 | 435 | 	if (lost > 0) { | 
 | 436 | 		handle_lost_ticks(lost, regs); | 
 | 437 | 		jiffies += lost; | 
 | 438 | 	} | 
 | 439 |  | 
 | 440 | /* | 
 | 441 |  * Do the timer stuff. | 
 | 442 |  */ | 
 | 443 |  | 
 | 444 | 	do_timer(regs); | 
 | 445 | #ifndef CONFIG_SMP | 
 | 446 | 	update_process_times(user_mode(regs)); | 
 | 447 | #endif | 
 | 448 |  | 
 | 449 | /* | 
 | 450 |  * In the SMP case we use the local APIC timer interrupt to do the profiling, | 
 | 451 |  * except when we simulate SMP mode on a uniprocessor system, in that case we | 
 | 452 |  * have to call the local interrupt handler. | 
 | 453 |  */ | 
 | 454 |  | 
 | 455 | #ifndef CONFIG_X86_LOCAL_APIC | 
 | 456 | 	profile_tick(CPU_PROFILING, regs); | 
 | 457 | #else | 
 | 458 | 	if (!using_apic_timer) | 
 | 459 | 		smp_local_timer_interrupt(regs); | 
 | 460 | #endif | 
 | 461 |  | 
 | 462 | /* | 
 | 463 |  * If we have an externally synchronized Linux clock, then update CMOS clock | 
 | 464 |  * accordingly every ~11 minutes. set_rtc_mmss() will be called in the jiffy | 
 | 465 |  * closest to exactly 500 ms before the next second. If the update fails, we | 
 | 466 |  * don't care, as it'll be updated on the next turn, and the problem (time way | 
 | 467 |  * off) isn't likely to go away much sooner anyway. | 
 | 468 |  */ | 
 | 469 |  | 
| john stultz | b149ee2 | 2005-09-06 15:17:46 -0700 | [diff] [blame] | 470 | 	if (ntp_synced() && xtime.tv_sec > rtc_update && | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 471 | 		abs(xtime.tv_nsec - 500000000) <= tick_nsec / 2) { | 
 | 472 | 		set_rtc_mmss(xtime.tv_sec); | 
 | 473 | 		rtc_update = xtime.tv_sec + 660; | 
 | 474 | 	} | 
 | 475 |   | 
 | 476 | 	write_sequnlock(&xtime_lock); | 
 | 477 |  | 
 | 478 | 	return IRQ_HANDLED; | 
 | 479 | } | 
 | 480 |  | 
 | 481 | static unsigned int cyc2ns_scale; | 
 | 482 | #define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */ | 
 | 483 |  | 
 | 484 | static inline void set_cyc2ns_scale(unsigned long cpu_mhz) | 
 | 485 | { | 
 | 486 | 	cyc2ns_scale = (1000 << CYC2NS_SCALE_FACTOR)/cpu_mhz; | 
 | 487 | } | 
 | 488 |  | 
 | 489 | static inline unsigned long long cycles_2_ns(unsigned long long cyc) | 
 | 490 | { | 
 | 491 | 	return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR; | 
 | 492 | } | 
 | 493 |  | 
 | 494 | unsigned long long sched_clock(void) | 
 | 495 | { | 
 | 496 | 	unsigned long a = 0; | 
 | 497 |  | 
 | 498 | #if 0 | 
 | 499 | 	/* Don't do a HPET read here. Using TSC always is much faster | 
 | 500 | 	   and HPET may not be mapped yet when the scheduler first runs. | 
 | 501 |            Disadvantage is a small drift between CPUs in some configurations, | 
 | 502 | 	   but that should be tolerable. */ | 
 | 503 | 	if (__vxtime.mode == VXTIME_HPET) | 
 | 504 | 		return (hpet_readl(HPET_COUNTER) * vxtime.quot) >> 32; | 
 | 505 | #endif | 
 | 506 |  | 
 | 507 | 	/* Could do CPU core sync here. Opteron can execute rdtsc speculatively, | 
 | 508 | 	   which means it is not completely exact and may not be monotonous between | 
 | 509 | 	   CPUs. But the errors should be too small to matter for scheduling | 
 | 510 | 	   purposes. */ | 
 | 511 |  | 
 | 512 | 	rdtscll(a); | 
 | 513 | 	return cycles_2_ns(a); | 
 | 514 | } | 
 | 515 |  | 
 | 516 | unsigned long get_cmos_time(void) | 
 | 517 | { | 
 | 518 | 	unsigned int timeout, year, mon, day, hour, min, sec; | 
 | 519 | 	unsigned char last, this; | 
 | 520 | 	unsigned long flags; | 
 | 521 |  | 
 | 522 | /* | 
 | 523 |  * The Linux interpretation of the CMOS clock register contents: When the | 
 | 524 |  * Update-In-Progress (UIP) flag goes from 1 to 0, the RTC registers show the | 
 | 525 |  * second which has precisely just started. Waiting for this can take up to 1 | 
 | 526 |  * second, we timeout approximately after 2.4 seconds on a machine with | 
 | 527 |  * standard 8.3 MHz ISA bus. | 
 | 528 |  */ | 
 | 529 |  | 
 | 530 | 	spin_lock_irqsave(&rtc_lock, flags); | 
 | 531 |  | 
 | 532 | 	timeout = 1000000; | 
 | 533 | 	last = this = 0; | 
 | 534 |  | 
 | 535 | 	while (timeout && last && !this) { | 
 | 536 | 		last = this; | 
 | 537 | 		this = CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP; | 
 | 538 | 		timeout--; | 
 | 539 | 	} | 
 | 540 |  | 
 | 541 | /* | 
 | 542 |  * Here we are safe to assume the registers won't change for a whole second, so | 
 | 543 |  * we just go ahead and read them. | 
 | 544 | 	 */ | 
 | 545 |  | 
 | 546 | 		sec = CMOS_READ(RTC_SECONDS); | 
 | 547 | 		min = CMOS_READ(RTC_MINUTES); | 
 | 548 | 		hour = CMOS_READ(RTC_HOURS); | 
 | 549 | 		day = CMOS_READ(RTC_DAY_OF_MONTH); | 
 | 550 | 		mon = CMOS_READ(RTC_MONTH); | 
 | 551 | 		year = CMOS_READ(RTC_YEAR); | 
 | 552 |  | 
 | 553 | 	spin_unlock_irqrestore(&rtc_lock, flags); | 
 | 554 |  | 
 | 555 | /* | 
 | 556 |  * We know that x86-64 always uses BCD format, no need to check the config | 
 | 557 |  * register. | 
 | 558 |  */ | 
 | 559 |  | 
 | 560 | 	    BCD_TO_BIN(sec); | 
 | 561 | 	    BCD_TO_BIN(min); | 
 | 562 | 	    BCD_TO_BIN(hour); | 
 | 563 | 	    BCD_TO_BIN(day); | 
 | 564 | 	    BCD_TO_BIN(mon); | 
 | 565 | 	    BCD_TO_BIN(year); | 
 | 566 |  | 
 | 567 | /* | 
 | 568 |  * x86-64 systems only exists since 2002. | 
 | 569 |  * This will work up to Dec 31, 2100 | 
 | 570 |  */ | 
 | 571 | 	year += 2000; | 
 | 572 |  | 
 | 573 | 	return mktime(year, mon, day, hour, min, sec); | 
 | 574 | } | 
 | 575 |  | 
 | 576 | #ifdef CONFIG_CPU_FREQ | 
 | 577 |  | 
 | 578 | /* Frequency scaling support. Adjust the TSC based timer when the cpu frequency | 
 | 579 |    changes. | 
 | 580 |     | 
 | 581 |    RED-PEN: On SMP we assume all CPUs run with the same frequency.  It's | 
 | 582 |    not that important because current Opteron setups do not support | 
 | 583 |    scaling on SMP anyroads. | 
 | 584 |  | 
 | 585 |    Should fix up last_tsc too. Currently gettimeofday in the | 
 | 586 |    first tick after the change will be slightly wrong. */ | 
 | 587 |  | 
 | 588 | #include <linux/workqueue.h> | 
 | 589 |  | 
 | 590 | static unsigned int cpufreq_delayed_issched = 0; | 
 | 591 | static unsigned int cpufreq_init = 0; | 
 | 592 | static struct work_struct cpufreq_delayed_get_work; | 
 | 593 |  | 
 | 594 | static void handle_cpufreq_delayed_get(void *v) | 
 | 595 | { | 
 | 596 | 	unsigned int cpu; | 
 | 597 | 	for_each_online_cpu(cpu) { | 
 | 598 | 		cpufreq_get(cpu); | 
 | 599 | 	} | 
 | 600 | 	cpufreq_delayed_issched = 0; | 
 | 601 | } | 
 | 602 |  | 
 | 603 | /* if we notice lost ticks, schedule a call to cpufreq_get() as it tries | 
 | 604 |  * to verify the CPU frequency the timing core thinks the CPU is running | 
 | 605 |  * at is still correct. | 
 | 606 |  */ | 
 | 607 | static void cpufreq_delayed_get(void) | 
 | 608 | { | 
 | 609 | 	static int warned; | 
 | 610 | 	if (cpufreq_init && !cpufreq_delayed_issched) { | 
 | 611 | 		cpufreq_delayed_issched = 1; | 
 | 612 | 		if (!warned) { | 
 | 613 | 			warned = 1; | 
 | 614 | 			printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n"); | 
 | 615 | 		} | 
 | 616 | 		schedule_work(&cpufreq_delayed_get_work); | 
 | 617 | 	} | 
 | 618 | } | 
 | 619 |  | 
 | 620 | static unsigned int  ref_freq = 0; | 
 | 621 | static unsigned long loops_per_jiffy_ref = 0; | 
 | 622 |  | 
 | 623 | static unsigned long cpu_khz_ref = 0; | 
 | 624 |  | 
 | 625 | static int time_cpufreq_notifier(struct notifier_block *nb, unsigned long val, | 
 | 626 | 				 void *data) | 
 | 627 | { | 
 | 628 |         struct cpufreq_freqs *freq = data; | 
 | 629 | 	unsigned long *lpj, dummy; | 
 | 630 |  | 
| Andi Kleen | c29601e | 2005-04-16 15:25:05 -0700 | [diff] [blame] | 631 | 	if (cpu_has(&cpu_data[freq->cpu], X86_FEATURE_CONSTANT_TSC)) | 
 | 632 | 		return 0; | 
 | 633 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 634 | 	lpj = &dummy; | 
 | 635 | 	if (!(freq->flags & CPUFREQ_CONST_LOOPS)) | 
 | 636 | #ifdef CONFIG_SMP | 
 | 637 | 	lpj = &cpu_data[freq->cpu].loops_per_jiffy; | 
 | 638 | #else | 
 | 639 | 	lpj = &boot_cpu_data.loops_per_jiffy; | 
 | 640 | #endif | 
 | 641 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 642 | 	if (!ref_freq) { | 
 | 643 | 		ref_freq = freq->old; | 
 | 644 | 		loops_per_jiffy_ref = *lpj; | 
 | 645 | 		cpu_khz_ref = cpu_khz; | 
 | 646 | 	} | 
 | 647 |         if ((val == CPUFREQ_PRECHANGE  && freq->old < freq->new) || | 
 | 648 |             (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) || | 
 | 649 | 	    (val == CPUFREQ_RESUMECHANGE)) { | 
 | 650 |                 *lpj = | 
 | 651 | 		cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new); | 
 | 652 |  | 
 | 653 | 		cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new); | 
 | 654 | 		if (!(freq->flags & CPUFREQ_CONST_LOOPS)) | 
 | 655 | 			vxtime.tsc_quot = (1000L << 32) / cpu_khz; | 
 | 656 | 	} | 
 | 657 | 	 | 
 | 658 | 	set_cyc2ns_scale(cpu_khz_ref / 1000); | 
 | 659 |  | 
 | 660 | 	return 0; | 
 | 661 | } | 
 | 662 |   | 
 | 663 | static struct notifier_block time_cpufreq_notifier_block = { | 
 | 664 |          .notifier_call  = time_cpufreq_notifier | 
 | 665 | }; | 
 | 666 |  | 
 | 667 | static int __init cpufreq_tsc(void) | 
 | 668 | { | 
 | 669 | 	INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL); | 
 | 670 | 	if (!cpufreq_register_notifier(&time_cpufreq_notifier_block, | 
 | 671 | 				       CPUFREQ_TRANSITION_NOTIFIER)) | 
 | 672 | 		cpufreq_init = 1; | 
 | 673 | 	return 0; | 
 | 674 | } | 
 | 675 |  | 
 | 676 | core_initcall(cpufreq_tsc); | 
 | 677 |  | 
 | 678 | #endif | 
 | 679 |  | 
 | 680 | /* | 
 | 681 |  * calibrate_tsc() calibrates the processor TSC in a very simple way, comparing | 
 | 682 |  * it to the HPET timer of known frequency. | 
 | 683 |  */ | 
 | 684 |  | 
 | 685 | #define TICK_COUNT 100000000 | 
 | 686 |  | 
 | 687 | static unsigned int __init hpet_calibrate_tsc(void) | 
 | 688 | { | 
 | 689 | 	int tsc_start, hpet_start; | 
 | 690 | 	int tsc_now, hpet_now; | 
 | 691 | 	unsigned long flags; | 
 | 692 |  | 
 | 693 | 	local_irq_save(flags); | 
 | 694 | 	local_irq_disable(); | 
 | 695 |  | 
 | 696 | 	hpet_start = hpet_readl(HPET_COUNTER); | 
 | 697 | 	rdtscl(tsc_start); | 
 | 698 |  | 
 | 699 | 	do { | 
 | 700 | 		local_irq_disable(); | 
 | 701 | 		hpet_now = hpet_readl(HPET_COUNTER); | 
 | 702 | 		sync_core(); | 
 | 703 | 		rdtscl(tsc_now); | 
 | 704 | 		local_irq_restore(flags); | 
 | 705 | 	} while ((tsc_now - tsc_start) < TICK_COUNT && | 
 | 706 | 		 (hpet_now - hpet_start) < TICK_COUNT); | 
 | 707 |  | 
 | 708 | 	return (tsc_now - tsc_start) * 1000000000L | 
 | 709 | 		/ ((hpet_now - hpet_start) * hpet_period / 1000); | 
 | 710 | } | 
 | 711 |  | 
 | 712 |  | 
 | 713 | /* | 
 | 714 |  * pit_calibrate_tsc() uses the speaker output (channel 2) of | 
 | 715 |  * the PIT. This is better than using the timer interrupt output, | 
 | 716 |  * because we can read the value of the speaker with just one inb(), | 
 | 717 |  * where we need three i/o operations for the interrupt channel. | 
 | 718 |  * We count how many ticks the TSC does in 50 ms. | 
 | 719 |  */ | 
 | 720 |  | 
 | 721 | static unsigned int __init pit_calibrate_tsc(void) | 
 | 722 | { | 
 | 723 | 	unsigned long start, end; | 
 | 724 | 	unsigned long flags; | 
 | 725 |  | 
 | 726 | 	spin_lock_irqsave(&i8253_lock, flags); | 
 | 727 |  | 
 | 728 | 	outb((inb(0x61) & ~0x02) | 0x01, 0x61); | 
 | 729 |  | 
 | 730 | 	outb(0xb0, 0x43); | 
 | 731 | 	outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42); | 
 | 732 | 	outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42); | 
 | 733 | 	rdtscll(start); | 
 | 734 | 	sync_core(); | 
 | 735 | 	while ((inb(0x61) & 0x20) == 0); | 
 | 736 | 	sync_core(); | 
 | 737 | 	rdtscll(end); | 
 | 738 |  | 
 | 739 | 	spin_unlock_irqrestore(&i8253_lock, flags); | 
 | 740 | 	 | 
 | 741 | 	return (end - start) / 50; | 
 | 742 | } | 
 | 743 |  | 
 | 744 | #ifdef	CONFIG_HPET | 
 | 745 | static __init int late_hpet_init(void) | 
 | 746 | { | 
 | 747 | 	struct hpet_data	hd; | 
 | 748 | 	unsigned int 		ntimer; | 
 | 749 |  | 
 | 750 | 	if (!vxtime.hpet_address) | 
 | 751 |           return -1; | 
 | 752 |  | 
 | 753 | 	memset(&hd, 0, sizeof (hd)); | 
 | 754 |  | 
 | 755 | 	ntimer = hpet_readl(HPET_ID); | 
 | 756 | 	ntimer = (ntimer & HPET_ID_NUMBER) >> HPET_ID_NUMBER_SHIFT; | 
 | 757 | 	ntimer++; | 
 | 758 |  | 
 | 759 | 	/* | 
 | 760 | 	 * Register with driver. | 
 | 761 | 	 * Timer0 and Timer1 is used by platform. | 
 | 762 | 	 */ | 
 | 763 | 	hd.hd_phys_address = vxtime.hpet_address; | 
 | 764 | 	hd.hd_address = (void *)fix_to_virt(FIX_HPET_BASE); | 
 | 765 | 	hd.hd_nirqs = ntimer; | 
 | 766 | 	hd.hd_flags = HPET_DATA_PLATFORM; | 
 | 767 | 	hpet_reserve_timer(&hd, 0); | 
 | 768 | #ifdef	CONFIG_HPET_EMULATE_RTC | 
 | 769 | 	hpet_reserve_timer(&hd, 1); | 
 | 770 | #endif | 
 | 771 | 	hd.hd_irq[0] = HPET_LEGACY_8254; | 
 | 772 | 	hd.hd_irq[1] = HPET_LEGACY_RTC; | 
 | 773 | 	if (ntimer > 2) { | 
 | 774 | 		struct hpet		*hpet; | 
 | 775 | 		struct hpet_timer	*timer; | 
 | 776 | 		int			i; | 
 | 777 |  | 
 | 778 | 		hpet = (struct hpet *) fix_to_virt(FIX_HPET_BASE); | 
 | 779 |  | 
 | 780 | 		for (i = 2, timer = &hpet->hpet_timers[2]; i < ntimer; | 
 | 781 | 		     timer++, i++) | 
 | 782 | 			hd.hd_irq[i] = (timer->hpet_config & | 
 | 783 | 					Tn_INT_ROUTE_CNF_MASK) >> | 
 | 784 | 				Tn_INT_ROUTE_CNF_SHIFT; | 
 | 785 |  | 
 | 786 | 	} | 
 | 787 |  | 
 | 788 | 	hpet_alloc(&hd); | 
 | 789 | 	return 0; | 
 | 790 | } | 
 | 791 | fs_initcall(late_hpet_init); | 
 | 792 | #endif | 
 | 793 |  | 
 | 794 | static int hpet_timer_stop_set_go(unsigned long tick) | 
 | 795 | { | 
 | 796 | 	unsigned int cfg; | 
 | 797 |  | 
 | 798 | /* | 
 | 799 |  * Stop the timers and reset the main counter. | 
 | 800 |  */ | 
 | 801 |  | 
 | 802 | 	cfg = hpet_readl(HPET_CFG); | 
 | 803 | 	cfg &= ~(HPET_CFG_ENABLE | HPET_CFG_LEGACY); | 
 | 804 | 	hpet_writel(cfg, HPET_CFG); | 
 | 805 | 	hpet_writel(0, HPET_COUNTER); | 
 | 806 | 	hpet_writel(0, HPET_COUNTER + 4); | 
 | 807 |  | 
 | 808 | /* | 
 | 809 |  * Set up timer 0, as periodic with first interrupt to happen at hpet_tick, | 
 | 810 |  * and period also hpet_tick. | 
 | 811 |  */ | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 812 | 	if (hpet_use_timer) { | 
 | 813 | 		hpet_writel(HPET_TN_ENABLE | HPET_TN_PERIODIC | HPET_TN_SETVAL | | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 814 | 		    HPET_TN_32BIT, HPET_T0_CFG); | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 815 | 		hpet_writel(hpet_tick, HPET_T0_CMP); | 
 | 816 | 		hpet_writel(hpet_tick, HPET_T0_CMP); /* AK: why twice? */ | 
 | 817 | 		cfg |= HPET_CFG_LEGACY; | 
 | 818 | 	} | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 819 | /* | 
 | 820 |  * Go! | 
 | 821 |  */ | 
 | 822 |  | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 823 | 	cfg |= HPET_CFG_ENABLE; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 824 | 	hpet_writel(cfg, HPET_CFG); | 
 | 825 |  | 
 | 826 | 	return 0; | 
 | 827 | } | 
 | 828 |  | 
 | 829 | static int hpet_init(void) | 
 | 830 | { | 
 | 831 | 	unsigned int id; | 
 | 832 |  | 
 | 833 | 	if (!vxtime.hpet_address) | 
 | 834 | 		return -1; | 
 | 835 | 	set_fixmap_nocache(FIX_HPET_BASE, vxtime.hpet_address); | 
 | 836 | 	__set_fixmap(VSYSCALL_HPET, vxtime.hpet_address, PAGE_KERNEL_VSYSCALL_NOCACHE); | 
 | 837 |  | 
 | 838 | /* | 
 | 839 |  * Read the period, compute tick and quotient. | 
 | 840 |  */ | 
 | 841 |  | 
 | 842 | 	id = hpet_readl(HPET_ID); | 
 | 843 |  | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 844 | 	if (!(id & HPET_ID_VENDOR) || !(id & HPET_ID_NUMBER)) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 845 | 		return -1; | 
 | 846 |  | 
 | 847 | 	hpet_period = hpet_readl(HPET_PERIOD); | 
 | 848 | 	if (hpet_period < 100000 || hpet_period > 100000000) | 
 | 849 | 		return -1; | 
 | 850 |  | 
 | 851 | 	hpet_tick = (1000000000L * (USEC_PER_SEC / HZ) + hpet_period / 2) / | 
 | 852 | 		hpet_period; | 
 | 853 |  | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 854 | 	hpet_use_timer = (id & HPET_ID_LEGSUP); | 
 | 855 |  | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 856 | 	return hpet_timer_stop_set_go(hpet_tick); | 
 | 857 | } | 
 | 858 |  | 
 | 859 | static int hpet_reenable(void) | 
 | 860 | { | 
 | 861 | 	return hpet_timer_stop_set_go(hpet_tick); | 
 | 862 | } | 
 | 863 |  | 
 | 864 | void __init pit_init(void) | 
 | 865 | { | 
 | 866 | 	unsigned long flags; | 
 | 867 |  | 
 | 868 | 	spin_lock_irqsave(&i8253_lock, flags); | 
 | 869 | 	outb_p(0x34, 0x43);		/* binary, mode 2, LSB/MSB, ch 0 */ | 
 | 870 | 	outb_p(LATCH & 0xff, 0x40);	/* LSB */ | 
 | 871 | 	outb_p(LATCH >> 8, 0x40);	/* MSB */ | 
 | 872 | 	spin_unlock_irqrestore(&i8253_lock, flags); | 
 | 873 | } | 
 | 874 |  | 
 | 875 | int __init time_setup(char *str) | 
 | 876 | { | 
 | 877 | 	report_lost_ticks = 1; | 
 | 878 | 	return 1; | 
 | 879 | } | 
 | 880 |  | 
 | 881 | static struct irqaction irq0 = { | 
 | 882 | 	timer_interrupt, SA_INTERRUPT, CPU_MASK_NONE, "timer", NULL, NULL | 
 | 883 | }; | 
 | 884 |  | 
 | 885 | extern void __init config_acpi_tables(void); | 
 | 886 |  | 
 | 887 | void __init time_init(void) | 
 | 888 | { | 
 | 889 | 	char *timename; | 
 | 890 |  | 
 | 891 | #ifdef HPET_HACK_ENABLE_DANGEROUS | 
 | 892 |         if (!vxtime.hpet_address) { | 
 | 893 | 		printk(KERN_WARNING "time.c: WARNING: Enabling HPET base " | 
 | 894 | 		       "manually!\n"); | 
 | 895 |                 outl(0x800038a0, 0xcf8); | 
 | 896 |                 outl(0xff000001, 0xcfc); | 
 | 897 |                 outl(0x800038a0, 0xcf8); | 
 | 898 |                 vxtime.hpet_address = inl(0xcfc) & 0xfffffffe; | 
 | 899 | 		printk(KERN_WARNING "time.c: WARNING: Enabled HPET " | 
 | 900 | 		       "at %#lx.\n", vxtime.hpet_address); | 
 | 901 |         } | 
 | 902 | #endif | 
 | 903 | 	if (nohpet) | 
 | 904 | 		vxtime.hpet_address = 0; | 
 | 905 |  | 
 | 906 | 	xtime.tv_sec = get_cmos_time(); | 
 | 907 | 	xtime.tv_nsec = 0; | 
 | 908 |  | 
 | 909 | 	set_normalized_timespec(&wall_to_monotonic, | 
 | 910 | 	                        -xtime.tv_sec, -xtime.tv_nsec); | 
 | 911 |  | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 912 | 	if (!hpet_init()) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 913 |                 vxtime_hz = (1000000000000000L + hpet_period / 2) / | 
 | 914 | 			hpet_period; | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 915 |  | 
 | 916 | 	if (hpet_use_timer) { | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 917 | 		cpu_khz = hpet_calibrate_tsc(); | 
 | 918 | 		timename = "HPET"; | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 919 | #ifdef CONFIG_X86_PM_TIMER | 
 | 920 | 	} else if (pmtmr_ioport) { | 
 | 921 | 		vxtime_hz = PM_TIMER_FREQUENCY; | 
 | 922 | 		timename = "PM"; | 
 | 923 | 		pit_init(); | 
 | 924 | 		cpu_khz = pit_calibrate_tsc(); | 
 | 925 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 926 | 	} else { | 
 | 927 | 		pit_init(); | 
 | 928 | 		cpu_khz = pit_calibrate_tsc(); | 
 | 929 | 		timename = "PIT"; | 
 | 930 | 	} | 
 | 931 |  | 
 | 932 | 	printk(KERN_INFO "time.c: Using %ld.%06ld MHz %s timer.\n", | 
 | 933 | 	       vxtime_hz / 1000000, vxtime_hz % 1000000, timename); | 
 | 934 | 	printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n", | 
 | 935 | 		cpu_khz / 1000, cpu_khz % 1000); | 
 | 936 | 	vxtime.mode = VXTIME_TSC; | 
 | 937 | 	vxtime.quot = (1000000L << 32) / vxtime_hz; | 
 | 938 | 	vxtime.tsc_quot = (1000L << 32) / cpu_khz; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 939 | 	rdtscll_sync(&vxtime.last_tsc); | 
 | 940 | 	setup_irq(0, &irq0); | 
 | 941 |  | 
 | 942 | 	set_cyc2ns_scale(cpu_khz / 1000); | 
| Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 943 |  | 
 | 944 | #ifndef CONFIG_SMP | 
 | 945 | 	time_init_gtod(); | 
 | 946 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 947 | } | 
 | 948 |  | 
| Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 949 | /* | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 950 |  * Make an educated guess if the TSC is trustworthy and synchronized | 
 | 951 |  * over all CPUs. | 
 | 952 |  */ | 
 | 953 | static __init int unsynchronized_tsc(void) | 
 | 954 | { | 
 | 955 | #ifdef CONFIG_SMP | 
 | 956 | 	if (oem_force_hpet_timer()) | 
 | 957 | 		return 1; | 
 | 958 |  	/* Intel systems are normally all synchronized. Exceptions | 
 | 959 |  	   are handled in the OEM check above. */ | 
 | 960 |  	if (boot_cpu_data.x86_vendor == X86_VENDOR_INTEL) | 
 | 961 |  		return 0; | 
 | 962 |  	/* All in a single socket - should be synchronized */ | 
 | 963 |  	if (cpus_weight(cpu_core_map[0]) == num_online_cpus()) | 
 | 964 |  		return 0; | 
 | 965 | #endif | 
 | 966 |  	/* Assume multi socket systems are not synchronized */ | 
 | 967 |  	return num_online_cpus() > 1; | 
 | 968 | } | 
 | 969 |  | 
 | 970 | /* | 
| Andi Kleen | a8ab26f | 2005-04-16 15:25:19 -0700 | [diff] [blame] | 971 |  * Decide after all CPUs are booted what mode gettimeofday should use. | 
 | 972 |  */ | 
 | 973 | void __init time_init_gtod(void) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 974 | { | 
 | 975 | 	char *timetype; | 
 | 976 |  | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 977 | 	if (unsynchronized_tsc()) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 978 | 		notsc = 1; | 
 | 979 | 	if (vxtime.hpet_address && notsc) { | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 980 | 		timetype = hpet_use_timer ? "HPET" : "PIT/HPET"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 981 | 		vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick; | 
 | 982 | 		vxtime.mode = VXTIME_HPET; | 
 | 983 | 		do_gettimeoffset = do_gettimeoffset_hpet; | 
| Andi Kleen | 312df5f | 2005-05-16 21:53:28 -0700 | [diff] [blame] | 984 | #ifdef CONFIG_X86_PM_TIMER | 
 | 985 | 	/* Using PM for gettimeofday is quite slow, but we have no other | 
 | 986 | 	   choice because the TSC is too unreliable on some systems. */ | 
 | 987 | 	} else if (pmtmr_ioport && !vxtime.hpet_address && notsc) { | 
 | 988 | 		timetype = "PM"; | 
 | 989 | 		do_gettimeoffset = do_gettimeoffset_pm; | 
 | 990 | 		vxtime.mode = VXTIME_PMTMR; | 
 | 991 | 		sysctl_vsyscall = 0; | 
 | 992 | 		printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n"); | 
 | 993 | #endif | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 994 | 	} else { | 
| john stultz | a3a0075 | 2005-06-23 00:08:36 -0700 | [diff] [blame] | 995 | 		timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC"; | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 996 | 		vxtime.mode = VXTIME_TSC; | 
 | 997 | 	} | 
 | 998 |  | 
 | 999 | 	printk(KERN_INFO "time.c: Using %s based timekeeping.\n", timetype); | 
 | 1000 | } | 
 | 1001 |  | 
 | 1002 | __setup("report_lost_ticks", time_setup); | 
 | 1003 |  | 
 | 1004 | static long clock_cmos_diff; | 
 | 1005 | static unsigned long sleep_start; | 
 | 1006 |  | 
| Pavel Machek | 0b9c33a | 2005-04-16 15:25:31 -0700 | [diff] [blame] | 1007 | static int timer_suspend(struct sys_device *dev, pm_message_t state) | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1008 | { | 
 | 1009 | 	/* | 
 | 1010 | 	 * Estimate time zone so that set_time can update the clock | 
 | 1011 | 	 */ | 
 | 1012 | 	long cmos_time =  get_cmos_time(); | 
 | 1013 |  | 
 | 1014 | 	clock_cmos_diff = -cmos_time; | 
 | 1015 | 	clock_cmos_diff += get_seconds(); | 
 | 1016 | 	sleep_start = cmos_time; | 
 | 1017 | 	return 0; | 
 | 1018 | } | 
 | 1019 |  | 
 | 1020 | static int timer_resume(struct sys_device *dev) | 
 | 1021 | { | 
 | 1022 | 	unsigned long flags; | 
 | 1023 | 	unsigned long sec; | 
 | 1024 | 	unsigned long ctime = get_cmos_time(); | 
 | 1025 | 	unsigned long sleep_length = (ctime - sleep_start) * HZ; | 
 | 1026 |  | 
 | 1027 | 	if (vxtime.hpet_address) | 
 | 1028 | 		hpet_reenable(); | 
 | 1029 | 	else | 
 | 1030 | 		i8254_timer_resume(); | 
 | 1031 |  | 
 | 1032 | 	sec = ctime + clock_cmos_diff; | 
 | 1033 | 	write_seqlock_irqsave(&xtime_lock,flags); | 
 | 1034 | 	xtime.tv_sec = sec; | 
 | 1035 | 	xtime.tv_nsec = 0; | 
 | 1036 | 	write_sequnlock_irqrestore(&xtime_lock,flags); | 
 | 1037 | 	jiffies += sleep_length; | 
 | 1038 | 	wall_jiffies += sleep_length; | 
| Ingo Molnar | 8446f1d | 2005-09-06 15:16:27 -0700 | [diff] [blame] | 1039 | 	touch_softlockup_watchdog(); | 
| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1040 | 	return 0; | 
 | 1041 | } | 
 | 1042 |  | 
 | 1043 | static struct sysdev_class timer_sysclass = { | 
 | 1044 | 	.resume = timer_resume, | 
 | 1045 | 	.suspend = timer_suspend, | 
 | 1046 | 	set_kset_name("timer"), | 
 | 1047 | }; | 
 | 1048 |  | 
 | 1049 |  | 
 | 1050 | /* XXX this driverfs stuff should probably go elsewhere later -john */ | 
 | 1051 | static struct sys_device device_timer = { | 
 | 1052 | 	.id	= 0, | 
 | 1053 | 	.cls	= &timer_sysclass, | 
 | 1054 | }; | 
 | 1055 |  | 
 | 1056 | static int time_init_device(void) | 
 | 1057 | { | 
 | 1058 | 	int error = sysdev_class_register(&timer_sysclass); | 
 | 1059 | 	if (!error) | 
 | 1060 | 		error = sysdev_register(&device_timer); | 
 | 1061 | 	return error; | 
 | 1062 | } | 
 | 1063 |  | 
 | 1064 | device_initcall(time_init_device); | 
 | 1065 |  | 
 | 1066 | #ifdef CONFIG_HPET_EMULATE_RTC | 
 | 1067 | /* HPET in LegacyReplacement Mode eats up RTC interrupt line. When, HPET | 
 | 1068 |  * is enabled, we support RTC interrupt functionality in software. | 
 | 1069 |  * RTC has 3 kinds of interrupts: | 
 | 1070 |  * 1) Update Interrupt - generate an interrupt, every sec, when RTC clock | 
 | 1071 |  *    is updated | 
 | 1072 |  * 2) Alarm Interrupt - generate an interrupt at a specific time of day | 
 | 1073 |  * 3) Periodic Interrupt - generate periodic interrupt, with frequencies | 
 | 1074 |  *    2Hz-8192Hz (2Hz-64Hz for non-root user) (all freqs in powers of 2) | 
 | 1075 |  * (1) and (2) above are implemented using polling at a frequency of | 
 | 1076 |  * 64 Hz. The exact frequency is a tradeoff between accuracy and interrupt | 
 | 1077 |  * overhead. (DEFAULT_RTC_INT_FREQ) | 
 | 1078 |  * For (3), we use interrupts at 64Hz or user specified periodic | 
 | 1079 |  * frequency, whichever is higher. | 
 | 1080 |  */ | 
 | 1081 | #include <linux/rtc.h> | 
 | 1082 |  | 
 | 1083 | extern irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs); | 
 | 1084 |  | 
 | 1085 | #define DEFAULT_RTC_INT_FREQ 	64 | 
 | 1086 | #define RTC_NUM_INTS 		1 | 
 | 1087 |  | 
 | 1088 | static unsigned long UIE_on; | 
 | 1089 | static unsigned long prev_update_sec; | 
 | 1090 |  | 
 | 1091 | static unsigned long AIE_on; | 
 | 1092 | static struct rtc_time alarm_time; | 
 | 1093 |  | 
 | 1094 | static unsigned long PIE_on; | 
 | 1095 | static unsigned long PIE_freq = DEFAULT_RTC_INT_FREQ; | 
 | 1096 | static unsigned long PIE_count; | 
 | 1097 |  | 
 | 1098 | static unsigned long hpet_rtc_int_freq; /* RTC interrupt frequency */ | 
 | 1099 |  | 
 | 1100 | int is_hpet_enabled(void) | 
 | 1101 | { | 
 | 1102 | 	return vxtime.hpet_address != 0; | 
 | 1103 | } | 
 | 1104 |  | 
 | 1105 | /* | 
 | 1106 |  * Timer 1 for RTC, we do not use periodic interrupt feature, | 
 | 1107 |  * even if HPET supports periodic interrupts on Timer 1. | 
 | 1108 |  * The reason being, to set up a periodic interrupt in HPET, we need to | 
 | 1109 |  * stop the main counter. And if we do that everytime someone diables/enables | 
 | 1110 |  * RTC, we will have adverse effect on main kernel timer running on Timer 0. | 
 | 1111 |  * So, for the time being, simulate the periodic interrupt in software. | 
 | 1112 |  * | 
 | 1113 |  * hpet_rtc_timer_init() is called for the first time and during subsequent | 
 | 1114 |  * interuppts reinit happens through hpet_rtc_timer_reinit(). | 
 | 1115 |  */ | 
 | 1116 | int hpet_rtc_timer_init(void) | 
 | 1117 | { | 
 | 1118 | 	unsigned int cfg, cnt; | 
 | 1119 | 	unsigned long flags; | 
 | 1120 |  | 
 | 1121 | 	if (!is_hpet_enabled()) | 
 | 1122 | 		return 0; | 
 | 1123 | 	/* | 
 | 1124 | 	 * Set the counter 1 and enable the interrupts. | 
 | 1125 | 	 */ | 
 | 1126 | 	if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ)) | 
 | 1127 | 		hpet_rtc_int_freq = PIE_freq; | 
 | 1128 | 	else | 
 | 1129 | 		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; | 
 | 1130 |  | 
 | 1131 | 	local_irq_save(flags); | 
 | 1132 | 	cnt = hpet_readl(HPET_COUNTER); | 
 | 1133 | 	cnt += ((hpet_tick*HZ)/hpet_rtc_int_freq); | 
 | 1134 | 	hpet_writel(cnt, HPET_T1_CMP); | 
 | 1135 | 	local_irq_restore(flags); | 
 | 1136 |  | 
 | 1137 | 	cfg = hpet_readl(HPET_T1_CFG); | 
 | 1138 | 	cfg |= HPET_TN_ENABLE | HPET_TN_SETVAL | HPET_TN_32BIT; | 
 | 1139 | 	hpet_writel(cfg, HPET_T1_CFG); | 
 | 1140 |  | 
 | 1141 | 	return 1; | 
 | 1142 | } | 
 | 1143 |  | 
 | 1144 | static void hpet_rtc_timer_reinit(void) | 
 | 1145 | { | 
 | 1146 | 	unsigned int cfg, cnt; | 
 | 1147 |  | 
 | 1148 | 	if (!(PIE_on | AIE_on | UIE_on)) | 
 | 1149 | 		return; | 
 | 1150 |  | 
 | 1151 | 	if (PIE_on && (PIE_freq > DEFAULT_RTC_INT_FREQ)) | 
 | 1152 | 		hpet_rtc_int_freq = PIE_freq; | 
 | 1153 | 	else | 
 | 1154 | 		hpet_rtc_int_freq = DEFAULT_RTC_INT_FREQ; | 
 | 1155 |  | 
 | 1156 | 	/* It is more accurate to use the comparator value than current count.*/ | 
 | 1157 | 	cnt = hpet_readl(HPET_T1_CMP); | 
 | 1158 | 	cnt += hpet_tick*HZ/hpet_rtc_int_freq; | 
 | 1159 | 	hpet_writel(cnt, HPET_T1_CMP); | 
 | 1160 |  | 
 | 1161 | 	cfg = hpet_readl(HPET_T1_CFG); | 
 | 1162 | 	cfg |= HPET_TN_ENABLE | HPET_TN_SETVAL | HPET_TN_32BIT; | 
 | 1163 | 	hpet_writel(cfg, HPET_T1_CFG); | 
 | 1164 |  | 
 | 1165 | 	return; | 
 | 1166 | } | 
 | 1167 |  | 
 | 1168 | /* | 
 | 1169 |  * The functions below are called from rtc driver. | 
 | 1170 |  * Return 0 if HPET is not being used. | 
 | 1171 |  * Otherwise do the necessary changes and return 1. | 
 | 1172 |  */ | 
 | 1173 | int hpet_mask_rtc_irq_bit(unsigned long bit_mask) | 
 | 1174 | { | 
 | 1175 | 	if (!is_hpet_enabled()) | 
 | 1176 | 		return 0; | 
 | 1177 |  | 
 | 1178 | 	if (bit_mask & RTC_UIE) | 
 | 1179 | 		UIE_on = 0; | 
 | 1180 | 	if (bit_mask & RTC_PIE) | 
 | 1181 | 		PIE_on = 0; | 
 | 1182 | 	if (bit_mask & RTC_AIE) | 
 | 1183 | 		AIE_on = 0; | 
 | 1184 |  | 
 | 1185 | 	return 1; | 
 | 1186 | } | 
 | 1187 |  | 
 | 1188 | int hpet_set_rtc_irq_bit(unsigned long bit_mask) | 
 | 1189 | { | 
 | 1190 | 	int timer_init_reqd = 0; | 
 | 1191 |  | 
 | 1192 | 	if (!is_hpet_enabled()) | 
 | 1193 | 		return 0; | 
 | 1194 |  | 
 | 1195 | 	if (!(PIE_on | AIE_on | UIE_on)) | 
 | 1196 | 		timer_init_reqd = 1; | 
 | 1197 |  | 
 | 1198 | 	if (bit_mask & RTC_UIE) { | 
 | 1199 | 		UIE_on = 1; | 
 | 1200 | 	} | 
 | 1201 | 	if (bit_mask & RTC_PIE) { | 
 | 1202 | 		PIE_on = 1; | 
 | 1203 | 		PIE_count = 0; | 
 | 1204 | 	} | 
 | 1205 | 	if (bit_mask & RTC_AIE) { | 
 | 1206 | 		AIE_on = 1; | 
 | 1207 | 	} | 
 | 1208 |  | 
 | 1209 | 	if (timer_init_reqd) | 
 | 1210 | 		hpet_rtc_timer_init(); | 
 | 1211 |  | 
 | 1212 | 	return 1; | 
 | 1213 | } | 
 | 1214 |  | 
 | 1215 | int hpet_set_alarm_time(unsigned char hrs, unsigned char min, unsigned char sec) | 
 | 1216 | { | 
 | 1217 | 	if (!is_hpet_enabled()) | 
 | 1218 | 		return 0; | 
 | 1219 |  | 
 | 1220 | 	alarm_time.tm_hour = hrs; | 
 | 1221 | 	alarm_time.tm_min = min; | 
 | 1222 | 	alarm_time.tm_sec = sec; | 
 | 1223 |  | 
 | 1224 | 	return 1; | 
 | 1225 | } | 
 | 1226 |  | 
 | 1227 | int hpet_set_periodic_freq(unsigned long freq) | 
 | 1228 | { | 
 | 1229 | 	if (!is_hpet_enabled()) | 
 | 1230 | 		return 0; | 
 | 1231 |  | 
 | 1232 | 	PIE_freq = freq; | 
 | 1233 | 	PIE_count = 0; | 
 | 1234 |  | 
 | 1235 | 	return 1; | 
 | 1236 | } | 
 | 1237 |  | 
 | 1238 | int hpet_rtc_dropped_irq(void) | 
 | 1239 | { | 
 | 1240 | 	if (!is_hpet_enabled()) | 
 | 1241 | 		return 0; | 
 | 1242 |  | 
 | 1243 | 	return 1; | 
 | 1244 | } | 
 | 1245 |  | 
 | 1246 | irqreturn_t hpet_rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs) | 
 | 1247 | { | 
 | 1248 | 	struct rtc_time curr_time; | 
 | 1249 | 	unsigned long rtc_int_flag = 0; | 
 | 1250 | 	int call_rtc_interrupt = 0; | 
 | 1251 |  | 
 | 1252 | 	hpet_rtc_timer_reinit(); | 
 | 1253 |  | 
 | 1254 | 	if (UIE_on | AIE_on) { | 
 | 1255 | 		rtc_get_rtc_time(&curr_time); | 
 | 1256 | 	} | 
 | 1257 | 	if (UIE_on) { | 
 | 1258 | 		if (curr_time.tm_sec != prev_update_sec) { | 
 | 1259 | 			/* Set update int info, call real rtc int routine */ | 
 | 1260 | 			call_rtc_interrupt = 1; | 
 | 1261 | 			rtc_int_flag = RTC_UF; | 
 | 1262 | 			prev_update_sec = curr_time.tm_sec; | 
 | 1263 | 		} | 
 | 1264 | 	} | 
 | 1265 | 	if (PIE_on) { | 
 | 1266 | 		PIE_count++; | 
 | 1267 | 		if (PIE_count >= hpet_rtc_int_freq/PIE_freq) { | 
 | 1268 | 			/* Set periodic int info, call real rtc int routine */ | 
 | 1269 | 			call_rtc_interrupt = 1; | 
 | 1270 | 			rtc_int_flag |= RTC_PF; | 
 | 1271 | 			PIE_count = 0; | 
 | 1272 | 		} | 
 | 1273 | 	} | 
 | 1274 | 	if (AIE_on) { | 
 | 1275 | 		if ((curr_time.tm_sec == alarm_time.tm_sec) && | 
 | 1276 | 		    (curr_time.tm_min == alarm_time.tm_min) && | 
 | 1277 | 		    (curr_time.tm_hour == alarm_time.tm_hour)) { | 
 | 1278 | 			/* Set alarm int info, call real rtc int routine */ | 
 | 1279 | 			call_rtc_interrupt = 1; | 
 | 1280 | 			rtc_int_flag |= RTC_AF; | 
 | 1281 | 		} | 
 | 1282 | 	} | 
 | 1283 | 	if (call_rtc_interrupt) { | 
 | 1284 | 		rtc_int_flag |= (RTC_IRQF | (RTC_NUM_INTS << 8)); | 
 | 1285 | 		rtc_interrupt(rtc_int_flag, dev_id, regs); | 
 | 1286 | 	} | 
 | 1287 | 	return IRQ_HANDLED; | 
 | 1288 | } | 
 | 1289 | #endif | 
 | 1290 |  | 
 | 1291 |  | 
 | 1292 |  | 
 | 1293 | static int __init nohpet_setup(char *s)  | 
 | 1294 | {  | 
 | 1295 | 	nohpet = 1; | 
 | 1296 | 	return 0; | 
 | 1297 | }  | 
 | 1298 |  | 
 | 1299 | __setup("nohpet", nohpet_setup); | 
 | 1300 |  | 
 | 1301 |  | 
 | 1302 | static int __init notsc_setup(char *s) | 
 | 1303 | { | 
 | 1304 | 	notsc = 1; | 
 | 1305 | 	return 0; | 
 | 1306 | } | 
 | 1307 |  | 
 | 1308 | __setup("notsc", notsc_setup); | 
 | 1309 |  | 
 | 1310 |  |