blob: a3c3a1d462b272f39900d9c9910ac1f1d8bde455 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022#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>
Ralf Baechle41c594a2006-04-05 09:45:45 +010032#include <asm/mipsmtregs.h>
Ralf Baechlee01402b2005-07-14 15:57:16 +000033#include <asm/hardirq.h>
34#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <asm/div64.h>
36#include <asm/cpu.h>
37#include <asm/time.h>
38#include <asm/mc146818-time.h>
Ralf Baechlee01402b2005-07-14 15:57:16 +000039#include <asm/msc01_ic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070040
41#include <asm/mips-boards/generic.h>
42#include <asm/mips-boards/prom.h>
Maciej W. Rozyckifc095a92006-09-12 19:12:18 +010043
44#ifdef CONFIG_MIPS_ATLAS
45#include <asm/mips-boards/atlasint.h>
46#endif
47#ifdef CONFIG_MIPS_MALTA
Ralf Baechlee01402b2005-07-14 15:57:16 +000048#include <asm/mips-boards/maltaint.h>
Maciej W. Rozyckifc095a92006-09-12 19:12:18 +010049#endif
Atsushi Nemotof75f3692007-01-08 01:27:40 +090050#ifdef CONFIG_MIPS_SEAD
51#include <asm/mips-boards/seadint.h>
52#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -070053
54unsigned long cpu_khz;
55
Linus Torvalds1da177e2005-04-16 15:20:36 -070056#if defined(CONFIG_MIPS_ATLAS)
57static char display_string[] = " LINUX ON ATLAS ";
58#endif
59#if defined(CONFIG_MIPS_MALTA)
Ralf Baechle41c594a2006-04-05 09:45:45 +010060#if defined(CONFIG_MIPS_MT_SMTC)
61static char display_string[] = " SMTC LINUX ON MALTA ";
62#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070063static char display_string[] = " LINUX ON MALTA ";
Ralf Baechle41c594a2006-04-05 09:45:45 +010064#endif /* CONFIG_MIPS_MT_SMTC */
Linus Torvalds1da177e2005-04-16 15:20:36 -070065#endif
66#if defined(CONFIG_MIPS_SEAD)
67static char display_string[] = " LINUX ON SEAD ";
68#endif
Ralf Baechle41c594a2006-04-05 09:45:45 +010069static unsigned int display_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070070#define MAX_DISPLAY_COUNT (sizeof(display_string) - 8)
71
Ralf Baechle41c594a2006-04-05 09:45:45 +010072#define CPUCTR_IMASKBIT (0x100 << MIPSCPU_INT_CPUCTR)
73
74static unsigned int timer_tick_count;
Ralf Baechlee01402b2005-07-14 15:57:16 +000075static int mips_cpu_timer_irq;
Ralf Baechle41c594a2006-04-05 09:45:45 +010076extern void smtc_timer_broadcast(int);
Linus Torvalds1da177e2005-04-16 15:20:36 -070077
Ralf Baechle340ee4b2005-08-17 17:44:08 +000078static 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
Ralf Baechle937a8012006-10-07 19:44:33 +010087static void mips_timer_dispatch(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -070088{
Ralf Baechle937a8012006-10-07 19:44:33 +010089 do_IRQ(mips_cpu_timer_irq);
Ralf Baechlee01402b2005-07-14 15:57:16 +000090}
91
Ralf Baechle41c594a2006-04-05 09:45:45 +010092/*
93 * Redeclare until I get around mopping the timer code insanity on MIPS.
94 */
Ralf Baechle937a8012006-10-07 19:44:33 +010095extern int null_perf_irq(void);
Ralf Baechleba339c02005-12-09 12:29:38 +000096
Ralf Baechle937a8012006-10-07 19:44:33 +010097extern int (*perf_irq)(void);
Ralf Baechleba339c02005-12-09 12:29:38 +000098
Ralf Baechle937a8012006-10-07 19:44:33 +010099irqreturn_t mips_timer_interrupt(int irq, void *dev_id)
Ralf Baechlee01402b2005-07-14 15:57:16 +0000100{
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000101 int cpu = smp_processor_id();
102
Ralf Baechle41c594a2006-04-05 09:45:45 +0100103#ifdef CONFIG_MIPS_MT_SMTC
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200104 /*
Ralf Baechle41c594a2006-04-05 09:45:45 +0100105 * 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
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200117 int vpflags;
118
Ralf Baechle41c594a2006-04-05 09:45:45 +0100119 /*
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200120 * We could be here due to timer interrupt,
121 * perf counter overflow, or both.
Ralf Baechle41c594a2006-04-05 09:45:45 +0100122 */
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200123 if (read_c0_cause() & (1 << 26))
Ralf Baechle937a8012006-10-07 19:44:33 +0100124 perf_irq();
Ralf Baechle41c594a2006-04-05 09:45:45 +0100125
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200126 if (read_c0_cause() & (1 << 30)) {
127 /* If timer interrupt, make it de-assert */
128 write_c0_compare (read_c0_count() - 1);
Ralf Baechle41c594a2006-04-05 09:45:45 +0100129 /*
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200130 * DVPE is necessary so long as cross-VPE interrupts
131 * are done via read-modify-write of Cause register.
Ralf Baechle41c594a2006-04-05 09:45:45 +0100132 */
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200133 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) {
Ralf Baechle937a8012006-10-07 19:44:33 +0100144 timer_interrupt(irq, NULL);
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200145 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));
Ralf Baechle937a8012006-10-07 19:44:33 +0100150 local_timer_interrupt(irq, dev_id);
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200151 smtc_timer_broadcast(cpu_data[cpu].vpe_id);
152 }
153 }
Ralf Baechle41c594a2006-04-05 09:45:45 +0100154#else /* CONFIG_MIPS_MT_SMTC */
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200155 int r2 = cpu_has_mips_r2;
156
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000157 if (cpu == 0) {
158 /*
Ralf Baechleba339c02005-12-09 12:29:38 +0000159 * CPU 0 handles the global timer interrupt job and process
160 * accounting resets count/compare registers to trigger next
161 * timer int.
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000162 */
Ralf Baechleba339c02005-12-09 12:29:38 +0000163 if (!r2 || (read_c0_cause() & (1 << 26)))
Ralf Baechle937a8012006-10-07 19:44:33 +0100164 if (perf_irq())
Ralf Baechleba339c02005-12-09 12:29:38 +0000165 goto out;
166
167 /* we keep interrupt disabled all the time */
168 if (!r2 || (read_c0_cause() & (1 << 30)))
Ralf Baechle937a8012006-10-07 19:44:33 +0100169 timer_interrupt(irq, NULL);
Ralf Baechleba339c02005-12-09 12:29:38 +0000170
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000171 scroll_display_message();
Ralf Baechle11e6df62005-12-09 12:09:22 +0000172 } else {
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000173 /* 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 */
Ralf Baechle41c594a2006-04-05 09:45:45 +0100180 write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ));
181
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000182 /*
Ralf Baechle41c594a2006-04-05 09:45:45 +0100183 * Other CPUs should do profiling and process accounting
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000184 */
Ralf Baechle937a8012006-10-07 19:44:33 +0100185 local_timer_interrupt(irq, dev_id);
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000186 }
Ralf Baechleba339c02005-12-09 12:29:38 +0000187out:
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200188#endif /* CONFIG_MIPS_MT_SMTC */
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000189 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190}
191
192/*
Ralf Baechle224dc502006-10-21 02:05:20 +0100193 * Estimate CPU frequency. Sets mips_hpt_frequency as a side-effect
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 */
195static unsigned int __init estimate_cpu_frequency(void)
196{
197 unsigned int prid = read_c0_prid() & 0xffff00;
198 unsigned int count;
199
Ralf Baechle41c594a2006-04-05 09:45:45 +0100200#if defined(CONFIG_MIPS_SEAD) || defined(CONFIG_MIPS_SIM)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 /*
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 */
Ralf Baechle42a3b4f2005-09-03 15:56:17 -0700206
Linus Torvalds1da177e2005-04-16 15:20:36 -0700207 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)
Ralf Baechlee79f55a2006-10-31 19:53:15 +0000214 unsigned long flags;
Ralf Baechle70e46f42006-10-31 18:33:09 +0000215 unsigned int start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216
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. */
Ralf Baechle70e46f42006-10-31 18:33:09 +0000224 start = read_c0_count();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700225
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
Ralf Baechle70e46f42006-10-31 18:33:09 +0000230 count = read_c0_count() - start;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
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
247unsigned long __init mips_rtc_get_time(void)
248{
249 return mc146818_get_cmos_time();
250}
251
252void __init mips_time_init(void)
253{
Ralf Baechleece22462006-07-09 22:27:23 +0100254 unsigned int est_freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 /* Set Data mode - binary. */
257 CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265}
266
Ralf Baechle54d0a212006-07-09 21:38:56 +0100267void __init plat_timer_setup(struct irqaction *irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268{
Atsushi Nemotof75f3692007-01-08 01:27:40 +0900269#ifdef MSC01E_INT_BASE
Ralf Baechlee01402b2005-07-14 15:57:16 +0000270 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;
Atsushi Nemotof75f3692007-01-08 01:27:40 +0900273 } else
274#endif
275 {
Ralf Baechlee01402b2005-07-14 15:57:16 +0000276 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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 /* we are using the cpu counter for timer interrupts */
Ralf Baechlee01402b2005-07-14 15:57:16 +0000283 irq->handler = mips_timer_interrupt; /* we use our own handler */
Ralf Baechle41c594a2006-04-05 09:45:45 +0100284#ifdef CONFIG_MIPS_MT_SMTC
285 setup_irq_smtc(mips_cpu_timer_irq, irq, CPUCTR_IMASKBIT);
286#else
Ralf Baechlee01402b2005-07-14 15:57:16 +0000287 setup_irq(mips_cpu_timer_irq, irq);
Ralf Baechle41c594a2006-04-05 09:45:45 +0100288#endif /* CONFIG_MIPS_MT_SMTC */
Ralf Baechlee01402b2005-07-14 15:57:16 +0000289
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000290#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;
Atsushi Nemoto14178362006-11-14 01:13:18 +0900296 set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq);
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000297#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700298
299 /* to generate the first timer interrupt */
300 write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700301}