2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/arch/x86-64/kernel/time.c
|
|
|
|
*
|
|
|
|
* "High Precision Event Timer" based timekeeping.
|
|
|
|
*
|
|
|
|
* Copyright (c) 1991,1992,1995 Linus Torvalds
|
|
|
|
* Copyright (c) 1994 Alan Modra
|
|
|
|
* Copyright (c) 1995 Markus Kuhn
|
|
|
|
* Copyright (c) 1996 Ingo Molnar
|
|
|
|
* Copyright (c) 1998 Andrea Arcangeli
|
2006-06-26 19:58:38 +08:00
|
|
|
* Copyright (c) 2002,2006 Vojtech Pavlik
|
2005-04-17 06:20:36 +08:00
|
|
|
* Copyright (c) 2003 Andi Kleen
|
|
|
|
* RTC support code taken from arch/i386/kernel/timers/time_hpet.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/mc146818rtc.h>
|
|
|
|
#include <linux/time.h>
|
|
|
|
#include <linux/ioport.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/sysdev.h>
|
|
|
|
#include <linux/bcd.h>
|
2006-09-26 16:52:28 +08:00
|
|
|
#include <linux/notifier.h>
|
|
|
|
#include <linux/cpu.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/kallsyms.h>
|
2005-05-17 12:53:28 +08:00
|
|
|
#include <linux/acpi.h>
|
2005-06-01 05:39:26 +08:00
|
|
|
#ifdef CONFIG_ACPI
|
2005-05-17 12:53:28 +08:00
|
|
|
#include <acpi/achware.h> /* for PM timer frequency */
|
2006-07-30 03:42:37 +08:00
|
|
|
#include <acpi/acpi_bus.h>
|
2005-06-01 05:39:26 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <asm/8253pit.h>
|
|
|
|
#include <asm/pgtable.h>
|
|
|
|
#include <asm/vsyscall.h>
|
|
|
|
#include <asm/timex.h>
|
|
|
|
#include <asm/proto.h>
|
|
|
|
#include <asm/hpet.h>
|
|
|
|
#include <asm/sections.h>
|
|
|
|
#include <linux/hpet.h>
|
|
|
|
#include <asm/apic.h>
|
2007-02-16 17:28:19 +08:00
|
|
|
#include <asm/hpet.h>
|
2007-05-03 01:27:05 +08:00
|
|
|
#include <asm/mpspec.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-26 16:52:28 +08:00
|
|
|
static char *timename = NULL;
|
2006-02-26 11:18:49 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
DEFINE_SPINLOCK(rtc_lock);
|
2006-06-26 19:59:44 +08:00
|
|
|
EXPORT_SYMBOL(rtc_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
DEFINE_SPINLOCK(i8253_lock);
|
|
|
|
|
|
|
|
volatile unsigned long __jiffies __section_jiffies = INITIAL_JIFFIES;
|
|
|
|
|
|
|
|
unsigned long profile_pc(struct pt_regs *regs)
|
|
|
|
{
|
|
|
|
unsigned long pc = instruction_pointer(regs);
|
|
|
|
|
2006-09-26 16:52:28 +08:00
|
|
|
/* Assume the lock function has either no stack frame or a copy
|
|
|
|
of eflags from PUSHF
|
|
|
|
Eflags always has bits 22 and up cleared unlike kernel addresses. */
|
2006-07-28 20:44:42 +08:00
|
|
|
if (!user_mode(regs) && in_lock_functions(pc)) {
|
2006-09-26 16:52:28 +08:00
|
|
|
unsigned long *sp = (unsigned long *)regs->rsp;
|
|
|
|
if (sp[0] >> 22)
|
|
|
|
return sp[0];
|
|
|
|
if (sp[1] >> 22)
|
|
|
|
return sp[1];
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
return pc;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(profile_pc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void set_rtc_mmss(unsigned long nowtime)
|
|
|
|
{
|
|
|
|
int real_seconds, real_minutes, cmos_minutes;
|
|
|
|
unsigned char control, freq_select;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* IRQs are disabled when we're called from the timer interrupt,
|
|
|
|
* no need for spin_lock_irqsave()
|
|
|
|
*/
|
|
|
|
|
|
|
|
spin_lock(&rtc_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Tell the clock it's being set and stop it.
|
|
|
|
*/
|
|
|
|
|
|
|
|
control = CMOS_READ(RTC_CONTROL);
|
|
|
|
CMOS_WRITE(control | RTC_SET, RTC_CONTROL);
|
|
|
|
|
|
|
|
freq_select = CMOS_READ(RTC_FREQ_SELECT);
|
|
|
|
CMOS_WRITE(freq_select | RTC_DIV_RESET2, RTC_FREQ_SELECT);
|
|
|
|
|
|
|
|
cmos_minutes = CMOS_READ(RTC_MINUTES);
|
|
|
|
BCD_TO_BIN(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. Since we're calling it only when
|
|
|
|
* our clock is externally synchronized using NTP, this shouldn't be a problem.
|
|
|
|
*/
|
|
|
|
|
|
|
|
real_seconds = nowtime % 60;
|
|
|
|
real_minutes = nowtime / 60;
|
|
|
|
if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1)
|
|
|
|
real_minutes += 30; /* correct for half hour time zone */
|
|
|
|
real_minutes %= 60;
|
|
|
|
|
|
|
|
if (abs(real_minutes - cmos_minutes) >= 30) {
|
|
|
|
printk(KERN_WARNING "time.c: can't update CMOS clock "
|
|
|
|
"from %d to %d\n", cmos_minutes, real_minutes);
|
2006-03-25 23:30:37 +08:00
|
|
|
} else {
|
2006-01-12 05:45:33 +08:00
|
|
|
BIN_TO_BCD(real_seconds);
|
|
|
|
BIN_TO_BCD(real_minutes);
|
2005-04-17 06:20:36 +08:00
|
|
|
CMOS_WRITE(real_seconds, RTC_SECONDS);
|
|
|
|
CMOS_WRITE(real_minutes, RTC_MINUTES);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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(control, RTC_CONTROL);
|
|
|
|
CMOS_WRITE(freq_select, RTC_FREQ_SELECT);
|
|
|
|
|
|
|
|
spin_unlock(&rtc_lock);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
void main_timer_handler(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
static unsigned long rtc_update = 0;
|
|
|
|
/*
|
|
|
|
* Here we are in the timer irq handler. We have irqs locally disabled (so we
|
|
|
|
* don't need spin_lock_irqsave()) but we don't know if the timer_bh is running
|
|
|
|
* on the other CPU, so we need a lock. We also need to lock the vsyscall
|
|
|
|
* variables, because both do_timer() and us change them -arca+vojtech
|
|
|
|
*/
|
|
|
|
|
|
|
|
write_seqlock(&xtime_lock);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Do the timer stuff.
|
|
|
|
*/
|
|
|
|
|
2007-02-16 17:28:20 +08:00
|
|
|
do_timer(1);
|
2005-04-17 06:20:36 +08:00
|
|
|
#ifndef CONFIG_SMP
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
update_process_times(user_mode(get_irq_regs()));
|
2005-04-17 06:20:36 +08:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In the SMP case we use the local APIC timer interrupt to do the profiling,
|
|
|
|
* except when we simulate SMP mode on a uniprocessor system, in that case we
|
|
|
|
* have to call the local interrupt handler.
|
|
|
|
*/
|
|
|
|
|
|
|
|
if (!using_apic_timer)
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
smp_local_timer_interrupt();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we have an externally synchronized Linux clock, then update CMOS clock
|
|
|
|
* accordingly every ~11 minutes. set_rtc_mmss() will be called in the jiffy
|
|
|
|
* closest to exactly 500 ms before the next second. If the update fails, we
|
|
|
|
* don't care, as it'll be updated on the next turn, and the problem (time way
|
|
|
|
* off) isn't likely to go away much sooner anyway.
|
|
|
|
*/
|
|
|
|
|
2005-09-07 06:17:46 +08:00
|
|
|
if (ntp_synced() && xtime.tv_sec > rtc_update &&
|
2005-04-17 06:20:36 +08:00
|
|
|
abs(xtime.tv_nsec - 500000000) <= tick_nsec / 2) {
|
|
|
|
set_rtc_mmss(xtime.tv_sec);
|
|
|
|
rtc_update = xtime.tv_sec + 660;
|
|
|
|
}
|
|
|
|
|
|
|
|
write_sequnlock(&xtime_lock);
|
2006-02-04 04:50:50 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static irqreturn_t timer_interrupt(int irq, void *dev_id)
|
2006-02-04 04:50:50 +08:00
|
|
|
{
|
|
|
|
if (apic_runs_main_timer > 1)
|
|
|
|
return IRQ_HANDLED;
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
main_timer_handler();
|
2006-01-12 05:44:24 +08:00
|
|
|
if (using_apic_timer)
|
|
|
|
smp_send_timer_broadcast_ipi();
|
2005-04-17 06:20:36 +08:00
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2006-01-12 05:46:39 +08:00
|
|
|
static unsigned long get_cmos_time(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-03-28 17:56:01 +08:00
|
|
|
unsigned int year, mon, day, hour, min, sec;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long flags;
|
2007-02-03 00:48:19 +08:00
|
|
|
unsigned century = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_lock_irqsave(&rtc_lock, flags);
|
|
|
|
|
2006-03-28 17:56:01 +08:00
|
|
|
do {
|
|
|
|
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);
|
2006-03-25 23:30:31 +08:00
|
|
|
#ifdef CONFIG_ACPI
|
2007-02-03 00:48:19 +08:00
|
|
|
if (acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID &&
|
|
|
|
acpi_gbl_FADT.century)
|
|
|
|
century = CMOS_READ(acpi_gbl_FADT.century);
|
2006-03-25 23:30:31 +08:00
|
|
|
#endif
|
2006-03-28 17:56:01 +08:00
|
|
|
} while (sec != CMOS_READ(RTC_SECONDS));
|
2006-03-25 23:30:31 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irqrestore(&rtc_lock, flags);
|
|
|
|
|
2006-01-12 05:45:33 +08:00
|
|
|
/*
|
|
|
|
* We know that x86-64 always uses BCD format, no need to check the
|
|
|
|
* config register.
|
2006-03-25 23:30:34 +08:00
|
|
|
*/
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-12 05:45:33 +08:00
|
|
|
BCD_TO_BIN(sec);
|
|
|
|
BCD_TO_BIN(min);
|
|
|
|
BCD_TO_BIN(hour);
|
|
|
|
BCD_TO_BIN(day);
|
|
|
|
BCD_TO_BIN(mon);
|
|
|
|
BCD_TO_BIN(year);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-02-03 00:48:19 +08:00
|
|
|
if (century) {
|
|
|
|
BCD_TO_BIN(century);
|
|
|
|
year += century * 100;
|
|
|
|
printk(KERN_INFO "Extended CMOS year: %d\n", century * 100);
|
2006-03-25 23:30:31 +08:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* x86-64 systems only exists since 2002.
|
|
|
|
* This will work up to Dec 31, 2100
|
|
|
|
*/
|
|
|
|
year += 2000;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return mktime(year, mon, day, hour, min, sec);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* pit_calibrate_tsc() uses the speaker output (channel 2) of
|
|
|
|
* the PIT. This is better than using the timer interrupt output,
|
|
|
|
* because we can read the value of the speaker with just one inb(),
|
|
|
|
* where we need three i/o operations for the interrupt channel.
|
|
|
|
* We count how many ticks the TSC does in 50 ms.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static unsigned int __init pit_calibrate_tsc(void)
|
|
|
|
{
|
|
|
|
unsigned long start, end;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&i8253_lock, flags);
|
|
|
|
|
|
|
|
outb((inb(0x61) & ~0x02) | 0x01, 0x61);
|
|
|
|
|
|
|
|
outb(0xb0, 0x43);
|
|
|
|
outb((PIT_TICK_RATE / (1000 / 50)) & 0xff, 0x42);
|
|
|
|
outb((PIT_TICK_RATE / (1000 / 50)) >> 8, 0x42);
|
2006-01-12 05:45:24 +08:00
|
|
|
start = get_cycles_sync();
|
2005-04-17 06:20:36 +08:00
|
|
|
while ((inb(0x61) & 0x20) == 0);
|
2006-01-12 05:45:24 +08:00
|
|
|
end = get_cycles_sync();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
spin_unlock_irqrestore(&i8253_lock, flags);
|
|
|
|
|
|
|
|
return (end - start) / 50;
|
|
|
|
}
|
|
|
|
|
2006-02-04 04:50:50 +08:00
|
|
|
#define PIT_MODE 0x43
|
|
|
|
#define PIT_CH0 0x40
|
|
|
|
|
|
|
|
static void __init __pit_init(int val, u8 mode)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
spin_lock_irqsave(&i8253_lock, flags);
|
2006-02-04 04:50:50 +08:00
|
|
|
outb_p(mode, PIT_MODE);
|
|
|
|
outb_p(val & 0xff, PIT_CH0); /* LSB */
|
|
|
|
outb_p(val >> 8, PIT_CH0); /* MSB */
|
2005-04-17 06:20:36 +08:00
|
|
|
spin_unlock_irqrestore(&i8253_lock, flags);
|
|
|
|
}
|
|
|
|
|
2006-02-04 04:50:50 +08:00
|
|
|
void __init pit_init(void)
|
|
|
|
{
|
|
|
|
__pit_init(LATCH, 0x34); /* binary, mode 2, LSB/MSB, ch 0 */
|
|
|
|
}
|
|
|
|
|
|
|
|
void __init pit_stop_interrupt(void)
|
|
|
|
{
|
|
|
|
__pit_init(0, 0x30); /* mode 0 */
|
|
|
|
}
|
|
|
|
|
|
|
|
void __init stop_timer_interrupt(void)
|
|
|
|
{
|
|
|
|
char *name;
|
2007-02-16 17:28:18 +08:00
|
|
|
if (hpet_address) {
|
2006-02-04 04:50:50 +08:00
|
|
|
name = "HPET";
|
|
|
|
hpet_timer_stop_set_go(0);
|
|
|
|
} else {
|
|
|
|
name = "PIT";
|
|
|
|
pit_stop_interrupt();
|
|
|
|
}
|
|
|
|
printk(KERN_INFO "timer: %s interrupt stopped.\n", name);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct irqaction irq0 = {
|
2006-07-02 10:29:29 +08:00
|
|
|
timer_interrupt, IRQF_DISABLED, CPU_MASK_NONE, "timer", NULL, NULL
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2006-09-26 16:52:28 +08:00
|
|
|
void __init time_init(void)
|
|
|
|
{
|
2005-04-17 06:20:36 +08:00
|
|
|
if (nohpet)
|
2007-02-16 17:28:18 +08:00
|
|
|
hpet_address = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
xtime.tv_sec = get_cmos_time();
|
|
|
|
xtime.tv_nsec = 0;
|
|
|
|
|
|
|
|
set_normalized_timespec(&wall_to_monotonic,
|
|
|
|
-xtime.tv_sec, -xtime.tv_nsec);
|
|
|
|
|
2007-02-16 17:28:20 +08:00
|
|
|
if (hpet_arch_init())
|
2007-02-16 17:28:18 +08:00
|
|
|
hpet_address = 0;
|
2005-06-23 15:08:36 +08:00
|
|
|
|
|
|
|
if (hpet_use_timer) {
|
2006-04-08 01:50:18 +08:00
|
|
|
/* set tick_nsec to use the proper rate for HPET */
|
|
|
|
tick_nsec = TICK_NSEC_HPET;
|
2005-04-17 06:20:36 +08:00
|
|
|
cpu_khz = hpet_calibrate_tsc();
|
|
|
|
timename = "HPET";
|
|
|
|
} else {
|
|
|
|
pit_init();
|
|
|
|
cpu_khz = pit_calibrate_tsc();
|
|
|
|
timename = "PIT";
|
|
|
|
}
|
|
|
|
|
2005-05-17 12:53:28 +08:00
|
|
|
if (unsynchronized_tsc())
|
2007-02-16 17:28:20 +08:00
|
|
|
mark_tsc_unstable();
|
2006-09-26 16:52:28 +08:00
|
|
|
|
2007-02-16 17:28:18 +08:00
|
|
|
if (cpu_has(&boot_cpu_data, X86_FEATURE_RDTSCP))
|
2006-09-26 16:52:28 +08:00
|
|
|
vgetcpu_mode = VGETCPU_RDTSCP;
|
|
|
|
else
|
|
|
|
vgetcpu_mode = VGETCPU_LSL;
|
|
|
|
|
2007-02-16 17:28:20 +08:00
|
|
|
set_cyc2ns_scale(cpu_khz);
|
2006-09-26 16:52:28 +08:00
|
|
|
printk(KERN_INFO "time.c: Detected %d.%03d MHz processor.\n",
|
|
|
|
cpu_khz / 1000, cpu_khz % 1000);
|
2007-03-05 16:30:50 +08:00
|
|
|
init_tsc_clocksource();
|
|
|
|
|
2007-02-16 17:28:20 +08:00
|
|
|
setup_irq(0, &irq0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static long clock_cmos_diff;
|
|
|
|
static unsigned long sleep_start;
|
|
|
|
|
2006-01-12 05:45:33 +08:00
|
|
|
/*
|
|
|
|
* sysfs support for the timer.
|
|
|
|
*/
|
|
|
|
|
2005-04-17 06:25:31 +08:00
|
|
|
static int timer_suspend(struct sys_device *dev, pm_message_t state)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Estimate time zone so that set_time can update the clock
|
|
|
|
*/
|
|
|
|
long cmos_time = get_cmos_time();
|
|
|
|
|
|
|
|
clock_cmos_diff = -cmos_time;
|
|
|
|
clock_cmos_diff += get_seconds();
|
|
|
|
sleep_start = cmos_time;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int timer_resume(struct sys_device *dev)
|
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
unsigned long sec;
|
|
|
|
unsigned long ctime = get_cmos_time();
|
2006-09-26 16:52:37 +08:00
|
|
|
long sleep_length = (ctime - sleep_start) * HZ;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-09-26 16:52:37 +08:00
|
|
|
if (sleep_length < 0) {
|
|
|
|
printk(KERN_WARNING "Time skew detected in timer resume!\n");
|
|
|
|
/* The time after the resume must not be earlier than the time
|
|
|
|
* before the suspend or some nasty things will happen
|
|
|
|
*/
|
|
|
|
sleep_length = 0;
|
|
|
|
ctime = sleep_start;
|
|
|
|
}
|
2007-02-16 17:28:18 +08:00
|
|
|
if (hpet_address)
|
2005-04-17 06:20:36 +08:00
|
|
|
hpet_reenable();
|
|
|
|
else
|
|
|
|
i8254_timer_resume();
|
|
|
|
|
|
|
|
sec = ctime + clock_cmos_diff;
|
|
|
|
write_seqlock_irqsave(&xtime_lock,flags);
|
|
|
|
xtime.tv_sec = sec;
|
|
|
|
xtime.tv_nsec = 0;
|
|
|
|
jiffies += sleep_length;
|
2007-02-16 17:28:20 +08:00
|
|
|
write_sequnlock_irqrestore(&xtime_lock,flags);
|
2005-09-07 06:16:27 +08:00
|
|
|
touch_softlockup_watchdog();
|
2005-04-17 06:20:36 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct sysdev_class timer_sysclass = {
|
|
|
|
.resume = timer_resume,
|
|
|
|
.suspend = timer_suspend,
|
|
|
|
set_kset_name("timer"),
|
|
|
|
};
|
|
|
|
|
2007-02-18 02:13:42 +08:00
|
|
|
/* XXX this sysfs stuff should probably go elsewhere later -john */
|
2005-04-17 06:20:36 +08:00
|
|
|
static struct sys_device device_timer = {
|
|
|
|
.id = 0,
|
|
|
|
.cls = &timer_sysclass,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int time_init_device(void)
|
|
|
|
{
|
|
|
|
int error = sysdev_class_register(&timer_sysclass);
|
|
|
|
if (!error)
|
|
|
|
error = sysdev_register(&device_timer);
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
device_initcall(time_init_device);
|