blob: de5798e1e015a1f8486293a74523c8688b1d4ac4 [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>
Linus Torvalds1da177e2005-04-16 15:20:36 -070033#include <asm/ptrace.h>
Ralf Baechlee01402b2005-07-14 15:57:16 +000034#include <asm/hardirq.h>
35#include <asm/irq.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070036#include <asm/div64.h>
37#include <asm/cpu.h>
38#include <asm/time.h>
39#include <asm/mc146818-time.h>
Ralf Baechlee01402b2005-07-14 15:57:16 +000040#include <asm/msc01_ic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070041
42#include <asm/mips-boards/generic.h>
43#include <asm/mips-boards/prom.h>
Ralf Baechlee01402b2005-07-14 15:57:16 +000044#include <asm/mips-boards/maltaint.h>
45#include <asm/mc146818-time.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070046
47unsigned long cpu_khz;
48
Linus Torvalds1da177e2005-04-16 15:20:36 -070049#if defined(CONFIG_MIPS_ATLAS)
50static char display_string[] = " LINUX ON ATLAS ";
51#endif
52#if defined(CONFIG_MIPS_MALTA)
Ralf Baechle41c594a2006-04-05 09:45:45 +010053#if defined(CONFIG_MIPS_MT_SMTC)
54static char display_string[] = " SMTC LINUX ON MALTA ";
55#else
Linus Torvalds1da177e2005-04-16 15:20:36 -070056static char display_string[] = " LINUX ON MALTA ";
Ralf Baechle41c594a2006-04-05 09:45:45 +010057#endif /* CONFIG_MIPS_MT_SMTC */
Linus Torvalds1da177e2005-04-16 15:20:36 -070058#endif
59#if defined(CONFIG_MIPS_SEAD)
60static char display_string[] = " LINUX ON SEAD ";
61#endif
Ralf Baechle41c594a2006-04-05 09:45:45 +010062static unsigned int display_count;
Linus Torvalds1da177e2005-04-16 15:20:36 -070063#define MAX_DISPLAY_COUNT (sizeof(display_string) - 8)
64
Ralf Baechle41c594a2006-04-05 09:45:45 +010065#define CPUCTR_IMASKBIT (0x100 << MIPSCPU_INT_CPUCTR)
66
67static unsigned int timer_tick_count;
Ralf Baechlee01402b2005-07-14 15:57:16 +000068static int mips_cpu_timer_irq;
Ralf Baechle41c594a2006-04-05 09:45:45 +010069extern void smtc_timer_broadcast(int);
Linus Torvalds1da177e2005-04-16 15:20:36 -070070
Ralf Baechle340ee4b2005-08-17 17:44:08 +000071static inline void scroll_display_message(void)
72{
73 if ((timer_tick_count++ % HZ) == 0) {
74 mips_display_message(&display_string[display_count++]);
75 if (display_count == MAX_DISPLAY_COUNT)
76 display_count = 0;
77 }
78}
79
Ralf Baechlee01402b2005-07-14 15:57:16 +000080static void mips_timer_dispatch (struct pt_regs *regs)
Linus Torvalds1da177e2005-04-16 15:20:36 -070081{
Ralf Baechlee01402b2005-07-14 15:57:16 +000082 do_IRQ (mips_cpu_timer_irq, regs);
83}
84
Ralf Baechle41c594a2006-04-05 09:45:45 +010085/*
86 * Redeclare until I get around mopping the timer code insanity on MIPS.
87 */
Ralf Baechleba339c02005-12-09 12:29:38 +000088extern int null_perf_irq(struct pt_regs *regs);
89
90extern int (*perf_irq)(struct pt_regs *regs);
91
Ralf Baechlee01402b2005-07-14 15:57:16 +000092irqreturn_t mips_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
93{
Ralf Baechle340ee4b2005-08-17 17:44:08 +000094 int cpu = smp_processor_id();
95
Ralf Baechle41c594a2006-04-05 09:45:45 +010096#ifdef CONFIG_MIPS_MT_SMTC
Kevin D. Kissell846acaa2006-09-12 12:08:08 +020097 /*
Ralf Baechle41c594a2006-04-05 09:45:45 +010098 * In an SMTC system, one Count/Compare set exists per VPE.
99 * Which TC within a VPE gets the interrupt is essentially
100 * random - we only know that it shouldn't be one with
101 * IXMT set. Whichever TC gets the interrupt needs to
102 * send special interprocessor interrupts to the other
103 * TCs to make sure that they schedule, etc.
104 *
105 * That code is specific to the SMTC kernel, not to
106 * the a particular platform, so it's invoked from
107 * the general MIPS timer_interrupt routine.
108 */
109
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200110 int vpflags;
111
Ralf Baechle41c594a2006-04-05 09:45:45 +0100112 /*
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200113 * We could be here due to timer interrupt,
114 * perf counter overflow, or both.
Ralf Baechle41c594a2006-04-05 09:45:45 +0100115 */
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200116 if (read_c0_cause() & (1 << 26))
117 perf_irq(regs);
Ralf Baechle41c594a2006-04-05 09:45:45 +0100118
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200119 if (read_c0_cause() & (1 << 30)) {
120 /* If timer interrupt, make it de-assert */
121 write_c0_compare (read_c0_count() - 1);
Ralf Baechle41c594a2006-04-05 09:45:45 +0100122 /*
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200123 * DVPE is necessary so long as cross-VPE interrupts
124 * are done via read-modify-write of Cause register.
Ralf Baechle41c594a2006-04-05 09:45:45 +0100125 */
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200126 vpflags = dvpe();
127 clear_c0_cause(CPUCTR_IMASKBIT);
128 evpe(vpflags);
129 /*
130 * There are things we only want to do once per tick
131 * in an "MP" system. One TC of each VPE will take
132 * the actual timer interrupt. The others will get
133 * timer broadcast IPIs. We use whoever it is that takes
134 * the tick on VPE 0 to run the full timer_interrupt().
135 */
136 if (cpu_data[cpu].vpe_id == 0) {
137 timer_interrupt(irq, NULL, regs);
138 smtc_timer_broadcast(cpu_data[cpu].vpe_id);
139 scroll_display_message();
140 } else {
141 write_c0_compare(read_c0_count() +
142 (mips_hpt_frequency/HZ));
143 local_timer_interrupt(irq, dev_id, regs);
144 smtc_timer_broadcast(cpu_data[cpu].vpe_id);
145 }
146 }
Ralf Baechle41c594a2006-04-05 09:45:45 +0100147#else /* CONFIG_MIPS_MT_SMTC */
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200148 int r2 = cpu_has_mips_r2;
149
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000150 if (cpu == 0) {
151 /*
Ralf Baechleba339c02005-12-09 12:29:38 +0000152 * CPU 0 handles the global timer interrupt job and process
153 * accounting resets count/compare registers to trigger next
154 * timer int.
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000155 */
Ralf Baechleba339c02005-12-09 12:29:38 +0000156 if (!r2 || (read_c0_cause() & (1 << 26)))
157 if (perf_irq(regs))
158 goto out;
159
160 /* we keep interrupt disabled all the time */
161 if (!r2 || (read_c0_cause() & (1 << 30)))
162 timer_interrupt(irq, NULL, regs);
163
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000164 scroll_display_message();
Ralf Baechle11e6df62005-12-09 12:09:22 +0000165 } else {
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000166 /* Everyone else needs to reset the timer int here as
167 ll_local_timer_interrupt doesn't */
168 /*
169 * FIXME: need to cope with counter underflow.
170 * More support needs to be added to kernel/time for
171 * counter/timer interrupts on multiple CPU's
172 */
Ralf Baechle41c594a2006-04-05 09:45:45 +0100173 write_c0_compare(read_c0_count() + (mips_hpt_frequency/HZ));
174
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000175 /*
Ralf Baechle41c594a2006-04-05 09:45:45 +0100176 * Other CPUs should do profiling and process accounting
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000177 */
Ralf Baechle41c594a2006-04-05 09:45:45 +0100178 local_timer_interrupt(irq, dev_id, regs);
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000179 }
Ralf Baechleba339c02005-12-09 12:29:38 +0000180out:
Kevin D. Kissell846acaa2006-09-12 12:08:08 +0200181#endif /* CONFIG_MIPS_MT_SMTC */
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000182 return IRQ_HANDLED;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183}
184
185/*
186 * Estimate CPU frequency. Sets mips_counter_frequency as a side-effect
187 */
188static unsigned int __init estimate_cpu_frequency(void)
189{
190 unsigned int prid = read_c0_prid() & 0xffff00;
191 unsigned int count;
192
Ralf Baechle41c594a2006-04-05 09:45:45 +0100193#if defined(CONFIG_MIPS_SEAD) || defined(CONFIG_MIPS_SIM)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194 /*
195 * The SEAD board doesn't have a real time clock, so we can't
196 * really calculate the timer frequency
197 * For now we hardwire the SEAD board frequency to 12MHz.
198 */
Ralf Baechle42a3b4f2005-09-03 15:56:17 -0700199
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) ||
201 (prid == (PRID_COMP_MIPS | PRID_IMP_25KF)))
202 count = 12000000;
203 else
204 count = 6000000;
205#endif
206#if defined(CONFIG_MIPS_ATLAS) || defined(CONFIG_MIPS_MALTA)
207 unsigned int flags;
208
209 local_irq_save(flags);
210
211 /* Start counter exactly on falling edge of update flag */
212 while (CMOS_READ(RTC_REG_A) & RTC_UIP);
213 while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
214
215 /* Start r4k counter. */
216 write_c0_count(0);
217
218 /* Read counter exactly on falling edge of update flag */
219 while (CMOS_READ(RTC_REG_A) & RTC_UIP);
220 while (!(CMOS_READ(RTC_REG_A) & RTC_UIP));
221
222 count = read_c0_count();
223
224 /* restore interrupts */
225 local_irq_restore(flags);
226#endif
227
228 mips_hpt_frequency = count;
229 if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
230 (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
231 count *= 2;
232
233 count += 5000; /* round */
234 count -= count%10000;
235
236 return count;
237}
238
239unsigned long __init mips_rtc_get_time(void)
240{
241 return mc146818_get_cmos_time();
242}
243
244void __init mips_time_init(void)
245{
Ralf Baechleece22462006-07-09 22:27:23 +0100246 unsigned int est_freq;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 /* Set Data mode - binary. */
249 CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
251 est_freq = estimate_cpu_frequency ();
252
253 printk("CPU frequency %d.%02d MHz\n", est_freq/1000000,
254 (est_freq%1000000)*100/1000000);
255
256 cpu_khz = est_freq / 1000;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700257}
258
Ralf Baechle54d0a212006-07-09 21:38:56 +0100259void __init plat_timer_setup(struct irqaction *irq)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700260{
Ralf Baechlee01402b2005-07-14 15:57:16 +0000261 if (cpu_has_veic) {
262 set_vi_handler (MSC01E_INT_CPUCTR, mips_timer_dispatch);
263 mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
264 }
265 else {
266 if (cpu_has_vint)
267 set_vi_handler (MIPSCPU_INT_CPUCTR, mips_timer_dispatch);
268 mips_cpu_timer_irq = MIPSCPU_INT_BASE + MIPSCPU_INT_CPUCTR;
269 }
270
271
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272 /* we are using the cpu counter for timer interrupts */
Ralf Baechlee01402b2005-07-14 15:57:16 +0000273 irq->handler = mips_timer_interrupt; /* we use our own handler */
Ralf Baechle41c594a2006-04-05 09:45:45 +0100274#ifdef CONFIG_MIPS_MT_SMTC
275 setup_irq_smtc(mips_cpu_timer_irq, irq, CPUCTR_IMASKBIT);
276#else
Ralf Baechlee01402b2005-07-14 15:57:16 +0000277 setup_irq(mips_cpu_timer_irq, irq);
Ralf Baechle41c594a2006-04-05 09:45:45 +0100278#endif /* CONFIG_MIPS_MT_SMTC */
Ralf Baechlee01402b2005-07-14 15:57:16 +0000279
Ralf Baechle340ee4b2005-08-17 17:44:08 +0000280#ifdef CONFIG_SMP
281 /* irq_desc(riptor) is a global resource, when the interrupt overlaps
282 on seperate cpu's the first one tries to handle the second interrupt.
283 The effect is that the int remains disabled on the second cpu.
284 Mark the interrupt with IRQ_PER_CPU to avoid any confusion */
285 irq_desc[mips_cpu_timer_irq].status |= IRQ_PER_CPU;
286#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
288 /* to generate the first timer interrupt */
289 write_c0_compare (read_c0_count() + mips_hpt_frequency/HZ);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290}