ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / arm / mach-iop3xx / iq80310-time.c
1 /*
2  * linux/arch/arm/mach-iop3xx/time-iq80310.c
3  *
4  * Timer functions for IQ80310 onboard timer
5  *
6  * Author:  Nicolas Pitre
7  * Copyright:   (C) 2001 MontaVista Software Inc.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  */
14 #include <linux/kernel.h>
15 #include <linux/interrupt.h>
16 #include <linux/time.h>
17 #include <linux/init.h>
18 #include <linux/timex.h>
19
20 #include <asm/hardware.h>
21 #include <asm/io.h>
22 #include <asm/irq.h>
23 #include <asm/uaccess.h>
24 #include <asm/mach-types.h>
25 #include <asm/mach/irq.h>
26
27 static void iq80310_write_timer (u_long val)
28 {
29         volatile u_char *la0 = (volatile u_char *)IQ80310_TIMER_LA0;
30         volatile u_char *la1 = (volatile u_char *)IQ80310_TIMER_LA1;
31         volatile u_char *la2 = (volatile u_char *)IQ80310_TIMER_LA2;
32
33         *la0 = val;
34         *la1 = val >> 8;
35         *la2 = (val >> 16) & 0x3f;
36 }
37
38 static u_long iq80310_read_timer (void)
39 {
40         volatile u_char *la0 = (volatile u_char *)IQ80310_TIMER_LA0;
41         volatile u_char *la1 = (volatile u_char *)IQ80310_TIMER_LA1;
42         volatile u_char *la2 = (volatile u_char *)IQ80310_TIMER_LA2;
43         volatile u_char *la3 = (volatile u_char *)IQ80310_TIMER_LA3;
44         u_long b0, b1, b2, b3, val;
45
46         b0 = *la0; b1 = *la1; b2 = *la2; b3 = *la3;
47         b0 = (((b0 & 0x40) >> 1) | (b0 & 0x1f));
48         b1 = (((b1 & 0x40) >> 1) | (b1 & 0x1f));
49         b2 = (((b2 & 0x40) >> 1) | (b2 & 0x1f));
50         b3 = (b3 & 0x0f);
51         val = ((b0 << 0) | (b1 << 6) | (b2 << 12) | (b3 << 18));
52         return val;
53 }
54
55 /*
56  * IRQs are disabled before entering here from do_gettimeofday().
57  * Note that the counter may wrap.  When it does, 'elapsed' will
58  * be small, but we will have a pending interrupt.
59  */
60 static unsigned long iq80310_gettimeoffset (void)
61 {
62         unsigned long elapsed, usec;
63         unsigned int stat1, stat2;
64
65         stat1 = *(volatile u8 *)IQ80310_INT_STAT;
66         elapsed = iq80310_read_timer();
67         stat2 = *(volatile u8 *)IQ80310_INT_STAT;
68
69         /*
70          * If an interrupt was pending before we read the timer,
71          * we've already wrapped.  Factor this into the time.
72          * If an interrupt was pending after we read the timer,
73          * it may have wrapped between checking the interrupt
74          * status and reading the timer.  Re-read the timer to
75          * be sure its value is after the wrap.
76          */
77         if (stat1 & 1)
78                 elapsed += LATCH;
79         else if (stat2 & 1)
80                 elapsed = LATCH + iq80310_read_timer();
81
82         /*
83          * Now convert them to usec.
84          */
85         usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;
86
87         return usec;
88 }
89
90
91 static irqreturn_t
92 iq80310_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
93 {
94         volatile u_char *timer_en = (volatile u_char *)IQ80310_TIMER_EN;
95
96         /* clear timer interrupt */
97         *timer_en &= ~2;
98         *timer_en |= 2;
99
100         do_timer(regs);
101
102         return IRQ_HANDLED;
103 }
104
105 extern unsigned long (*gettimeoffset)(void);
106
107 static struct irqaction timer_irq = {
108         .name           = "timer",
109         .handler        = iq80310_timer_interrupt,
110 };
111
112
113 void __init time_init(void)
114 {
115         volatile u_char *timer_en = (volatile u_char *)IQ80310_TIMER_EN;
116
117         gettimeoffset = iq80310_gettimeoffset;
118
119         setup_irq(IRQ_IQ80310_TIMER, &timer_irq);
120
121         *timer_en = 0;
122         iq80310_write_timer(LATCH);
123         *timer_en |= 2;
124         *timer_en |= 1;
125 }