xref: /netbsd/sys/arch/sh3/sh3/interrupt.c (revision bf9ec67e)
1 /*	$NetBSD: interrupt.c,v 1.2 2002/04/28 17:10:38 uch Exp $	*/
2 
3 /*-
4  * Copyright (c) 2002 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/malloc.h>
41 
42 #include <uvm/uvm_extern.h>	/* uvmexp.intrs */
43 
44 #include <net/netisr.h>
45 
46 #include <sh3/exception.h>
47 #include <sh3/clock.h>
48 #include <sh3/intcreg.h>
49 #include <sh3/tmureg.h>
50 #include <machine/intr.h>
51 
52 void intc_intr_priority(int, int);
53 struct intc_intrhand *intc_alloc_ih(void);
54 void intc_free_ih(struct intc_intrhand *);
55 int intc_unknown_intr(void *);
56 void netintr(void);
57 void tmu1_oneshot(void);
58 int tmu1_intr(void *);
59 void tmu2_oneshot(void);
60 int tmu2_intr(void *);
61 /*
62  * EVTCODE to intc_intrhand mapper.
63  * max #60 is SH7709_INTEVT2_ADC_ADI (0x980)
64  */
65 int8_t __intc_evtcode_to_ih[64];
66 
67 struct intc_intrhand __intc_intrhand[_INTR_N + 1] = {
68 	/* Place holder interrupt handler for unregistered interrupt. */
69 	[0] = { .ih_func = intc_unknown_intr, .ih_level = 0xf0 }
70 };
71 
72 struct sh_soft_intr sh_soft_intrs[_IPL_NSOFT];
73 struct sh_soft_intrhand *softnet_intrhand;
74 
75 /*
76  * SH INTC support.
77  */
78 void
79 intc_init()
80 {
81 
82 	switch (cpu_product) {
83 	case CPU_PRODUCT_7709:
84 	case CPU_PRODUCT_7709A:
85 		_reg_write_2(SH7709_IPRC, 0);
86 		_reg_write_2(SH7709_IPRD, 0);
87 		_reg_write_2(SH7709_IPRE, 0);
88 		/* FALLTHROUGH */
89 	case CPU_PRODUCT_7708:
90 	case CPU_PRODUCT_7708S:
91 	case CPU_PRODUCT_7708R:
92 		_reg_write_2(SH3_IPRA, 0);
93 		_reg_write_2(SH3_IPRB, 0);
94 		break;
95 	case CPU_PRODUCT_7750S:
96 		_reg_write_2(SH4_IPRD, 0);
97 		/* FALLTHROUGH */
98 	case CPU_PRODUCT_7750:
99 		_reg_write_2(SH4_IPRA, 0);
100 		_reg_write_2(SH4_IPRB, 0);
101 		_reg_write_2(SH4_IPRC, 0);
102 		break;
103 	}
104 }
105 
106 void *
107 intc_intr_establish(int evtcode, int trigger, int level,
108     int (*ih_func)(void *), void *ih_arg)
109 {
110 	struct intc_intrhand *ih;
111 
112 	KDASSERT(evtcode >= 0x200 && level > 0);
113 
114 	ih = intc_alloc_ih();
115 	ih->ih_func	= ih_func;
116 	ih->ih_arg	= ih_arg;
117 	ih->ih_level	= level << 4;	/* convert to SR.IMASK format. */
118 	ih->ih_evtcode	= evtcode;
119 
120 	/* Map interrupt handler */
121 	EVTCODE_TO_IH_INDEX(evtcode) = ih->ih_idx;
122 
123 	/* Priority */
124 	intc_intr_priority(evtcode, level);
125 
126 	/* Sense select (SH7709, SH7709A only) XXX notyet */
127 
128 	return (ih);
129 }
130 
131 void
132 intc_intr_disestablish(void *arg)
133 {
134 	struct intc_intrhand *ih = arg;
135 	int evtcode = ih->ih_evtcode;
136 
137 	/* Mask interrupt if IPR can manage it. if not, cascated ICU will do */
138 	intc_intr_priority(evtcode, 0);
139 
140 	/* Unmap interrupt handler */
141 	EVTCODE_TO_IH_INDEX(evtcode) = 0;
142 
143 	intc_free_ih(ih);
144 }
145 
146 /*
147  * void intc_intr_priority(int evtcode, int level)
148  *	Setup interrupt priority register.
149  *	SH7708, SH7708S, SH7708R, SH7750, SH7750S ... evtcode is INTEVT
150  *	SH7709, SH7709A				  ... evtcode is INTEVT2
151  */
152 void
153 intc_intr_priority(int evtcode, int level)
154 {
155 #define	SET_LEVEL(r, pos, level)					\
156 	r = (r & ~(0xf << (pos))) | (level << (pos))
157 #define	__SH_IPR(sh, x, pos, level)					\
158 do {									\
159 	r = _reg_read_2(SH ## sh ## _IPR ## x);				\
160 	SET_LEVEL(r, pos, level);					\
161 	_reg_write_2(SH ## sh ## _IPR ## x, r);				\
162 } while (/*CONSTCOND*/0)
163 #define	SH3_IPR(x, pos, level)		__SH_IPR(3, x, pos, level)
164 #define	SH4_IPR(x, pos, level)		__SH_IPR(4, x, pos, level)
165 #define	SH7709_IPR(x, pos, level)	__SH_IPR(7709, x, pos, level)
166 #define	SH_IPR(x, pos, level)						\
167 do {									\
168 	if (CPU_IS_SH3)							\
169 		SH3_IPR(x, pos, level);					\
170 	else								\
171 		SH4_IPR(x, pos, level);					\
172 } while (/*CONSTCOND*/0)
173 
174 	u_int16_t r;
175 
176 	switch (evtcode) {
177 	case SH_INTEVT_TMU0_TUNI0:
178 		SH_IPR(A, 12, level);
179 		break;
180 	case SH_INTEVT_TMU1_TUNI1:
181 		SH_IPR(A, 8, level);
182 		break;
183 	case SH_INTEVT_TMU2_TUNI2:
184 		SH_IPR(A, 4, level);
185 		break;
186 	case SH_INTEVT_SCI_ERI:
187 	case SH_INTEVT_SCI_RXI:
188 	case SH_INTEVT_SCI_TXI:
189 	case SH_INTEVT_SCI_TEI:
190 		SH_IPR(B, 4, level);
191 		break;
192 	case SH7709_INTEVT2_SCIF_ERI:
193 	case SH7709_INTEVT2_SCIF_RXI:
194 	case SH7709_INTEVT2_SCIF_BRI:
195 	case SH7709_INTEVT2_SCIF_TXI:
196 		SH7709_IPR(E, 4, level);
197 		break;
198 	case SH7709_INTEVT2_IRQ4:
199 		SH7709_IPR(D, 0, level);
200 		break;
201 	case SH4_INTEVT_SCIF_ERI:
202 	case SH4_INTEVT_SCIF_RXI:
203 	case SH4_INTEVT_SCIF_BRI:
204 	case SH4_INTEVT_SCIF_TXI:
205 		SH4_IPR(C, 4, level);
206 		break;
207 	}
208 }
209 
210 /*
211  * Interrupt handler holder allocater.
212  */
213 struct intc_intrhand *
214 intc_alloc_ih()
215 {
216 	/* #0 is reserved for unregistered interrupt. */
217 	struct intc_intrhand *ih = &__intc_intrhand[1];
218 	int i;
219 
220 	for (i = 1; i <= _INTR_N; i++, ih++)
221 		if (ih->ih_idx == 0) {	/* no driver use this. */
222 			ih->ih_idx = i;	/* register myself */
223 			return (ih);
224 		}
225 
226 	panic("increase _INTR_N greater than %d\n", _INTR_N);
227 	return (NULL);
228 }
229 
230 void
231 intc_free_ih(struct intc_intrhand *ih)
232 {
233 
234 	memset(ih, 0, sizeof(*ih));
235 }
236 
237 /* Place-holder for debugging */
238 int
239 intc_unknown_intr(void *arg)
240 {
241 
242 	printf("INTEVT=0x%x", _reg_read_4(SH_(INTEVT)));
243 	if (cpu_product == CPU_PRODUCT_7709 || cpu_product == CPU_PRODUCT_7709A)
244 		printf(" INTEVT2=0x%x", _reg_read_4(SH7709_INTEVT2));
245 	printf("\n");
246 
247 	panic("unknown interrupt");
248 	/* NOTREACHED */
249 	return (0);
250 }
251 
252 /*
253  * Software interrupt support
254  */
255 void
256 softintr_init()
257 {
258 	static const char *softintr_names[] = IPL_SOFTNAMES;
259 	struct sh_soft_intr *asi;
260 	int i;
261 
262 	for (i = 0; i < _IPL_NSOFT; i++) {
263 		asi = &sh_soft_intrs[i];
264 		TAILQ_INIT(&asi->softintr_q);
265 
266 		asi->softintr_ipl = IPL_SOFT + i;
267 		simple_lock_init(&asi->softintr_slock);
268 		evcnt_attach_dynamic(&asi->softintr_evcnt, EVCNT_TYPE_INTR,
269 		    NULL, "soft", softintr_names[i]);
270 	}
271 
272 	/* XXX Establish legacy soft interrupt handlers. */
273 	softnet_intrhand = softintr_establish(IPL_SOFTNET,
274 	    (void (*)(void *))netintr, NULL);
275 	KDASSERT(softnet_intrhand != NULL);
276 
277 	intc_intr_establish(SH_INTEVT_TMU1_TUNI1, IST_LEVEL, IPL_SOFT,
278 	    tmu1_intr, NULL);
279 	intc_intr_establish(SH_INTEVT_TMU2_TUNI2, IST_LEVEL, IPL_SOFTNET,
280 	    tmu2_intr, NULL);
281 }
282 
283 void
284 softintr_dispatch(int ipl)
285 {
286 	struct sh_soft_intr *asi;
287 	struct sh_soft_intrhand *sih;
288 	int s;
289 
290 	s = _cpu_intr_suspend();
291 
292 	asi = &sh_soft_intrs[ipl - IPL_SOFT];
293 
294 	if (TAILQ_FIRST(&asi->softintr_q) != NULL)
295 		asi->softintr_evcnt.ev_count++;
296 
297 	while ((sih = TAILQ_FIRST(&asi->softintr_q)) != NULL) {
298 		TAILQ_REMOVE(&asi->softintr_q, sih, sih_q);
299 		sih->sih_pending = 0;
300 
301 		uvmexp.softs++;
302 
303 		_cpu_intr_resume(s);
304 		(*sih->sih_fn)(sih->sih_arg);
305 		s = _cpu_intr_suspend();
306 	}
307 
308 	_cpu_intr_resume(s);
309 }
310 
311 void
312 setsoft(int ipl)
313 {
314 
315 	if (ipl < IPL_SOFTNET)
316 		tmu1_oneshot();
317 	else
318 		tmu2_oneshot();
319 }
320 
321 /* Register a software interrupt handler. */
322 void *
323 softintr_establish(int ipl, void (*func)(void *), void *arg)
324 {
325 	struct sh_soft_intr *asi;
326 	struct sh_soft_intrhand *sih;
327 	int s;
328 
329 	if (__predict_false(ipl >= (IPL_SOFT + _IPL_NSOFT) ||
330 			    ipl < IPL_SOFT))
331 		panic("softintr_establish");
332 
333 	sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT);
334 
335 	s = _cpu_intr_suspend();
336 	asi = &sh_soft_intrs[ipl - IPL_SOFT];
337 	if (__predict_true(sih != NULL)) {
338 		sih->sih_intrhead = asi;
339 		sih->sih_fn = func;
340 		sih->sih_arg = arg;
341 		sih->sih_pending = 0;
342 	}
343 	_cpu_intr_resume(s);
344 
345 	return (sih);
346 }
347 
348 /* Unregister a software interrupt handler. */
349 void
350 softintr_disestablish(void *arg)
351 {
352 	struct sh_soft_intrhand *sih = arg;
353 	struct sh_soft_intr *asi = sih->sih_intrhead;
354 	int s;
355 
356 	s = _cpu_intr_suspend();
357 	if (sih->sih_pending) {
358 		TAILQ_REMOVE(&asi->softintr_q, sih, sih_q);
359 		sih->sih_pending = 0;
360 	}
361 	_cpu_intr_resume(s);
362 
363 	free(sih, M_DEVBUF);
364 }
365 
366 /*
367  * Software (low priority) network interrupt. i.e. softnet().
368  */
369 void
370 netintr()
371 {
372 #define	DONETISR(bit, fn)						\
373 	do {								\
374 		if (n & (1 << bit))					\
375 			fn();						\
376 	} while (/*CONSTCOND*/0)
377 
378 	int s, n;
379 
380 	s = splnet();
381 	n = netisr;
382 	netisr = 0;
383 	splx(s);
384 #include <net/netisr_dispatch.h>
385 
386 #undef DONETISR
387 }
388 
389 /*
390  * Software interrupt is simulated with TMU one-shot timer.
391  */
392 void
393 tmu1_oneshot()
394 {
395 
396 	_reg_write_4(SH_(TCNT1), 0);
397 	_reg_write_1(SH_(TSTR), _reg_read_1(SH_(TSTR)) | TSTR_STR1);
398 }
399 
400 int
401 tmu1_intr(void *arg)
402 {
403 
404 	_reg_write_1(SH_(TSTR), _reg_read_1(SH_(TSTR)) & ~TSTR_STR1);
405 	_reg_write_2(SH_(TCR1), _reg_read_2(SH_(TCR1)) & ~TCR_UNF);
406 
407 	softintr_dispatch(IPL_SOFTCLOCK);
408 	softintr_dispatch(IPL_SOFT);
409 
410 	return (0);
411 }
412 
413 void
414 tmu2_oneshot()
415 {
416 
417 	_reg_write_4(SH_(TCNT2), 0);
418 	_reg_write_1(SH_(TSTR), _reg_read_1(SH_(TSTR)) | TSTR_STR2);
419 }
420 
421 int
422 tmu2_intr(void *arg)
423 {
424 
425 	_reg_write_1(SH_(TSTR), _reg_read_1(SH_(TSTR)) & ~TSTR_STR2);
426 	_reg_write_2(SH_(TCR2), _reg_read_2(SH_(TCR2)) & ~TCR_UNF);
427 
428 	softintr_dispatch(IPL_SOFTSERIAL);
429 	softintr_dispatch(IPL_SOFTNET);
430 
431 	return (0);
432 }
433