blob: 3327d302618d652099bfb2d90edd78d2963258aa [file] [log] [blame]
Uwe Kleine-König9918cda2007-02-16 15:36:55 +01001/*
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
22static u32 usecs_per_tick;
23
24static irqreturn_t
25ns9xxx_timer_interrupt(int irq, void *dev_id)
26{
Uwe Kleine-Königbf62e862007-08-07 21:08:21 +010027 int timerno = irq - IRQ_TIMER0;
28 u32 tc;
29
Uwe Kleine-König9918cda2007-02-16 15:36:55 +010030 write_seqlock(&xtime_lock);
31 timer_tick();
32 write_sequnlock(&xtime_lock);
33
Uwe Kleine-Königbf62e862007-08-07 21:08:21 +010034 /* 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
Uwe Kleine-König9918cda2007-02-16 15:36:55 +010045 return IRQ_HANDLED;
46}
47
48static 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
Simon Arlott6cbdc8c2007-05-11 20:40:30 +010052 * reloads, return one period more. */
Uwe Kleine-König9918cda2007-02-16 15:36:55 +010053
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
68static struct irqaction ns9xxx_timer_irq = {
69 .name = "NS9xxx Timer Tick",
Bernhard Walleb30faba2007-05-08 00:35:39 -070070 .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,
Uwe Kleine-König9918cda2007-02-16 15:36:55 +010071 .handler = ns9xxx_timer_interrupt,
72};
73
74static 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
99struct sys_timer ns9xxx_timer = {
100 .init = ns9xxx_timer_init,
101 .offset = ns9xxx_timer_gettimeoffset,
102};