xref: /netbsd-src/sys/arch/evbsh3/ap_ms104_sh4/ap_ms104_sh4_intr.c (revision d206c3458b7848902ac12dfb5de8f3a16c6f4067)
1 /*	$NetBSD: ap_ms104_sh4_intr.c,v 1.4 2020/11/21 16:21:24 thorpej 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.4 2020/11/21 16:21:24 thorpej Exp $");
30 
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/kmem.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
extintr_init(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
fakeintr(void * arg)86 fakeintr(void *arg)
87 {
88 
89 	return 0;
90 }
91 
92 void *
extintr_establish(int irq,int trigger,int level,int (* ih_fun)(void *),void * ih_arg)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 = kmem_alloc(sizeof(*ih), KM_SLEEP);
106 
107 	s = _cpu_intr_suspend();
108 
109 	switch (level) {
110 	default:
111 #if defined(DEBUG)
112 		panic("extintr_establish: unknown level %d", level);
113 		/*NOTREACHED*/
114 #endif
115 	case IPL_VM:
116 		break;
117 	}
118 
119 	eih = &extintr_handler[irq];
120 	if (eih->eih_func == NULL) {
121 		evtcode = 0x200 + (irq << 5);
122 		eih->eih_func = intc_intr_establish(evtcode, trigger, level,
123 		    extintr_intr_handler, eih);
124 	}
125 
126 	/*
127 	 * Figure out where to put the handler.
128 	 * This is O(N^2), but we want to preserve the order, and N is
129 	 * generally small.
130 	 */
131 	for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
132 		continue;
133 
134 	/*
135 	 * Actually install a fake handler momentarily, since we might be doing
136 	 * this with interrupts enabled and don't want the real routine called
137 	 * until masking is set up.
138 	 */
139 	fakehand.ih_level = level;
140 	*p = &fakehand;
141 
142 	/*
143 	 * Poke the real handler in now.
144 	 */
145 	memset(ih, 0, sizeof(*ih));
146 	ih->ih_fun = ih_fun;
147 	ih->ih_arg = ih_arg;
148 	ih->ih_next = NULL;
149 	ih->ih_enable = 1;
150 	ih->ih_level = level;
151 	ih->ih_irq = irq;
152 	name = extintr_names[irq];
153 	evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "ext", name);
154 	*p = ih;
155 
156 	if (++eih->eih_nih == 1) {
157 		uint8_t reg;
158 
159 		/* Unmask interrupt */
160 		switch (irq) {
161 		case 1: case 2:
162 			reg = _reg_read_1(EXTINTR_MASK4);
163 			reg |= 1 << (2 - irq);
164 			_reg_write_1(EXTINTR_MASK4, reg);
165 			break;
166 
167 		case 3: case 4: case 5: case 6:
168 			reg = _reg_read_1(EXTINTR_MASK3);
169 			reg |= 1 << (6 - irq);
170 			_reg_write_1(EXTINTR_MASK3, reg);
171 			break;
172 
173 		case 7: case 8: case 9: case 10:
174 			reg = _reg_read_1(EXTINTR_MASK2);
175 			reg |= 1 << (10 - irq);
176 			_reg_write_1(EXTINTR_MASK2, reg);
177 			break;
178 
179 		case 11: case 12: case 13: case 14:
180 			reg = _reg_read_1(EXTINTR_MASK1);
181 			reg |= 1 << (14 - irq);
182 			_reg_write_1(EXTINTR_MASK1, reg);
183 			break;
184 
185 		default:
186 			panic("unknown irq%d\n", irq);
187 			/*NOTREACHED*/
188 			break;
189 		}
190 	}
191 
192 	splx(s);
193 
194 	return (ih);
195 }
196 
197 void
extintr_disestablish(void * cookie)198 extintr_disestablish(void *cookie)
199 {
200 	struct intrhand *ih = (struct intrhand *)cookie;
201 	struct intrhand **p, *q;
202 	struct extintr_handler *eih;
203 	int irq;
204 	int s;
205 
206 	KDASSERT(ih != NULL);
207 
208 	s = _cpu_intr_suspend();
209 
210 	irq = ih->ih_irq;
211 	eih = &extintr_handler[irq];
212 
213 	/*
214 	 * Remove the handler from the chain.
215 	 * This is O(n^2), too.
216 	 */
217 	for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
218 		continue;
219 	if (q == NULL)
220 		panic("extintr_disestablish: handler not registered");
221 
222 	*p = q->ih_next;
223 
224 	evcnt_detach(&ih->ih_evcnt);
225 
226 	kmem_free((void *)ih, sizeof(*ih));
227 
228 	if (--eih->eih_nih == 0) {
229 		uint8_t reg;
230 
231 		intc_intr_disestablish(eih->eih_func);
232 		eih->eih_func = NULL;
233 
234 		/* Mask interrupt */
235 		switch (irq) {
236 		case 1: case 2:
237 			reg = _reg_read_1(EXTINTR_MASK4);
238 			reg &= ~(1 << (2 - irq));
239 			_reg_write_1(EXTINTR_MASK4, reg);
240 			break;
241 
242 		case 3: case 4: case 5: case 6:
243 			reg = _reg_read_1(EXTINTR_MASK3);
244 			reg &= ~(1 << (6 - irq));
245 			_reg_write_1(EXTINTR_MASK3, reg);
246 			break;
247 
248 		case 7: case 8: case 9: case 10:
249 			reg = _reg_read_1(EXTINTR_MASK2);
250 			reg &= ~(1 << (10 - irq));
251 			_reg_write_1(EXTINTR_MASK2, reg);
252 			break;
253 
254 		case 11: case 12: case 13: case 14:
255 			reg = _reg_read_1(EXTINTR_MASK1);
256 			reg &= ~(1 << (14 - irq));
257 			_reg_write_1(EXTINTR_MASK1, reg);
258 			break;
259 
260 		default:
261 			panic("unknown irq%d\n", irq);
262 			/*NOTREACHED*/
263 			break;
264 		}
265 	}
266 
267 	splx(s);
268 }
269 
270 static int
extintr_intr_handler(void * arg)271 extintr_intr_handler(void *arg)
272 {
273 	struct extintr_handler *eih = arg;
274 	struct intrhand *ih;
275 	int r;
276 
277 	if (__predict_true(eih != NULL)) {
278 		for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
279 			if (__predict_true(ih->ih_enable)) {
280 				r = (*ih->ih_fun)(ih->ih_arg);
281 				if (__predict_true(r != 0)) {
282 					ih->ih_evcnt.ev_count++;
283 				}
284 			}
285 		}
286 		return 1;
287 	}
288 	return 0;
289 }
290