xref: /netbsd-src/sys/arch/evbsh3/ap_ms104_sh4/ap_ms104_sh4_intr.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
1 /*	$NetBSD: ap_ms104_sh4_intr.c,v 1.2 2012/01/21 19:44: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.2 2012/01/21 19:44: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 
47 struct 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 
57 struct extintr_handler {
58 	void		*eih_func;
59 	struct intrhand	*eih_ih;
60 	int		eih_nih;
61 };
62 static struct extintr_handler extintr_handler[_N_EXTINTR];
63 
64 static 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 
71 static int fakeintr(void *arg);
72 static int extintr_intr_handler(void *arg);
73 
74 void
75 extintr_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*/
85 static int
86 fakeintr(void *arg)
87 {
88 
89 	return 0;
90 }
91 
92 void *
93 extintr_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 
199 void
200 extintr_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 
272 static int
273 extintr_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