mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-02 07:04:24 +00:00
ARM: riscpc: replace gettimeoffset() with clocksource
Replace the old gettimeoffset() interface (which became buggy in several ways) with a clocksource that atomically reads the count and status from the timer, and corrects the count as appropriate ensuring proper resolution of time without time warping backwards. We keep the original periodic timer non-clock event implementation to provide the kernel with a regular source of interrupts, which are required to keep the clocksource properly updated. Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
This commit is contained in:
parent
e93c9c99a6
commit
a44c1d700c
2 changed files with 25 additions and 14 deletions
|
@ -528,7 +528,6 @@ config ARCH_RPC
|
||||||
select ARCH_ACORN
|
select ARCH_ACORN
|
||||||
select ARCH_MAY_HAVE_PC_FDC
|
select ARCH_MAY_HAVE_PC_FDC
|
||||||
select ARCH_SPARSEMEM_ENABLE
|
select ARCH_SPARSEMEM_ENABLE
|
||||||
select ARCH_USES_GETTIMEOFFSET
|
|
||||||
select CPU_SA110
|
select CPU_SA110
|
||||||
select FIQ
|
select FIQ
|
||||||
select HAVE_IDE
|
select HAVE_IDE
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
* 04-Dec-1997 RMK Updated for new arch/arm/time.c
|
* 04-Dec-1997 RMK Updated for new arch/arm/time.c
|
||||||
* 13=Jun-2004 DS Moved to arch/arm/common b/c shared w/CLPS7500
|
* 13=Jun-2004 DS Moved to arch/arm/common b/c shared w/CLPS7500
|
||||||
*/
|
*/
|
||||||
#include <linux/timex.h>
|
#include <linux/clocksource.h>
|
||||||
#include <linux/init.h>
|
#include <linux/init.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/irq.h>
|
#include <linux/irq.h>
|
||||||
|
@ -27,11 +27,15 @@
|
||||||
#define RPC_CLOCK_FREQ 2000000
|
#define RPC_CLOCK_FREQ 2000000
|
||||||
#define RPC_LATCH DIV_ROUND_CLOSEST(RPC_CLOCK_FREQ, HZ)
|
#define RPC_LATCH DIV_ROUND_CLOSEST(RPC_CLOCK_FREQ, HZ)
|
||||||
|
|
||||||
static u32 ioc_timer_gettimeoffset(void)
|
static u32 ioc_time;
|
||||||
|
|
||||||
|
static u64 ioc_timer_read(struct clocksource *cs)
|
||||||
{
|
{
|
||||||
unsigned int count1, count2, status;
|
unsigned int count1, count2, status;
|
||||||
long offset;
|
unsigned long flags;
|
||||||
|
u32 ticks;
|
||||||
|
|
||||||
|
local_irq_save(flags);
|
||||||
ioc_writeb (0, IOC_T0LATCH);
|
ioc_writeb (0, IOC_T0LATCH);
|
||||||
barrier ();
|
barrier ();
|
||||||
count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
count1 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
||||||
|
@ -41,27 +45,34 @@ static u32 ioc_timer_gettimeoffset(void)
|
||||||
ioc_writeb (0, IOC_T0LATCH);
|
ioc_writeb (0, IOC_T0LATCH);
|
||||||
barrier ();
|
barrier ();
|
||||||
count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
count2 = ioc_readb(IOC_T0CNTL) | (ioc_readb(IOC_T0CNTH) << 8);
|
||||||
|
ticks = ioc_time + RPC_LATCH - count2;
|
||||||
|
local_irq_restore(flags);
|
||||||
|
|
||||||
offset = count2;
|
|
||||||
if (count2 < count1) {
|
if (count2 < count1) {
|
||||||
/*
|
/*
|
||||||
* We have not had an interrupt between reading count1
|
* The timer has not reloaded between reading count1 and
|
||||||
* and count2.
|
* count2, check whether an interrupt was actually pending.
|
||||||
*/
|
*/
|
||||||
if (status & (1 << 5))
|
if (status & (1 << 5))
|
||||||
offset -= RPC_LATCH;
|
ticks += RPC_LATCH;
|
||||||
} else if (count2 > count1) {
|
} else if (count2 > count1) {
|
||||||
/*
|
/*
|
||||||
* We have just had another interrupt between reading
|
* The timer has reloaded, so count2 indicates the new
|
||||||
* count1 and count2.
|
* count since the wrap. The interrupt would not have
|
||||||
|
* been processed, so add the missed ticks.
|
||||||
*/
|
*/
|
||||||
offset -= RPC_LATCH;
|
ticks += RPC_LATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
offset = (RPC_LATCH - offset) * (tick_nsec / 1000);
|
return ticks;
|
||||||
return DIV_ROUND_CLOSEST(offset, RPC_LATCH) * 1000;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct clocksource ioctime_clocksource = {
|
||||||
|
.read = ioc_timer_read,
|
||||||
|
.mask = CLOCKSOURCE_MASK(32),
|
||||||
|
.rating = 100,
|
||||||
|
};
|
||||||
|
|
||||||
void __init ioctime_init(void)
|
void __init ioctime_init(void)
|
||||||
{
|
{
|
||||||
ioc_writeb(RPC_LATCH & 255, IOC_T0LTCHL);
|
ioc_writeb(RPC_LATCH & 255, IOC_T0LTCHL);
|
||||||
|
@ -72,6 +83,7 @@ void __init ioctime_init(void)
|
||||||
static irqreturn_t
|
static irqreturn_t
|
||||||
ioc_timer_interrupt(int irq, void *dev_id)
|
ioc_timer_interrupt(int irq, void *dev_id)
|
||||||
{
|
{
|
||||||
|
ioc_time += RPC_LATCH;
|
||||||
timer_tick();
|
timer_tick();
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
@ -86,7 +98,7 @@ static struct irqaction ioc_timer_irq = {
|
||||||
*/
|
*/
|
||||||
void __init ioc_timer_init(void)
|
void __init ioc_timer_init(void)
|
||||||
{
|
{
|
||||||
arch_gettimeoffset = ioc_timer_gettimeoffset;
|
WARN_ON(clocksource_register_hz(&ioctime_clocksource, RPC_CLOCK_FREQ));
|
||||||
ioctime_init();
|
ioctime_init();
|
||||||
setup_irq(IRQ_TIMER0, &ioc_timer_irq);
|
setup_irq(IRQ_TIMER0, &ioc_timer_irq);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue