1#include <linux/types.h>
2#include <linux/init.h>
3#include <linux/kernel_stat.h>
4#include <linux/sched.h>
5#include <linux/spinlock.h>
6#include <linux/interrupt.h>
7#include <linux/mc146818rtc.h>
8#include <linux/timex.h>
9
10#include <asm/mipsregs.h>
11#include <asm/ptrace.h>
12#include <asm/hardirq.h>
13#include <asm/div64.h>
14#include <asm/cpu.h>
15#include <asm/time.h>
16#include <asm/irq.h>
17#include <asm/mc146818-time.h>
18#include <asm/msc01_ic.h>
19#include <asm/smp.h>
20
21#include <asm/mips-boards/generic.h>
22#include <asm/mips-boards/prom.h>
23#include <asm/mips-boards/simint.h>
24
25
26unsigned long cpu_khz;
27
28irqreturn_t sim_timer_interrupt(int irq, void *dev_id)
29{
30#ifdef CONFIG_SMP
31	int cpu = smp_processor_id();
32
33	/*
34	 * CPU 0 handles the global timer interrupt job
35	 * resets count/compare registers to trigger next timer int.
36	 */
37#ifndef CONFIG_MIPS_MT_SMTC
38	if (cpu == 0) {
39		timer_interrupt(irq, dev_id);
40	}
41	else {
42		/* Everyone else needs to reset the timer int here as
43		   ll_local_timer_interrupt doesn't */
44		write_c0_compare (read_c0_count() + ( mips_hpt_frequency/HZ));
45	}
46#else /* SMTC */
47	/*
48	 *  In SMTC system, one Count/Compare set exists per VPE.
49	 *  Which TC within a VPE gets the interrupt is essentially
50	 *  random - we only know that it shouldn't be one with
51	 *  IXMT set. Whichever TC gets the interrupt needs to
52	 *  send special interprocessor interrupts to the other
53	 *  TCs to make sure that they schedule, etc.
54	 *
55	 *  That code is specific to the SMTC kernel, not to
56	 *  the simulation platform, so it's invoked from
57	 *  the general MIPS timer_interrupt routine.
58	 *
59	 * We have a problem in that the interrupt vector code
60	 * had to turn off the timer IM bit to avoid redundant
61	 * entries, but we may never get to mips_cpu_irq_end
62	 * to turn it back on again if the scheduler gets
63	 * involved.  So we clear the pending timer here,
64	 * and re-enable the mask...
65	 */
66
67	int vpflags = dvpe();
68	write_c0_compare (read_c0_count() - 1);
69	clear_c0_cause(0x100 << cp0_compare_irq);
70	set_c0_status(0x100 << cp0_compare_irq);
71	irq_enable_hazard();
72	evpe(vpflags);
73
74	if(cpu_data[cpu].vpe_id == 0) timer_interrupt(irq, dev_id);
75	else write_c0_compare (read_c0_count() + ( mips_hpt_frequency/HZ));
76	smtc_timer_broadcast(cpu_data[cpu].vpe_id);
77
78#endif /* CONFIG_MIPS_MT_SMTC */
79
80	/*
81	 * every CPU should do profiling and process accounting
82	 */
83 	local_timer_interrupt (irq, dev_id);
84	return IRQ_HANDLED;
85#else
86	return timer_interrupt (irq, dev_id);
87#endif
88}
89
90
91
92/*
93 * Estimate CPU frequency.  Sets mips_hpt_frequency as a side-effect
94 */
95static unsigned int __init estimate_cpu_frequency(void)
96{
97	unsigned int prid = read_c0_prid() & 0xffff00;
98	unsigned int count;
99
100	/*
101	 * hardwire the board frequency to 12MHz.
102	 */
103
104	if ((prid == (PRID_COMP_MIPS | PRID_IMP_20KC)) ||
105	    (prid == (PRID_COMP_MIPS | PRID_IMP_25KF)))
106		count = 12000000;
107	else
108		count =  6000000;
109
110	mips_hpt_frequency = count;
111
112	if ((prid != (PRID_COMP_MIPS | PRID_IMP_20KC)) &&
113	    (prid != (PRID_COMP_MIPS | PRID_IMP_25KF)))
114		count *= 2;
115
116	count += 5000;    /* round */
117	count -= count%10000;
118
119	return count;
120}
121
122void __init sim_time_init(void)
123{
124	unsigned int est_freq, flags;
125
126	local_irq_save(flags);
127
128
129        /* Set Data mode - binary. */
130	CMOS_WRITE(CMOS_READ(RTC_CONTROL) | RTC_DM_BINARY, RTC_CONTROL);
131
132
133	est_freq = estimate_cpu_frequency ();
134
135	printk("CPU frequency %d.%02d MHz\n", est_freq/1000000,
136	       (est_freq%1000000)*100/1000000);
137
138        cpu_khz = est_freq / 1000;
139
140	local_irq_restore(flags);
141}
142
143static int mips_cpu_timer_irq;
144
145static void mips_timer_dispatch(void)
146{
147	do_IRQ(mips_cpu_timer_irq);
148}
149
150
151void __init plat_timer_setup(struct irqaction *irq)
152{
153	if (cpu_has_veic) {
154		set_vi_handler(MSC01E_INT_CPUCTR, mips_timer_dispatch);
155		mips_cpu_timer_irq = MSC01E_INT_BASE + MSC01E_INT_CPUCTR;
156	}
157	else {
158		if (cpu_has_vint)
159			set_vi_handler(cp0_compare_irq, mips_timer_dispatch);
160		mips_cpu_timer_irq = MIPS_CPU_IRQ_BASE + cp0_compare_irq;
161	}
162
163	/* we are using the cpu counter for timer interrupts */
164	irq->handler = sim_timer_interrupt;
165	setup_irq(mips_cpu_timer_irq, irq);
166
167#ifdef CONFIG_SMP
168	/* irq_desc(riptor) is a global resource, when the interrupt overlaps
169	   on seperate cpu's the first one tries to handle the second interrupt.
170	   The effect is that the int remains disabled on the second cpu.
171	   Mark the interrupt with IRQ_PER_CPU to avoid any confusion */
172	irq_desc[mips_cpu_timer_irq].flags |= IRQ_PER_CPU;
173	set_irq_handler(mips_cpu_timer_irq, handle_percpu_irq);
174#endif
175}
176