1/*	$NetBSD: ap_ms104_sh4_intr.c,v 1.1 2010/04/06 15:54:29 nonaka Exp $	*/
2
3/*-
4 * Copyright (C) 2009 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: ap_ms104_sh4_intr.c,v 1.1 2010/04/06 15:54:29 nonaka 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/devreg.h>
38#include <sh3/exception.h>
39
40#include <machine/intr.h>
41
42#include <evbsh3/ap_ms104_sh4/ap_ms104_sh4reg.h>
43#include <evbsh3/ap_ms104_sh4/ap_ms104_sh4var.h>
44
45#define	_N_EXTINTR	16
46
47struct intrhand {
48	int	(*ih_fun)(void *);
49	void	*ih_arg;
50	struct	intrhand *ih_next;
51	int	ih_enable;
52	int	ih_level;
53	int	ih_irq;
54	struct evcnt ih_evcnt;
55};
56
57struct extintr_handler {
58	void		*eih_func;
59	struct intrhand	*eih_ih;
60	int		eih_nih;
61};
62static struct extintr_handler extintr_handler[_N_EXTINTR];
63
64static const char *extintr_names[_N_EXTINTR] = {
65	"irq0", "irq1", "irq2", "irq3",
66	"irq4", "irq5", "irq6", "irq7",
67	"irq8", "irq9", "irq10", "irq11",
68	"irq12", "irq13", "irq14", "irq15"
69};
70
71static int fakeintr(void *arg);
72static int extintr_intr_handler(void *arg);
73
74void
75extintr_init(void)
76{
77
78	_reg_write_1(EXTINTR_MASK1, 0);
79	_reg_write_1(EXTINTR_MASK2, 0);
80	_reg_write_1(EXTINTR_MASK3, 0);
81	_reg_write_1(EXTINTR_MASK4, 0);
82}
83
84/*ARGSUSED*/
85static int
86fakeintr(void *arg)
87{
88
89	return 0;
90}
91
92void *
93extintr_establish(int irq, int trigger, int level,
94    int (*ih_fun)(void *), void *ih_arg)
95{
96	static struct intrhand fakehand = {fakeintr};
97	struct extintr_handler *eih;
98	struct intrhand **p, *q, *ih;
99	const char *name;
100	int evtcode;
101	int s;
102
103	KDASSERT(irq >= 1 && irq <= 14);
104
105	ih = malloc(sizeof(*ih), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
106	if (ih == NULL)
107		panic("intr_establish: can't malloc handler info");
108
109	s = _cpu_intr_suspend();
110
111	switch (level) {
112	default:
113#if defined(DEBUG)
114		panic("extintr_establish: unknown level %d", level);
115		/*NOTREACHED*/
116#endif
117	case IPL_VM:
118		break;
119	}
120
121	eih = &extintr_handler[irq];
122	if (eih->eih_func == NULL) {
123		evtcode = 0x200 + (irq << 5);
124		eih->eih_func = intc_intr_establish(evtcode, trigger, level,
125		    extintr_intr_handler, eih);
126	}
127
128	/*
129	 * Figure out where to put the handler.
130	 * This is O(N^2), but we want to preserve the order, and N is
131	 * generally small.
132	 */
133	for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
134		continue;
135
136	/*
137	 * Actually install a fake handler momentarily, since we might be doing
138	 * this with interrupts enabled and don't want the real routine called
139	 * until masking is set up.
140	 */
141	fakehand.ih_level = level;
142	*p = &fakehand;
143
144	/*
145	 * Poke the real handler in now.
146	 */
147	memset(ih, 0, sizeof(*ih));
148	ih->ih_fun = ih_fun;
149	ih->ih_arg = ih_arg;
150	ih->ih_next = NULL;
151	ih->ih_enable = 1;
152	ih->ih_level = level;
153	ih->ih_irq = irq;
154	name = extintr_names[irq];
155	evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "ext", name);
156	*p = ih;
157
158	if (++eih->eih_nih == 1) {
159		uint8_t reg;
160
161		/* Unmask interrupt */
162		switch (irq) {
163		case 1: case 2:
164			reg = _reg_read_1(EXTINTR_MASK4);
165			reg |= 1 << (2 - irq);
166			_reg_write_1(EXTINTR_MASK4, reg);
167			break;
168
169		case 3: case 4: case 5: case 6:
170			reg = _reg_read_1(EXTINTR_MASK3);
171			reg |= 1 << (6 - irq);
172			_reg_write_1(EXTINTR_MASK3, reg);
173			break;
174
175		case 7: case 8: case 9: case 10:
176			reg = _reg_read_1(EXTINTR_MASK2);
177			reg |= 1 << (10 - irq);
178			_reg_write_1(EXTINTR_MASK2, reg);
179			break;
180
181		case 11: case 12: case 13: case 14:
182			reg = _reg_read_1(EXTINTR_MASK1);
183			reg |= 1 << (14 - irq);
184			_reg_write_1(EXTINTR_MASK1, reg);
185			break;
186
187		default:
188			panic("unknown irq%d\n", irq);
189			/*NOTREACHED*/
190			break;
191		}
192	}
193
194	splx(s);
195
196	return (ih);
197}
198
199void
200extintr_disestablish(void *cookie)
201{
202	struct intrhand *ih = (struct intrhand *)cookie;
203	struct intrhand **p, *q;
204	struct extintr_handler *eih;
205	int irq;
206	int s;
207
208	KDASSERT(ih != NULL);
209
210	s = _cpu_intr_suspend();
211
212	irq = ih->ih_irq;
213	eih = &extintr_handler[irq];
214
215	/*
216	 * Remove the handler from the chain.
217	 * This is O(n^2), too.
218	 */
219	for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
220		continue;
221	if (q == NULL)
222		panic("extintr_disestablish: handler not registered");
223
224	*p = q->ih_next;
225
226	evcnt_detach(&ih->ih_evcnt);
227
228	free((void *)ih, M_DEVBUF);
229
230	if (--eih->eih_nih == 0) {
231		uint8_t reg;
232
233		intc_intr_disestablish(eih->eih_func);
234		eih->eih_func = NULL;
235
236		/* Mask interrupt */
237		switch (irq) {
238		case 1: case 2:
239			reg = _reg_read_1(EXTINTR_MASK4);
240			reg &= ~(1 << (2 - irq));
241			_reg_write_1(EXTINTR_MASK4, reg);
242			break;
243
244		case 3: case 4: case 5: case 6:
245			reg = _reg_read_1(EXTINTR_MASK3);
246			reg &= ~(1 << (6 - irq));
247			_reg_write_1(EXTINTR_MASK3, reg);
248			break;
249
250		case 7: case 8: case 9: case 10:
251			reg = _reg_read_1(EXTINTR_MASK2);
252			reg &= ~(1 << (10 - irq));
253			_reg_write_1(EXTINTR_MASK2, reg);
254			break;
255
256		case 11: case 12: case 13: case 14:
257			reg = _reg_read_1(EXTINTR_MASK1);
258			reg &= ~(1 << (14 - irq));
259			_reg_write_1(EXTINTR_MASK1, reg);
260			break;
261
262		default:
263			panic("unknown irq%d\n", irq);
264			/*NOTREACHED*/
265			break;
266		}
267	}
268
269	splx(s);
270}
271
272static int
273extintr_intr_handler(void *arg)
274{
275	struct extintr_handler *eih = arg;
276	struct intrhand *ih;
277	int r;
278
279	if (__predict_true(eih != NULL)) {
280		for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
281			if (__predict_true(ih->ih_enable)) {
282				r = (*ih->ih_fun)(ih->ih_arg);
283				if (__predict_true(r != 0)) {
284					ih->ih_evcnt.ev_count++;
285				}
286			}
287		}
288		return 1;
289	}
290	return 0;
291}
292