# BRCM_VERSION=3
[bcm963xx.git] / kernel / linux / 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/irq.h>
25
26 static void iq80310_write_timer (u_long val)
27 {
28         volatile u_char *la0 = (volatile u_char *)IQ80310_TIMER_LA0;
29         volatile u_char *la1 = (volatile u_char *)IQ80310_TIMER_LA1;
30         volatile u_char *la2 = (volatile u_char *)IQ80310_TIMER_LA2;
31
32         *la0 = val;
33         *la1 = val >> 8;
34         *la2 = (val >> 16) & 0x3f;
35 }
36
37 static u_long iq80310_read_timer (void)
38 {
39         volatile u_char *la0 = (volatile u_char *)IQ80310_TIMER_LA0;
40         volatile u_char *la1 = (volatile u_char *)IQ80310_TIMER_LA1;
41         volatile u_char *la2 = (volatile u_char *)IQ80310_TIMER_LA2;
42         volatile u_char *la3 = (volatile u_char *)IQ80310_TIMER_LA3;
43         u_long b0, b1, b2, b3, val;
44
45         b0 = *la0; b1 = *la1; b2 = *la2; b3 = *la3;
46         b0 = (((b0 & 0x40) >> 1) | (b0 & 0x1f));
47         b1 = (((b1 & 0x40) >> 1) | (b1 & 0x1f));
48         b2 = (((b2 & 0x40) >> 1) | (b2 & 0x1f));
49         b3 = (b3 & 0x0f);
50         val = ((b0 << 0) | (b1 << 6) | (b2 << 12) | (b3 << 18));
51         return val;
52 }
53
54 /*
55  * IRQs are disabled before entering here from do_gettimeofday().
56  * Note that the counter may wrap.  When it does, 'elapsed' will
57  * be small, but we will have a pending interrupt.
58  */
59 static unsigned long iq80310_gettimeoffset (void)
60 {
61         unsigned long elapsed, usec;
62         unsigned int stat1, stat2;
63
64         stat1 = *(volatile u8 *)IQ80310_INT_STAT;
65         elapsed = iq80310_read_timer();
66         stat2 = *(volatile u8 *)IQ80310_INT_STAT;
67
68         /*
69          * If an interrupt was pending before we read the timer,
70          * we've already wrapped.  Factor this into the time.
71          * If an interrupt was pending after we read the timer,
72          * it may have wrapped between checking the interrupt
73          * status and reading the timer.  Re-read the timer to
74          * be sure its value is after the wrap.
75          */
76         if (stat1 & 1)
77                 elapsed += LATCH;
78         else if (stat2 & 1)
79                 elapsed = LATCH + iq80310_read_timer();
80
81         /*
82          * Now convert them to usec.
83          */
84         usec = (unsigned long)(elapsed * (tick_nsec / 1000))/LATCH;
85
86         return usec;
87 }
88
89
90 static irqreturn_t
91 iq80310_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
92 {
93         volatile u_char *timer_en = (volatile u_char *)IQ80310_TIMER_EN;
94
95         /* clear timer interrupt */
96         *timer_en &= ~2;
97         *timer_en |= 2;
98
99         do_timer(regs);
100
101         return IRQ_HANDLED;
102 }
103
104 extern unsigned long (*gettimeoffset)(void);
105
106 static struct irqaction timer_irq = {
107         .name           = "timer",
108         .handler        = iq80310_timer_interrupt,
109 };
110
111
112 void __init time_init(void)
113 {
114         volatile u_char *timer_en = (volatile u_char *)IQ80310_TIMER_EN;
115
116         gettimeoffset = iq80310_gettimeoffset;
117
118         setup_irq(IRQ_IQ80310_TIMER, &timer_irq);
119
120         *timer_en = 0;
121         iq80310_write_timer(LATCH);
122         *timer_en |= 2;
123         *timer_en |= 1;
124 }