]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blob - arch/mips/kernel/cevt-r4k.c
08b84d476c8727c243f3a0bb2cb942eae65190f9
[mirror_ubuntu-artful-kernel.git] / arch / mips / kernel / cevt-r4k.c
1 /*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2007 MIPS Technologies, Inc.
7 * Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
8 */
9 #include <linux/clockchips.h>
10 #include <linux/interrupt.h>
11 #include <linux/percpu.h>
12
13 #include <asm/time.h>
14
15 static int mips_next_event(unsigned long delta,
16 struct clock_event_device *evt)
17 {
18 unsigned int cnt;
19 int res;
20
21 #ifdef CONFIG_MIPS_MT_SMTC
22 {
23 unsigned long flags, vpflags;
24 local_irq_save(flags);
25 vpflags = dvpe();
26 #endif
27 cnt = read_c0_count();
28 cnt += delta;
29 write_c0_compare(cnt);
30 res = ((long)(read_c0_count() - cnt ) > 0) ? -ETIME : 0;
31 #ifdef CONFIG_MIPS_MT_SMTC
32 evpe(vpflags);
33 local_irq_restore(flags);
34 }
35 #endif
36 return res;
37 }
38
39 static void mips_set_mode(enum clock_event_mode mode,
40 struct clock_event_device *evt)
41 {
42 /* Nothing to do ... */
43 }
44
45 static DEFINE_PER_CPU(struct clock_event_device, mips_clockevent_device);
46 static int cp0_timer_irq_installed;
47
48 /*
49 * Timer ack for an R4k-compatible timer of a known frequency.
50 */
51 static void c0_timer_ack(void)
52 {
53 write_c0_compare(read_c0_compare());
54 }
55
56 /*
57 * Possibly handle a performance counter interrupt.
58 * Return true if the timer interrupt should not be checked
59 */
60 static inline int handle_perf_irq(int r2)
61 {
62 /*
63 * The performance counter overflow interrupt may be shared with the
64 * timer interrupt (cp0_perfcount_irq < 0). If it is and a
65 * performance counter has overflowed (perf_irq() == IRQ_HANDLED)
66 * and we can't reliably determine if a counter interrupt has also
67 * happened (!r2) then don't check for a timer interrupt.
68 */
69 return (cp0_perfcount_irq < 0) &&
70 perf_irq() == IRQ_HANDLED &&
71 !r2;
72 }
73
74 static irqreturn_t c0_compare_interrupt(int irq, void *dev_id)
75 {
76 const int r2 = cpu_has_mips_r2;
77 struct clock_event_device *cd;
78 int cpu = smp_processor_id();
79
80 /*
81 * Suckage alert:
82 * Before R2 of the architecture there was no way to see if a
83 * performance counter interrupt was pending, so we have to run
84 * the performance counter interrupt handler anyway.
85 */
86 if (handle_perf_irq(r2))
87 goto out;
88
89 /*
90 * The same applies to performance counter interrupts. But with the
91 * above we now know that the reason we got here must be a timer
92 * interrupt. Being the paranoiacs we are we check anyway.
93 */
94 if (!r2 || (read_c0_cause() & (1 << 30))) {
95 c0_timer_ack();
96 #ifdef CONFIG_MIPS_MT_SMTC
97 if (cpu_data[cpu].vpe_id)
98 goto out;
99 cpu = 0;
100 #endif
101 cd = &per_cpu(mips_clockevent_device, cpu);
102 cd->event_handler(cd);
103 }
104
105 out:
106 return IRQ_HANDLED;
107 }
108
109 static struct irqaction c0_compare_irqaction = {
110 .handler = c0_compare_interrupt,
111 #ifdef CONFIG_MIPS_MT_SMTC
112 .flags = IRQF_DISABLED,
113 #else
114 .flags = IRQF_DISABLED | IRQF_PERCPU,
115 #endif
116 .name = "timer",
117 };
118
119 #ifdef CONFIG_MIPS_MT_SMTC
120 DEFINE_PER_CPU(struct clock_event_device, smtc_dummy_clockevent_device);
121
122 static void smtc_set_mode(enum clock_event_mode mode,
123 struct clock_event_device *evt)
124 {
125 }
126
127 static void mips_broadcast(cpumask_t mask)
128 {
129 unsigned int cpu;
130
131 for_each_cpu_mask(cpu, mask)
132 smtc_send_ipi(cpu, SMTC_CLOCK_TICK, 0);
133 }
134
135 static void setup_smtc_dummy_clockevent_device(void)
136 {
137 //uint64_t mips_freq = mips_hpt_^frequency;
138 unsigned int cpu = smp_processor_id();
139 struct clock_event_device *cd;
140
141 cd = &per_cpu(smtc_dummy_clockevent_device, cpu);
142
143 cd->name = "SMTC";
144 cd->features = CLOCK_EVT_FEAT_DUMMY;
145
146 /* Calculate the min / max delta */
147 cd->mult = 0; //div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
148 cd->shift = 0; //32;
149 cd->max_delta_ns = 0; //clockevent_delta2ns(0x7fffffff, cd);
150 cd->min_delta_ns = 0; //clockevent_delta2ns(0x30, cd);
151
152 cd->rating = 200;
153 cd->irq = 17; //-1;
154 // if (cpu)
155 // cd->cpumask = CPU_MASK_ALL; // cpumask_of_cpu(cpu);
156 // else
157 cd->cpumask = cpumask_of_cpu(cpu);
158
159 cd->set_mode = smtc_set_mode;
160
161 cd->broadcast = mips_broadcast;
162
163 clockevents_register_device(cd);
164 }
165 #endif
166
167 static void mips_event_handler(struct clock_event_device *dev)
168 {
169 }
170
171 /*
172 * FIXME: This doesn't hold for the relocated E9000 compare interrupt.
173 */
174 static int c0_compare_int_pending(void)
175 {
176 return (read_c0_cause() >> cp0_compare_irq) & 0x100;
177 }
178
179 static int c0_compare_int_usable(void)
180 {
181 const unsigned int delta = 0x300000;
182 unsigned int cnt;
183
184 /*
185 * IP7 already pending? Try to clear it by acking the timer.
186 */
187 if (c0_compare_int_pending()) {
188 write_c0_compare(read_c0_compare());
189 irq_disable_hazard();
190 if (c0_compare_int_pending())
191 return 0;
192 }
193
194 cnt = read_c0_count();
195 cnt += delta;
196 write_c0_compare(cnt);
197
198 while ((long)(read_c0_count() - cnt) <= 0)
199 ; /* Wait for expiry */
200
201 if (!c0_compare_int_pending())
202 return 0;
203
204 write_c0_compare(read_c0_compare());
205 irq_disable_hazard();
206 if (c0_compare_int_pending())
207 return 0;
208
209 /*
210 * Feels like a real count / compare timer.
211 */
212 return 1;
213 }
214
215 void __cpuinit mips_clockevent_init(void)
216 {
217 uint64_t mips_freq = mips_hpt_frequency;
218 unsigned int cpu = smp_processor_id();
219 struct clock_event_device *cd;
220 unsigned int irq = MIPS_CPU_IRQ_BASE + 7;
221
222 if (!cpu_has_counter)
223 return;
224
225 #ifdef CONFIG_MIPS_MT_SMTC
226 setup_smtc_dummy_clockevent_device();
227
228 /*
229 * On SMTC we only register VPE0's compare interrupt as clockevent
230 * device.
231 */
232 if (cpu)
233 return;
234 #endif
235
236 if (!c0_compare_int_usable())
237 return;
238
239 cd = &per_cpu(mips_clockevent_device, cpu);
240
241 cd->name = "MIPS";
242 cd->features = CLOCK_EVT_FEAT_ONESHOT;
243
244 /* Calculate the min / max delta */
245 cd->mult = div_sc((unsigned long) mips_freq, NSEC_PER_SEC, 32);
246 cd->shift = 32;
247 cd->max_delta_ns = clockevent_delta2ns(0x7fffffff, cd);
248 cd->min_delta_ns = clockevent_delta2ns(0x300, cd);
249
250 cd->rating = 300;
251 cd->irq = irq;
252 #ifdef CONFIG_MIPS_MT_SMTC
253 cd->cpumask = CPU_MASK_ALL;
254 #else
255 cd->cpumask = cpumask_of_cpu(cpu);
256 #endif
257 cd->set_next_event = mips_next_event;
258 cd->set_mode = mips_set_mode;
259 cd->event_handler = mips_event_handler;
260
261 clockevents_register_device(cd);
262
263 if (!cp0_timer_irq_installed) {
264 #ifdef CONFIG_MIPS_MT_SMTC
265 #define CPUCTR_IMASKBIT (0x100 << cp0_compare_irq)
266 setup_irq_smtc(irq, &c0_compare_irqaction, CPUCTR_IMASKBIT);
267 #else
268 setup_irq(irq, &c0_compare_irqaction);
269 #endif /* CONFIG_MIPS_MT_SMTC */
270 cp0_timer_irq_installed = 1;
271 }
272 }