# BRCM_VERSION=3
[bcm963xx.git] / kernel / linux / arch / arm / mach-s3c2410 / time.c
1 /* linux/include/asm-arm/arch-s3c2410/time.h
2  *
3  *  Copyright (C) 2003 Simtec Electronics <linux@simtec.co.uk>
4  *    Ben Dooks, <ben@simtec.co.uk>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include <linux/config.h>
22 #include <linux/kernel.h>
23 #include <linux/sched.h>
24 #include <linux/init.h>
25 #include <linux/interrupt.h>
26 #include <asm/system.h>
27 #include <asm/leds.h>
28 #include <asm/mach-types.h>
29
30 #include <asm/io.h>
31 #include <asm/irq.h>
32 #include <asm/arch/map.h>
33 #include <asm/arch/regs-timer.h>
34 #include <asm/mach/time.h>
35
36 static unsigned long timer_startval;
37 static unsigned long timer_ticks_usec;
38
39 #ifdef CONFIG_S3C2410_RTC
40 extern void s3c2410_rtc_check();
41 #endif
42
43 /* with an 12MHz clock, we get 12 ticks per-usec
44  */
45
46
47 /***
48  * Returns microsecond  since last clock interrupt.  Note that interrupts
49  * will have been disabled by do_gettimeoffset()
50  * IRQs are disabled before entering here from do_gettimeofday()
51  */
52 static unsigned long s3c2410_gettimeoffset (void)
53 {
54         unsigned long tdone;
55         unsigned long usec;
56
57         /* work out how many ticks have gone since last timer interrupt */
58
59         tdone = timer_startval - __raw_readl(S3C2410_TCNTO(4));
60
61         /* currently, tcnt is in 12MHz units, but this may change
62          * for non-bast machines...
63          */
64
65         usec = tdone / timer_ticks_usec;
66
67         return usec;
68 }
69
70
71 /*
72  * IRQ handler for the timer
73  */
74 static irqreturn_t
75 s3c2410_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
76 {
77         timer_tick(regs);
78
79         return IRQ_HANDLED;
80 }
81
82 static struct irqaction s3c2410_timer_irq = {
83         .name           = "S32410 Timer Tick",
84         .flags          = SA_INTERRUPT,
85         .handler        = s3c2410_timer_interrupt
86 };
87
88 /*
89  * Set up timer interrupt, and return the current time in seconds.
90  *
91  * Currently we only use timer4, as it is the only timer which has no
92  * other function that can be exploited externally
93  */
94 void __init s3c2410_init_time (void)
95 {
96         unsigned long tcon;
97         unsigned long tcnt;
98         unsigned long tcfg1;
99         unsigned long tcfg0;
100
101         gettimeoffset = s3c2410_gettimeoffset;
102
103         tcnt = 0xffff;  /* default value for tcnt */
104
105         /* read the current timer configuration bits */
106
107         tcon = __raw_readl(S3C2410_TCON);
108         tcfg1 = __raw_readl(S3C2410_TCFG1);
109         tcfg0 = __raw_readl(S3C2410_TCFG0);
110
111         /* configure the system for whichever machine is in use */
112
113         if (machine_is_bast() || machine_is_vr1000()) {
114                 timer_ticks_usec = 12;        /* timer is at 12MHz */
115                 tcnt = (timer_ticks_usec * (1000*1000)) / HZ;
116         }
117
118         /* for the h1940, we use the pclk from the core to generate
119          * the timer values. since 67.5MHz is not a value we can directly
120          * generate the timer value from, we need to pre-scale and
121          * divied before using it.
122          *
123          * overall divsior to get 200Hz is 337500
124          *   we can fit tcnt if we pre-scale by 6, producing a tick rate
125          *   of 11.25MHz, and a tcnt of 56250.
126          */
127
128         if (machine_is_h1940() || machine_is_smdk2410() ) {
129                 timer_ticks_usec = s3c2410_pclk / (1000*1000);
130                 timer_ticks_usec /= 6;
131
132                 tcfg1 &= ~S3C2410_TCFG1_MUX4_MASK;
133                 tcfg1 |= S3C2410_TCFG1_MUX4_DIV2;
134
135                 tcfg0 &= ~S3C2410_TCFG_PRESCALER1_MASK;
136                 tcfg0 |= ((6 - 1) / 2) << S3C2410_TCFG_PRESCALER1_SHIFT;
137
138                 tcnt = (s3c2410_pclk / 6) / HZ;
139         }
140
141
142         printk("setup_timer tcon=%08lx, tcnt %04lx, tcfg %08lx,%08lx\n",
143                tcon, tcnt, tcfg0, tcfg1);
144
145         /* check to see if timer is within 16bit range... */
146         if (tcnt > 0xffff) {
147                 panic("setup_timer: HZ is too small, cannot configure timer!");
148                 return;
149         }
150
151         __raw_writel(tcfg1, S3C2410_TCFG1);
152         __raw_writel(tcfg0, S3C2410_TCFG0);
153
154         timer_startval = tcnt;
155         __raw_writel(tcnt, S3C2410_TCNTB(4));
156
157         /* ensure timer is stopped... */
158
159         tcon &= ~(7<<20);
160         tcon |= S3C2410_TCON_T4RELOAD;
161         tcon |= S3C2410_TCON_T4MANUALUPD;
162
163         __raw_writel(tcon, S3C2410_TCON);
164         __raw_writel(tcnt, S3C2410_TCNTB(4));
165         __raw_writel(tcnt, S3C2410_TCMPB(4));
166
167         setup_irq(IRQ_TIMER4, &s3c2410_timer_irq);
168
169         /* start the timer running */
170         tcon |= S3C2410_TCON_T4START;
171         tcon &= ~S3C2410_TCON_T4MANUALUPD;
172         __raw_writel(tcon, S3C2410_TCON);
173 }
174
175
176