]> nv-tegra.nvidia Code Review - linux-2.6.git/blob - arch/mips/mips-boards/generic/time.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/sfrench/cifs-2.6
[linux-2.6.git] / arch / mips / mips-boards / generic / time.c
1 /*
2  * Carsten Langgaard, carstenl@mips.com
3  * Copyright (C) 1999,2000 MIPS Technologies, Inc.  All rights reserved.
4  *
5  *  This program is free software; you can distribute it and/or modify it
6  *  under the terms of the GNU General Public License (Version 2) as
7  *  published by the Free Software Foundation.
8  *
9  *  This program is distributed in the hope it will be useful, but WITHOUT
10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  *  for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  59 Temple Place - Suite 330, Boston MA 02111-1307, USA.
17  *
18  * Setting up the clock on the MIPS boards.
19  */
20
21 #include <linux/types.h>
22 #include <linux/init.h>
23 #include <linux/kernel_stat.h>
24 #include <linux/sched.h>
25 #include <linux/spinlock.h>
26 #include <linux/interrupt.h>
27 #include <linux/time.h>
28 #include <linux/timex.h>
29 #include <linux/mc146818rtc.h>
30
31 #include <asm/mipsregs.h>
32 #include <asm/mipsmtregs.h>
33 #include <asm/hardirq.h>
34 #include <asm/irq.h>
35 #include <asm/div64.h>
36 #include <asm/cpu.h>
37 #include <asm/time.h>
38 #include <asm/mc146818-time.h>
39 #include <asm/msc01_ic.h>
40
41 #include <asm/mips-boards/generic.h>
42 #include <asm/mips-boards/prom.h>
43
44 #ifdef CONFIG_MIPS_ATLAS
45 #include <asm/mips-boards/atlasint.h>
46 #endif
47 #ifdef CONFIG_MIPS_MALTA
48 #include <asm/mips-boards/maltaint.h>
49 #endif
50 #ifdef CONFIG_MIPS_SEAD
51 #include <asm/mips-boards/seadint.h>
52 #endif
53
54 unsigned long cpu_khz;
55
56 #if defined(CONFIG_MIPS_ATLAS)
57 static char display_string[] = "        LINUX ON ATLAS       ";
58 #endif
59 #if defined(CONFIG_MIPS_MALTA)
60 #if defined(CONFIG_MIPS_MT_SMTC)
61 static char display_string[] = "       SMTC LINUX ON MALTA       ";
62 #else
63 static char display_string[] = "        LINUX ON MALTA       ";
64 #endif /* CONFIG_MIPS_MT_SMTC */
65 #endif
66 #if defined(CONFIG_MIPS_SEAD)
67 static char display_string[] = "        LINUX ON SEAD       ";
68 #endif
69 static unsigned int display_count;
70 #define MAX_DISPLAY_COUNT (sizeof(display_string) - 8)
71
72 #define CPUCTR_IMASKBIT (0x100 << MIPSCPU_INT_CPUCTR)
73
74 static unsigned int timer_tick_count;
75 static int mips_cpu_timer_irq;
76 extern void smtc_timer_broadcast(int);
77
78 static inline void scroll_display_message(void)
79 {
80         if ((timer_tick_count++ % HZ) == 0) {
81                 mips_display_message(&display_string[display_count++]);
82                 if (display_count == MAX_DISPLAY_COUNT)
83                         display_count = 0;
84         }
85 }
86
87 static void mips_timer_dispatch(void)
88 {
89         do_IRQ(mips_cpu_timer_irq);
90 }
91
92 /*
93  * Redeclare until I get around mopping the timer code insanity on MIPS.
94  */
95 extern int null_perf_irq(void);
96
97 extern int (*perf_irq)(void);
98
99 irqreturn_t mips_timer_interrupt(int irq, void *dev_id)
100 {
101         int cpu = smp_processor_id();
102
103 #ifdef CONFIG_MIPS_MT_SMTC
104         /*
105          *  In an SMTC system, one Count/Compare set exists per VPE.
106          *  Which TC within a VPE gets the interrupt is essentially
107          *  random - we only know that it shouldn't be one with
108          *  IXMT set. Whichever TC gets the interrupt needs to
109          *  send special interprocessor interrupts to the other
110          *  TCs to make sure that they schedule, etc.
111          *
112          *  That code is specific to the SMTC kernel, not to
113          *  the a particular platform, so it's invoked from
114          *  the general MIPS timer_interrupt routine.
115          */
116
117         int vpflags;
118
119         /*
120          * We could be here due to timer interrupt,
121          * perf counter overflow, or both.
122          */
123         if (read_c0_cause() & (1 << 26))
124                 perf_irq();
125
126         if (read_c0_cause() & (1 << 30)) {
127                 /* If timer interrupt, make it de-assert */
128                 write_c0_compare (read_c0_count() - 1);
129                 /*
130                  * DVPE is necessary so long as cross-VPE interrupts
131                  * are done via read-modify-write of Cause register.
132                  */
133                 vpflags = dvpe();
134                 clear_c0_cause(CPUCTR_IMASKBIT);
135                 evpe(vpflags);
136                 /*
137                  * There are things we only want to do once per tick
138                  * in an "MP" system.   One TC of each VPE will take
139                  * the actual timer interrupt.  The others will get
140                  * timer broadcast IPIs. We use whoever it is that takes
141                  * the tick on VPE 0 to run the full timer_interrupt().
142                  */
143                 if (cpu_data[cpu].vpe_id == 0) {
144                                 timer_interrupt(irq, NULL);
145                                 smtc_timer_broadcast(cpu_data[cpu].vpe_id);
146                                 scroll_display_message();
147                 } else {
148                         write_c0_compare(read_c0_count() +
149                                          (mips_hpt_frequency/HZ));
150                         local_timer_interrupt(irq, dev_id);
151                         smtc_timer_broadcast(cpu_data[cpu].vpe_id);
152                 }
153         }
154 #else /* CONFIG_MIPS_MT_SMTC */
155         int r2 = cpu_has_mips_r2;
156
157         if (cpu == 0) {
158                 /*
159                  * CPU 0 handles the global timer interrupt job and process
160                  * accounting resets count/compare registers to trigger next
161                  * timer int.
162                  */
163                 if (!r2 || (read_c0_cause() & (1 << 26)))
164                         if (perf_irq())
165                                 goto out;
166
167                 /* we keep interrupt disabled all the time */
168                 if (!r2 || (read_c0_cause() & (1 << 30)))
169                         timer_interrupt(irq, NULL);
170
171                 scroll_display_message();
172         } else {
173                 /* Everyone else needs to reset the timer int here as
174                    ll_local_timer_interrupt doesn't */
175                 /*
176                  * FIXME: need to cope with counter underflow.
177                  * More support needs to be added to kernel/time for
178                  * counter/timer interrupts on multiple CPU's
179                  */
180                 write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ));
181
182                 /*
183                  * Other CPUs should do profiling and process accounting
184                  */
185                 local_timer_interrupt(irq, dev_id);
186         }
187 out:
188 #endif /* CONFIG_MIPS_MT_SMTC */
189         return IRQ_HANDLED;
190 }
191
192 /*
193  * Estimate CPU frequency.  Sets mips_hpt_frequency as a side-effect
194  */
195 static unsigned int __init estimate_cpu_frequency(void)
196 {
197         unsigned int prid = read_c0_prid() & 0xffff00;
198         unsigned int count;
199
200 #if defined(CONFIG_MIPS_SEAD) || defined(CONFIG_MIPS_SIM)
201         /*
202          * The SEAD board doesn't have a real time clock, so we can't
203          * really calculate the timer frequency
204          * For now we hardwire the SEAD board frequency to 12MHz.
205          */
206
207         if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) ||
208             (prid == (PRID_COMP_MIPS | PRID_IMP_25KF)))
209                 count = 12000000;
210         else
211                 count = 6000000;
212 #endif
213 #if defined(CONFIG_MIPS_ATLAS) || defined(CONFIG_MIPS_MALTA)
214         unsigned long flags;
215         unsigned int start;
216
217         local_irq_save(flags);
218
219         /* Start counter exactly on falling edge of update flag */
220         while (CMOS_READ(RTC_REG_A) & RTC_UIP);
221         while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
222
223         /* Start r4k counter. */
224         start = read_c0_count();
225
226         /* Read counter exactly on falling edge of update flag */
227         while (CMOS_READ(RTC_REG_A) & RTC_UIP);
228         while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
229
230         count = read_c0_count() - start;
231
232         /* restore interrupts */
233         local_irq_restore(flags);
234 #endif
235
236         mips_hpt_frequency = count;
237         if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
238             (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
239                 count *= 2;
240
241         count += 5000;    /* round */
242         count -= count%10000;
243
244         return count;
245 }
246
247 unsigned long __init mips_rtc_get_time(void)
248 {
249         return mc146818_get_cmos_time();
250 }
251
252 void __init mips_time_init(void)
253 {
254         unsigned int est_freq;
255
256         /* Set Data mode - binary. */
257         CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL);
258
259         est_freq = estimate_cpu_frequency ();
260
261         printk("CPU frequency %d.%02d MHz\n", est_freq/1000000,
262                (est_freq%1000000)*100/1000000);
263
264         cpu_khz = est_freq / 1000;
265 }
266
267 void __init plat_timer_setup(struct irqaction *irq)
268 {
269 #ifdef MSC01E_INT_BASE
270         if (cpu_has_veic) {
271                 set_vi_handler (MSC01E_INT_CPUCTR, mips_timer_dispatch);
272                 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
273         } else
274 #endif
275         {
276                 if (cpu_has_vint)
277                         set_vi_handler (MIPSCPU_INT_CPUCTR, mips_timer_dispatch);
278                 mips_cpu_timer_irq = MIPSCPU_INT_BASE + MIPSCPU_INT_CPUCTR;
279         }
280
281
282         /* we are using the cpu counter for timer interrupts */
283         irq->handler = mips_timer_interrupt;    /* we use our own handler */
284 #ifdef CONFIG_MIPS_MT_SMTC
285         setup_irq_smtc(mips_cpu_timer_irq, irq, CPUCTR_IMASKBIT);
286 #else
287         setup_irq(mips_cpu_timer_irq, irq);
288 #endif /* CONFIG_MIPS_MT_SMTC */
289
290 #ifdef CONFIG_SMP
291         /* irq_desc(riptor) is a global resource, when the interrupt overlaps
292            on seperate cpu's the first one tries to handle the second interrupt.
293            The effect is that the int remains disabled on the second cpu.
294            Mark the interrupt with IRQ_PER_CPU to avoid any confusion */
295         irq_desc[mips_cpu_timer_irq].status |= IRQ_PER_CPU;
296         set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq);
297 #endif
298
299         /* to generate the first timer interrupt */
300         write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ);
301 }