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