]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - arch/arm/mach-ns9xxx/time.c
[ARM] 4545/1: ns9xxx: simplify irq ack'ing
[linux-2.6-omap-h63xx.git] / arch / arm / mach-ns9xxx / time.c
1 /*
2  * arch/arm/mach-ns9xxx/time.c
3  *
4  * Copyright (C) 2006 by Digi International Inc.
5  * All rights reserved.
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License version 2 as published by
9  * the Free Software Foundation.
10  */
11 #include <linux/jiffies.h>
12 #include <linux/interrupt.h>
13 #include <linux/irq.h>
14 #include <asm/arch-ns9xxx/regs-sys.h>
15 #include <asm/arch-ns9xxx/clock.h>
16 #include <asm/arch-ns9xxx/irqs.h>
17 #include <asm/arch/system.h>
18 #include "generic.h"
19
20 #define TIMERCLOCKSELECT 64
21
22 static u32 usecs_per_tick;
23
24 static irqreturn_t
25 ns9xxx_timer_interrupt(int irq, void *dev_id)
26 {
27         int timerno = irq - IRQ_TIMER0;
28         u32 tc;
29
30         write_seqlock(&xtime_lock);
31         timer_tick();
32         write_sequnlock(&xtime_lock);
33
34         /* clear irq */
35         tc = SYS_TC(timerno);
36         if (REGGET(tc, SYS_TCx, REN) == SYS_TCx_REN_DIS) {
37                 REGSET(tc, SYS_TCx, TEN, DIS);
38                 SYS_TC(timerno) = tc;
39         }
40         REGSET(tc, SYS_TCx, INTC, SET);
41         SYS_TC(timerno) = tc;
42         REGSET(tc, SYS_TCx, INTC, UNSET);
43         SYS_TC(timerno) = tc;
44
45         return IRQ_HANDLED;
46 }
47
48 static unsigned long ns9xxx_timer_gettimeoffset(void)
49 {
50         /* return the microseconds which have passed since the last interrupt
51          * was _serviced_.  That is, if an interrupt is pending or the counter
52          * reloads, return one period more. */
53
54         u32 counter1 = SYS_TR(0);
55         int pending = SYS_ISR & (1 << IRQ_TIMER0);
56         u32 counter2 = SYS_TR(0);
57         u32 elapsed;
58
59         if (pending || counter2 > counter1)
60                 elapsed = 2 * SYS_TRC(0) - counter2;
61         else
62                 elapsed = SYS_TRC(0) - counter1;
63
64         return (elapsed * usecs_per_tick) >> 16;
65
66 }
67
68 static struct irqaction ns9xxx_timer_irq = {
69         .name = "NS9xxx Timer Tick",
70         .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
71         .handler = ns9xxx_timer_interrupt,
72 };
73
74 static void __init ns9xxx_timer_init(void)
75 {
76         int tc;
77
78         usecs_per_tick =
79                 SH_DIV(1000000 * TIMERCLOCKSELECT, ns9xxx_cpuclock(), 16);
80
81         /* disable timer */
82         if ((tc = SYS_TC(0)) & SYS_TCx_TEN)
83                 SYS_TC(0) = tc & ~SYS_TCx_TEN;
84
85         SYS_TRC(0) = SH_DIV(ns9xxx_cpuclock(), (TIMERCLOCKSELECT * HZ), 0);
86
87         REGSET(tc, SYS_TCx, TEN, EN);
88         REGSET(tc, SYS_TCx, TLCS, DIV64); /* This must match TIMERCLOCKSELECT */
89         REGSET(tc, SYS_TCx, INTS, EN);
90         REGSET(tc, SYS_TCx, UDS, DOWN);
91         REGSET(tc, SYS_TCx, TDBG, STOP);
92         REGSET(tc, SYS_TCx, TSZ, 32);
93         REGSET(tc, SYS_TCx, REN, EN);
94         SYS_TC(0) = tc;
95
96         setup_irq(IRQ_TIMER0, &ns9xxx_timer_irq);
97 }
98
99 struct sys_timer ns9xxx_timer = {
100         .init = ns9xxx_timer_init,
101         .offset = ns9xxx_timer_gettimeoffset,
102 };