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>
|
2007-10-13 05:04:07 +08:00
|
|
|
#include <linux/clockchips.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
|
2007-07-21 23:11:18 +08:00
|
|
|
#include <asm/i8253.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#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>
|
2007-05-03 01:27:06 +08:00
|
|
|
#include <asm/nmi.h>
|
2007-07-21 23:10:01 +08:00
|
|
|
#include <asm/vgtod.h>
|
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
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2007-07-21 23:10:11 +08:00
|
|
|
static int set_rtc_mmss(unsigned long nowtime)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2007-07-21 23:10:11 +08:00
|
|
|
int retval = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
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);
|
2007-07-21 23:10:11 +08:00
|
|
|
retval = -1;
|
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);
|
2007-07-21 23:10:11 +08:00
|
|
|
|
|
|
|
return retval;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-07-21 23:10:11 +08:00
|
|
|
int update_persistent_clock(struct timespec now)
|
|
|
|
{
|
|
|
|
return set_rtc_mmss(now.tv_sec);
|
|
|
|
}
|
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
|
|
|
void main_timer_handler(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2007-10-13 05:04:07 +08:00
|
|
|
static irqreturn_t timer_event_interrupt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
global_clock_event->event_handler(global_clock_event);
|
|
|
|
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
2007-07-21 23:10:12 +08:00
|
|
|
unsigned long read_persistent_clock(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.
|
2007-07-21 23:10:18 +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);
|
2007-07-21 23:10:18 +08:00
|
|
|
} else {
|
2006-03-25 23:30:31 +08:00
|
|
|
/*
|
|
|
|
* x86-64 systems only exists since 2002.
|
|
|
|
* This will work up to Dec 31, 2100
|
2007-07-21 23:10:18 +08:00
|
|
|
*/
|
2006-03-25 23:30:31 +08:00
|
|
|
year += 2000;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return mktime(year, mon, day, hour, min, sec);
|
|
|
|
}
|
|
|
|
|
2007-05-03 01:27:06 +08:00
|
|
|
/* calibrate_cpu is used on systems with fixed rate TSCs to determine
|
|
|
|
* processor frequency */
|
|
|
|
#define TICK_COUNT 100000000
|
|
|
|
static unsigned int __init tsc_calibrate_cpu_khz(void)
|
|
|
|
{
|
2007-07-21 23:10:18 +08:00
|
|
|
int tsc_start, tsc_now;
|
|
|
|
int i, no_ctr_free;
|
|
|
|
unsigned long evntsel3 = 0, pmc3 = 0, pmc_now = 0;
|
|
|
|
unsigned long flags;
|
|
|
|
|
|
|
|
for (i = 0; i < 4; i++)
|
|
|
|
if (avail_to_resrv_perfctr_nmi_bit(i))
|
|
|
|
break;
|
|
|
|
no_ctr_free = (i == 4);
|
|
|
|
if (no_ctr_free) {
|
|
|
|
i = 3;
|
|
|
|
rdmsrl(MSR_K7_EVNTSEL3, evntsel3);
|
|
|
|
wrmsrl(MSR_K7_EVNTSEL3, 0);
|
|
|
|
rdmsrl(MSR_K7_PERFCTR3, pmc3);
|
|
|
|
} else {
|
|
|
|
reserve_perfctr_nmi(MSR_K7_PERFCTR0 + i);
|
|
|
|
reserve_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
|
|
|
|
}
|
|
|
|
local_irq_save(flags);
|
|
|
|
/* start meauring cycles, incrementing from 0 */
|
|
|
|
wrmsrl(MSR_K7_PERFCTR0 + i, 0);
|
|
|
|
wrmsrl(MSR_K7_EVNTSEL0 + i, 1 << 22 | 3 << 16 | 0x76);
|
|
|
|
rdtscl(tsc_start);
|
|
|
|
do {
|
|
|
|
rdmsrl(MSR_K7_PERFCTR0 + i, pmc_now);
|
|
|
|
tsc_now = get_cycles_sync();
|
|
|
|
} while ((tsc_now - tsc_start) < TICK_COUNT);
|
|
|
|
|
|
|
|
local_irq_restore(flags);
|
|
|
|
if (no_ctr_free) {
|
|
|
|
wrmsrl(MSR_K7_EVNTSEL3, 0);
|
|
|
|
wrmsrl(MSR_K7_PERFCTR3, pmc3);
|
|
|
|
wrmsrl(MSR_K7_EVNTSEL3, evntsel3);
|
|
|
|
} else {
|
|
|
|
release_perfctr_nmi(MSR_K7_PERFCTR0 + i);
|
|
|
|
release_evntsel_nmi(MSR_K7_EVNTSEL0 + i);
|
|
|
|
}
|
|
|
|
|
|
|
|
return pmc_now * tsc_khz / (tsc_now - tsc_start);
|
2007-05-03 01:27:06 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
static struct irqaction irq0 = {
|
2007-10-13 05:04:07 +08:00
|
|
|
.handler = timer_event_interrupt,
|
2007-10-13 05:04:06 +08:00
|
|
|
.flags = IRQF_DISABLED | IRQF_IRQPOLL | IRQF_NOBALANCING,
|
2007-05-08 15:35:28 +08:00
|
|
|
.mask = CPU_MASK_NONE,
|
2007-07-21 23:10:18 +08:00
|
|
|
.name = "timer"
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
2006-09-26 16:52:28 +08:00
|
|
|
void __init time_init(void)
|
|
|
|
{
|
2007-10-13 05:04:07 +08:00
|
|
|
if (!hpet_enable())
|
|
|
|
setup_pit_timer();
|
2005-06-23 15:08:36 +08:00
|
|
|
|
2007-10-13 05:04:07 +08:00
|
|
|
setup_irq(0, &irq0);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2007-10-13 05:04:06 +08:00
|
|
|
tsc_calibrate();
|
|
|
|
|
2007-05-03 01:27:06 +08:00
|
|
|
cpu_khz = tsc_khz;
|
|
|
|
if (cpu_has(&boot_cpu_data, X86_FEATURE_CONSTANT_TSC) &&
|
|
|
|
boot_cpu_data.x86_vendor == X86_VENDOR_AMD &&
|
|
|
|
boot_cpu_data.x86 == 16)
|
|
|
|
cpu_khz = tsc_calibrate_cpu_khz();
|
|
|
|
|
2005-05-17 12:53:28 +08:00
|
|
|
if (unsynchronized_tsc())
|
2007-05-03 01:27:08 +08:00
|
|
|
mark_tsc_unstable("TSCs unsynchronized");
|
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-05-03 01:27:06 +08:00
|
|
|
set_cyc2ns_scale(tsc_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();
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|