bf62e86269
Now the drivers are responsible to clear the irq in the respective device, which seems to be the normal thing to do. So the ack'ing of the timer irq moved to time.c. Signed-off-by: Uwe Kleine-König <ukleinek@informatik.uni-freiburg.de> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
103 lines
2.4 KiB
C
103 lines
2.4 KiB
C
/*
|
|
* arch/arm/mach-ns9xxx/time.c
|
|
*
|
|
* Copyright (C) 2006 by Digi International Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 as published by
|
|
* the Free Software Foundation.
|
|
*/
|
|
#include <linux/jiffies.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <asm/arch-ns9xxx/regs-sys.h>
|
|
#include <asm/arch-ns9xxx/clock.h>
|
|
#include <asm/arch-ns9xxx/irqs.h>
|
|
#include <asm/arch/system.h>
|
|
#include "generic.h"
|
|
|
|
#define TIMERCLOCKSELECT 64
|
|
|
|
static u32 usecs_per_tick;
|
|
|
|
static irqreturn_t
|
|
ns9xxx_timer_interrupt(int irq, void *dev_id)
|
|
{
|
|
int timerno = irq - IRQ_TIMER0;
|
|
u32 tc;
|
|
|
|
write_seqlock(&xtime_lock);
|
|
timer_tick();
|
|
write_sequnlock(&xtime_lock);
|
|
|
|
/* clear irq */
|
|
tc = SYS_TC(timerno);
|
|
if (REGGET(tc, SYS_TCx, REN) == SYS_TCx_REN_DIS) {
|
|
REGSET(tc, SYS_TCx, TEN, DIS);
|
|
SYS_TC(timerno) = tc;
|
|
}
|
|
REGSET(tc, SYS_TCx, INTC, SET);
|
|
SYS_TC(timerno) = tc;
|
|
REGSET(tc, SYS_TCx, INTC, UNSET);
|
|
SYS_TC(timerno) = tc;
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static unsigned long ns9xxx_timer_gettimeoffset(void)
|
|
{
|
|
/* return the microseconds which have passed since the last interrupt
|
|
* was _serviced_. That is, if an interrupt is pending or the counter
|
|
* reloads, return one period more. */
|
|
|
|
u32 counter1 = SYS_TR(0);
|
|
int pending = SYS_ISR & (1 << IRQ_TIMER0);
|
|
u32 counter2 = SYS_TR(0);
|
|
u32 elapsed;
|
|
|
|
if (pending || counter2 > counter1)
|
|
elapsed = 2 * SYS_TRC(0) - counter2;
|
|
else
|
|
elapsed = SYS_TRC(0) - counter1;
|
|
|
|
return (elapsed * usecs_per_tick) >> 16;
|
|
|
|
}
|
|
|
|
static struct irqaction ns9xxx_timer_irq = {
|
|
.name = "NS9xxx Timer Tick",
|
|
.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
|
|
.handler = ns9xxx_timer_interrupt,
|
|
};
|
|
|
|
static void __init ns9xxx_timer_init(void)
|
|
{
|
|
int tc;
|
|
|
|
usecs_per_tick =
|
|
SH_DIV(1000000 * TIMERCLOCKSELECT, ns9xxx_cpuclock(), 16);
|
|
|
|
/* disable timer */
|
|
if ((tc = SYS_TC(0)) & SYS_TCx_TEN)
|
|
SYS_TC(0) = tc & ~SYS_TCx_TEN;
|
|
|
|
SYS_TRC(0) = SH_DIV(ns9xxx_cpuclock(), (TIMERCLOCKSELECT * HZ), 0);
|
|
|
|
REGSET(tc, SYS_TCx, TEN, EN);
|
|
REGSET(tc, SYS_TCx, TLCS, DIV64); /* This must match TIMERCLOCKSELECT */
|
|
REGSET(tc, SYS_TCx, INTS, EN);
|
|
REGSET(tc, SYS_TCx, UDS, DOWN);
|
|
REGSET(tc, SYS_TCx, TDBG, STOP);
|
|
REGSET(tc, SYS_TCx, TSZ, 32);
|
|
REGSET(tc, SYS_TCx, REN, EN);
|
|
SYS_TC(0) = tc;
|
|
|
|
setup_irq(IRQ_TIMER0, &ns9xxx_timer_irq);
|
|
}
|
|
|
|
struct sys_timer ns9xxx_timer = {
|
|
.init = ns9xxx_timer_init,
|
|
.offset = ns9xxx_timer_gettimeoffset,
|
|
};
|