xref: /openbsd/sys/arch/octeon/dev/octeon_intr.c (revision cca36db2)
1 /*
2  * Copyright (c) 2000-2004 Opsycon AB  (www.opsycon.se)
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
14  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  */
26 
27 /*
28  * Interrupt support for Octeon Processor.
29  */
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/conf.h>
35 #include <sys/malloc.h>
36 #include <sys/device.h>
37 #include <sys/proc.h>
38 
39 #include <mips64/archtype.h>
40 
41 #include <machine/autoconf.h>
42 #include <machine/atomic.h>
43 #include <machine/intr.h>
44 
45 #include <octeon/dev/octeonreg.h>
46 #include <octeon/dev/iobusvar.h>
47 
48 extern bus_space_handle_t iobus_h;
49 
50 #define OCTEON_NINTS 64
51 
52 void	 octeon_intr_makemasks(void);
53 void	 octeon_splx(int);
54 uint32_t octeon_iointr(uint32_t, struct trap_frame *);
55 uint32_t octeon_aux(uint32_t, struct trap_frame *);
56 int	 octeon_iointr_skip(struct intrhand *, uint64_t, uint64_t);
57 void	 octeon_setintrmask(int);
58 
59 struct intrhand *octeon_intrhand[OCTEON_NINTS];
60 
61 #define	INTPRI_CIU_0	(INTPRI_CLOCK + 1)
62 
63 uint64_t octeon_intem[MAXCPUS];
64 uint64_t octeon_imask[MAXCPUS][NIPLS];
65 
66 void
67 octeon_intr_init(void)
68 {
69 	int cpuid = cpu_number();
70 	bus_space_write_8(&iobus_tag, iobus_h, CIU_IP2_EN0(cpuid), 0);
71 	bus_space_write_8(&iobus_tag, iobus_h, CIU_IP3_EN0(cpuid), 0);
72 	bus_space_write_8(&iobus_tag, iobus_h, CIU_IP2_EN1(cpuid), 0);
73 	bus_space_write_8(&iobus_tag, iobus_h, CIU_IP3_EN1(cpuid), 0);
74 
75 	set_intr(INTPRI_CIU_0, CR_INT_0, octeon_iointr);
76 	register_splx_handler(octeon_splx);
77 }
78 
79 /*
80  * Establish an interrupt handler called from the dispatcher.
81  * The interrupt function established should return zero if there was nothing
82  * to serve (no int) and non-zero when an interrupt was serviced.
83  *
84  * Interrupts are numbered from 1 and up where 1 maps to HW int 0.
85  * XXX There is no reason to keep this... except for hardcoded interrupts
86  * XXX in kernel configuration files...
87  */
88 void *
89 octeon_intr_establish(int irq, int level,
90     int (*ih_fun)(void *), void *ih_arg, const char *ih_what)
91 {
92 	int cpuid = cpu_number();
93 	struct intrhand **p, *q, *ih;
94 	int s;
95 
96 #ifdef DIAGNOSTIC
97 	if (irq >= OCTEON_NINTS || irq < 0)
98 		panic("intr_establish: illegal irq %d", irq);
99 #endif
100 
101 	ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT);
102 	if (ih == NULL)
103 		return NULL;
104 
105 	ih->ih_next = NULL;
106 	ih->ih_fun = ih_fun;
107 	ih->ih_arg = ih_arg;
108 	ih->ih_level = level;
109 	ih->ih_irq = irq;
110 	evcount_attach(&ih->ih_count, ih_what, (void *)&ih->ih_irq);
111 
112 	s = splhigh();
113 
114 	/*
115 	 * Figure out where to put the handler.
116 	 * This is O(N^2), but we want to preserve the order, and N is
117 	 * generally small.
118 	 */
119 	for (p = &octeon_intrhand[irq]; (q = *p) != NULL;
120 	    p = (struct intrhand **)&q->ih_next)
121 		;
122 	*p = ih;
123 
124 	octeon_intem[cpuid] |= 1UL << irq;
125 	octeon_intr_makemasks();
126 
127 	splx(s);	/* causes hw mask update */
128 
129 	return (ih);
130 }
131 
132 void
133 octeon_intr_disestablish(void *ih)
134 {
135 	/* XXX */
136 	panic("%s not implemented", __func__);
137 }
138 
139 void
140 octeon_splx(int newipl)
141 {
142 	struct cpu_info *ci = curcpu();
143 
144 	/* Update masks to new ipl. Order highly important! */
145 	__asm__ (".set noreorder\n");
146 	ci->ci_ipl = newipl;
147 	__asm__ ("sync\n\t.set reorder\n");
148 	if (CPU_IS_PRIMARY(ci))
149 		octeon_setintrmask(newipl);
150 	/* If we still have softints pending trigger processing. */
151 	if (ci->ci_softpending != 0 && newipl < IPL_SOFTINT)
152 		setsoftintr0();
153 }
154 
155 /*
156  * Recompute interrupt masks.
157  */
158 void
159 octeon_intr_makemasks()
160 {
161 	int cpuid = cpu_number();
162 	int irq, level;
163 	struct intrhand *q;
164 	uint intrlevel[OCTEON_NINTS];
165 
166 	/* First, figure out which levels each IRQ uses. */
167 	for (irq = 0; irq < OCTEON_NINTS; irq++) {
168 		uint levels = 0;
169 		for (q = (struct intrhand *)octeon_intrhand[irq]; q != NULL;
170 			q = q->ih_next)
171 			levels |= 1 << q->ih_level;
172 		intrlevel[irq] = levels;
173 	}
174 
175 	/*
176 	 * Then figure out which IRQs use each level.
177 	 * Note that we make sure never to overwrite imask[IPL_HIGH], in
178 	 * case an interrupt occurs during intr_disestablish() and causes
179 	 * an unfortunate splx() while we are here recomputing the masks.
180 	 */
181 	for (level = IPL_NONE; level < NIPLS; level++) {
182 		uint64_t irqs = 0;
183 		for (irq = 0; irq < OCTEON_NINTS; irq++)
184 			if (intrlevel[irq] & (1 << level))
185 				irqs |= 1UL << irq;
186 		octeon_imask[cpuid][level] = irqs;
187 	}
188 	/*
189 	 * There are tty, network and disk drivers that use free() at interrupt
190 	 * time, so vm > (tty | net | bio).
191 	 *
192 	 * Enforce a hierarchy that gives slow devices a better chance at not
193 	 * dropping data.
194 	 */
195 	octeon_imask[cpuid][IPL_NET] |= octeon_imask[cpuid][IPL_BIO];
196 	octeon_imask[cpuid][IPL_TTY] |= octeon_imask[cpuid][IPL_NET];
197 	octeon_imask[cpuid][IPL_VM] |= octeon_imask[cpuid][IPL_TTY];
198 	octeon_imask[cpuid][IPL_CLOCK] |= octeon_imask[cpuid][IPL_VM];
199 	octeon_imask[cpuid][IPL_HIGH] |= octeon_imask[cpuid][IPL_CLOCK];
200 	octeon_imask[cpuid][IPL_IPI] |= octeon_imask[cpuid][IPL_HIGH];
201 
202 	/*
203 	 * These are pseudo-levels.
204 	 */
205 	octeon_imask[cpuid][IPL_NONE] = 0;
206 }
207 
208 /*
209  * Interrupt dispatcher.
210  */
211 uint32_t
212 octeon_iointr(uint32_t hwpend, struct trap_frame *frame)
213 {
214 	struct cpu_info *ci = curcpu();
215 	int cpuid = cpu_number();
216 	uint64_t imr, isr, mask;
217 	int ipl;
218 	int bit;
219 	struct intrhand *ih;
220 	int rc;
221 	uint64_t sum0 = CIU_IP2_SUM0(cpuid);
222 	uint64_t en0 = CIU_IP2_EN0(cpuid);
223 
224 	isr = bus_space_read_8(&iobus_tag, iobus_h, sum0);
225 	imr = bus_space_read_8(&iobus_tag, iobus_h, en0);
226 	bit = 63;
227 
228 	isr &= imr;
229 	if (isr == 0)
230 		return 0;	/* not for us */
231 
232 	/*
233 	 * Mask all pending interrupts.
234 	 */
235 	bus_space_write_8(&iobus_tag, iobus_h, en0, imr & ~isr);
236 
237 	/*
238 	 * If interrupts are spl-masked, mask them and wait for splx()
239 	 * to reenable them when necessary.
240 	 */
241 	if ((mask = isr & octeon_imask[cpuid][frame->ipl]) != 0) {
242 		isr &= ~mask;
243 		imr &= ~mask;
244 	}
245 
246 	/*
247 	 * Now process allowed interrupts.
248 	 */
249 	if (isr != 0) {
250 		int lvl, bitno;
251 		uint64_t tmpisr;
252 
253 		__asm__ (".set noreorder\n");
254 		ipl = ci->ci_ipl;
255 		__asm__ ("sync\n\t.set reorder\n");
256 
257 		/* Service higher level interrupts first */
258 		for (lvl = NIPLS - 1; lvl != IPL_NONE; lvl--) {
259 			tmpisr = isr & (octeon_imask[cpuid][lvl] ^ octeon_imask[cpuid][lvl - 1]);
260 			if (tmpisr == 0)
261 				continue;
262 			for (bitno = bit, mask = 1UL << bitno; mask != 0;
263 			    bitno--, mask >>= 1) {
264 				if ((tmpisr & mask) == 0)
265 					continue;
266 
267 				rc = 0;
268 				for (ih = (struct intrhand *)octeon_intrhand[bitno];
269 					ih != NULL;
270 				    ih = ih->ih_next) {
271 #ifdef MULTIPROCESSOR
272 					u_int32_t sr;
273 #endif
274 					splraise(ih->ih_level);
275 #ifdef MULTIPROCESSOR
276 					if (ih->ih_level < IPL_IPI) {
277 						sr = getsr();
278 						ENABLEIPI();
279 						if (ipl < IPL_SCHED)
280 							__mp_lock(&kernel_lock);
281 					}
282 #endif
283 					if ((*ih->ih_fun)(ih->ih_arg) != 0) {
284 						rc = 1;
285 						atomic_add_uint64(&ih->ih_count.ec_count, 1);
286 					}
287 #ifdef MULTIPROCESSOR
288 					if (ih->ih_level < IPL_IPI) {
289 						if (ipl < IPL_SCHED)
290 							__mp_unlock(&kernel_lock);
291 						setsr(sr);
292 					}
293 #endif
294 					__asm__ (".set noreorder\n");
295 					ci->ci_ipl = ipl;
296 					__asm__ ("sync\n\t.set reorder\n");
297 				}
298 				if (rc == 0)
299 					printf("spurious crime interrupt %d\n", bitno);
300 
301 				isr ^= mask;
302 				if ((tmpisr ^= mask) == 0)
303 					break;
304 			}
305 		}
306 
307 		/*
308 		 * Reenable interrupts which have been serviced.
309 		 */
310 		bus_space_write_8(&iobus_tag, iobus_h, en0, imr);
311 	}
312 
313 	return hwpend;
314 }
315 
316 void
317 octeon_setintrmask(int level)
318 {
319 	int cpuid = cpu_number();
320 
321 	bus_space_write_8(&iobus_tag, iobus_h, CIU_IP2_EN0(cpuid),
322 		octeon_intem[cpuid] & ~octeon_imask[cpuid][level]);
323 }
324