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