1/*	$NetBSD: intr.c,v 1.4 2010/12/20 00:25:36 matt Exp $	*/
2
3/*-
4 * Copyright (C) 2005 NONAKA Kimihiro <nonaka@netbsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28#include <sys/cdefs.h>
29__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.4 2010/12/20 00:25:36 matt Exp $");
30
31#include <sys/param.h>
32#include <sys/systm.h>
33#include <sys/kernel.h>
34#include <sys/malloc.h>
35#include <sys/device.h>
36
37#include <sh3/exception.h>
38
39#include <machine/intr.h>
40
41#define	_N_EXTINTR		8
42
43#define	LANDISK_INTEN		0xb0000005
44#define	INTEN_ALL_MASK		0x00
45
46struct intrhand {
47	int	(*ih_fun)(void *);
48	void	*ih_arg;
49	struct	intrhand *ih_next;
50	int	ih_enable;
51	int	ih_level;
52	int	ih_irq;
53	struct evcnt ih_evcnt;
54};
55
56struct extintr_handler {
57	int		(*eih_func)(void *eih_arg);
58	void		*eih_arg;
59	struct intrhand	*eih_ih;
60	int		eih_nih;
61};
62
63static struct extintr_handler extintr_handler[_N_EXTINTR];
64
65static const char *extintr_names[_N_EXTINTR] = {
66	"irq5", "irq6", "irq7", "irq8",
67	"irq9", "irq10", "irq11", "irq12"
68};
69
70static int fakeintr(void *arg);
71static int extintr_intr_handler(void *arg);
72
73void
74intc_intr(int ssr, int spc, int ssp)
75{
76	struct intc_intrhand *ih;
77	struct clockframe cf;
78	int evtcode;
79
80	curcpu()->ci_data.cpu_nintr++;
81
82	evtcode = _reg_read_4(SH4_INTEVT);
83	ih = EVTCODE_IH(evtcode);
84	KDASSERT(ih->ih_func);
85
86	switch (evtcode) {
87#if 0
88#define	IRL(irq)	(0x200 + ((irq) << 5))
89	case IRL(5): case IRL(6): case IRL(7): case IRL(8):
90	case IRL(9): case IRL(10): case IRL(11): case IRL(12):
91	{
92		int level;
93		uint8_t inten, bit;
94
95		bit = 1 << (EVTCODE_TO_MAP_INDEX(evtcode) - 5);
96		inten = _reg_read_1(LANDISK_INTEN);
97		_reg_write_1(LANDISK_INTEN, inten & ~bit);
98		level = (_IPL_NSOFT + 1) << 4;	/* disable softintr */
99		ssr &= 0xf0;
100		if (level < ssr)
101			level = ssr;
102		(void)_cpu_intr_resume(level);
103		(*ih->ih_func)(ih->ih_arg);
104		_reg_write_1(LANDISK_INTEN, inten);
105		break;
106	}
107#endif
108	default:
109		(void)_cpu_intr_resume(ih->ih_level);
110		(*ih->ih_func)(ih->ih_arg);
111		break;
112
113	case SH_INTEVT_TMU0_TUNI0:
114		(void)_cpu_intr_resume(ih->ih_level);
115		cf.spc = spc;
116		cf.ssr = ssr;
117		cf.ssp = ssp;
118		(*ih->ih_func)(&cf);
119		break;
120
121	case SH_INTEVT_NMI:
122		printf("NMI ignored.\n");
123		break;
124	}
125}
126
127void
128intr_init(void)
129{
130
131	_reg_write_1(LANDISK_INTEN, INTEN_ALL_MASK);
132}
133
134void *
135extintr_establish(int irq, int level, int (*ih_fun)(void *), void *ih_arg)
136{
137	static struct intrhand fakehand = {fakeintr};
138	struct extintr_handler *eih;
139	struct intrhand **p, *q, *ih;
140	const char *name;
141	int evtcode;
142	int s;
143
144	KDASSERT(irq >= 5 && irq <= 12);
145
146	ih = malloc(sizeof(*ih), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
147	if (ih == NULL)
148		panic("intr_establish: can't malloc handler info");
149
150	s = _cpu_intr_suspend();
151
152	switch (level) {
153	default:
154#if defined(DEBUG)
155		panic("extintr_establish: unknown level %d", level);
156		/*NOTREACHED*/
157#endif
158	case IPL_VM:
159		break;
160	}
161
162	eih = &extintr_handler[irq - 5];
163	if (eih->eih_func == NULL) {
164		evtcode = 0x200 + (irq << 5);
165		eih->eih_func = intc_intr_establish(evtcode, IST_LEVEL, level,
166		    extintr_intr_handler, eih);
167	}
168
169	/*
170	 * Figure out where to put the handler.
171	 * This is O(N^2), but we want to preserve the order, and N is
172	 * generally small.
173	 */
174	for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
175		continue;
176
177	/*
178	 * Actually install a fake handler momentarily, since we might be doing
179	 * this with interrupts enabled and don't want the real routine called
180	 * until masking is set up.
181	 */
182	fakehand.ih_level = level;
183	*p = &fakehand;
184
185	/*
186	 * Poke the real handler in now.
187	 */
188	memset(ih, 0, sizeof(*ih));
189	ih->ih_fun = ih_fun;
190	ih->ih_arg = ih_arg;
191	ih->ih_next = NULL;
192	ih->ih_enable = 1;
193	ih->ih_level = level;
194	ih->ih_irq = irq - 5;
195	name = extintr_names[irq - 5];
196	evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR,
197	    NULL, "ext", name);
198	*p = ih;
199
200	if (++eih->eih_nih == 1) {
201		/* Unmask interrupt */
202		_reg_bset_1(LANDISK_INTEN, (1 << (irq - 5)));
203	}
204
205	splx(s);
206
207	return (ih);
208}
209
210void
211extintr_disestablish(void *aux)
212{
213	struct intrhand *ih = aux;
214	struct intrhand **p, *q;
215	struct extintr_handler *eih;
216	int irq;
217	int s;
218
219	KDASSERT(ih != NULL);
220
221	s = _cpu_intr_suspend();
222
223	irq = ih->ih_irq;
224	eih = &extintr_handler[irq];
225
226	/*
227	 * Remove the handler from the chain.
228	 * This is O(n^2), too.
229	 */
230	for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
231		continue;
232	if (q == NULL)
233		panic("extintr_disestablish: handler not registered");
234
235	*p = q->ih_next;
236
237	evcnt_detach(&ih->ih_evcnt);
238
239	free((void *)ih, M_DEVBUF);
240
241	if (--eih->eih_nih == 0) {
242		intc_intr_disestablish(eih->eih_func);
243
244		/* Mask interrupt */
245		_reg_bclr_1(LANDISK_INTEN, (1 << irq));
246	}
247
248	splx(s);
249}
250
251void
252extintr_enable(void *aux)
253{
254	struct intrhand *ih = aux;
255	struct intrhand *p, *q;
256	struct extintr_handler *eih;
257	int irq;
258	int cnt;
259	int s;
260
261	KDASSERT(ih != NULL);
262
263	s = _cpu_intr_suspend();
264
265	irq = ih->ih_irq;
266	KDASSERT(irq >= 0 && irq < 8);
267	eih = &extintr_handler[irq];
268	for (cnt = 0, p = eih->eih_ih, q = NULL; p != NULL; p = p->ih_next) {
269		if (p->ih_enable) {
270			cnt++;
271		}
272		if (p == ih) {
273			q = p;
274			p->ih_enable = 1;
275		}
276	}
277	KDASSERT(q != NULL);
278
279	if (cnt == 0) {
280		/* Unmask interrupt */
281		_reg_bset_1(LANDISK_INTEN, (1 << irq));
282	}
283
284	splx(s);
285}
286
287void
288extintr_disable(void *aux)
289{
290	struct intrhand *ih = aux;
291	struct intrhand *p, *q;
292	struct extintr_handler *eih;
293	int irq;
294	int cnt;
295	int s;
296
297	KDASSERT(ih != NULL);
298
299	s = _cpu_intr_suspend();
300
301	irq = ih->ih_irq;
302	KDASSERT(irq >= 0 && irq < 8);
303	eih = &extintr_handler[irq];
304	for (cnt = 0, p = eih->eih_ih, q = NULL; p != NULL; p = p->ih_next) {
305		if (p == ih) {
306			q = p;
307			p->ih_enable = 0;
308		}
309		if (!ih->ih_enable) {
310			cnt++;
311		}
312	}
313	KDASSERT(q != NULL);
314
315	if (cnt == 0) {
316		/* Mask interrupt */
317		_reg_bclr_1(LANDISK_INTEN, (1 << irq));
318	}
319
320	splx(s);
321}
322
323void
324extintr_disable_by_num(int irq)
325{
326	struct extintr_handler *eih;
327	struct intrhand *ih;
328	int s;
329
330	KDASSERT(irq >= 5 && irq <= 12);
331
332	s = _cpu_intr_suspend();
333	eih = &extintr_handler[irq - 5];
334	for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
335		ih->ih_enable = 0;
336	}
337	/* Mask interrupt */
338	_reg_bclr_1(LANDISK_INTEN, (1 << irq));
339	splx(s);
340}
341
342static int
343fakeintr(void *arg)
344{
345
346	return 0;
347}
348
349static int
350extintr_intr_handler(void *arg)
351{
352	struct extintr_handler *eih = arg;
353	struct intrhand *ih;
354	int r;
355
356	if (__predict_true(eih != NULL)) {
357		for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
358			if (__predict_true(ih->ih_enable)) {
359				r = (*ih->ih_fun)(ih->ih_arg);
360				if (__predict_true(r != 0)) {
361					ih->ih_evcnt.ev_count++;
362				}
363			}
364		}
365		return 1;
366	}
367	return 0;
368}
369