xref: /netbsd-src/sys/arch/evbsh3/ap_ms104_sh4/ap_ms104_sh4_intr.c (revision d206c3458b7848902ac12dfb5de8f3a16c6f4067)
1*d206c345Sthorpej /*	$NetBSD: ap_ms104_sh4_intr.c,v 1.4 2020/11/21 16:21:24 thorpej Exp $	*/
2238763f6Snonaka 
3238763f6Snonaka /*-
42388feefSnonaka  * Copyright (C) 2009 NONAKA Kimihiro <nonaka@netbsd.org>
5238763f6Snonaka  * All rights reserved.
6238763f6Snonaka  *
7238763f6Snonaka  * Redistribution and use in source and binary forms, with or without
8238763f6Snonaka  * modification, are permitted provided that the following conditions
9238763f6Snonaka  * are met:
10238763f6Snonaka  * 1. Redistributions of source code must retain the above copyright
11238763f6Snonaka  *    notice, this list of conditions and the following disclaimer.
12238763f6Snonaka  * 2. Redistributions in binary form must reproduce the above copyright
13238763f6Snonaka  *    notice, this list of conditions and the following disclaimer in the
14238763f6Snonaka  *    documentation and/or other materials provided with the distribution.
15238763f6Snonaka  *
162388feefSnonaka  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
172388feefSnonaka  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
182388feefSnonaka  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
192388feefSnonaka  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
202388feefSnonaka  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
212388feefSnonaka  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
222388feefSnonaka  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
232388feefSnonaka  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
242388feefSnonaka  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
252388feefSnonaka  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26238763f6Snonaka  */
27238763f6Snonaka 
28238763f6Snonaka #include <sys/cdefs.h>
29*d206c345Sthorpej __KERNEL_RCSID(0, "$NetBSD: ap_ms104_sh4_intr.c,v 1.4 2020/11/21 16:21:24 thorpej Exp $");
30238763f6Snonaka 
31238763f6Snonaka #include <sys/param.h>
32238763f6Snonaka #include <sys/systm.h>
33238763f6Snonaka #include <sys/kernel.h>
34*d206c345Sthorpej #include <sys/kmem.h>
35238763f6Snonaka #include <sys/device.h>
36238763f6Snonaka 
37238763f6Snonaka #include <sh3/devreg.h>
38238763f6Snonaka #include <sh3/exception.h>
39238763f6Snonaka 
40238763f6Snonaka #include <machine/intr.h>
41238763f6Snonaka 
42238763f6Snonaka #include <evbsh3/ap_ms104_sh4/ap_ms104_sh4reg.h>
43238763f6Snonaka #include <evbsh3/ap_ms104_sh4/ap_ms104_sh4var.h>
44238763f6Snonaka 
45238763f6Snonaka #define	_N_EXTINTR	16
46238763f6Snonaka 
47238763f6Snonaka struct intrhand {
48238763f6Snonaka 	int	(*ih_fun)(void *);
49238763f6Snonaka 	void	*ih_arg;
50238763f6Snonaka 	struct	intrhand *ih_next;
51238763f6Snonaka 	int	ih_enable;
52238763f6Snonaka 	int	ih_level;
53238763f6Snonaka 	int	ih_irq;
54238763f6Snonaka 	struct evcnt ih_evcnt;
55238763f6Snonaka };
56238763f6Snonaka 
57238763f6Snonaka struct extintr_handler {
58238763f6Snonaka 	void		*eih_func;
59238763f6Snonaka 	struct intrhand	*eih_ih;
60238763f6Snonaka 	int		eih_nih;
61238763f6Snonaka };
62238763f6Snonaka static struct extintr_handler extintr_handler[_N_EXTINTR];
63238763f6Snonaka 
64238763f6Snonaka static const char *extintr_names[_N_EXTINTR] = {
65238763f6Snonaka 	"irq0", "irq1", "irq2", "irq3",
66238763f6Snonaka 	"irq4", "irq5", "irq6", "irq7",
67238763f6Snonaka 	"irq8", "irq9", "irq10", "irq11",
68238763f6Snonaka 	"irq12", "irq13", "irq14", "irq15"
69238763f6Snonaka };
70238763f6Snonaka 
71238763f6Snonaka static int fakeintr(void *arg);
72238763f6Snonaka static int extintr_intr_handler(void *arg);
73238763f6Snonaka 
74238763f6Snonaka void
extintr_init(void)75238763f6Snonaka extintr_init(void)
76238763f6Snonaka {
77238763f6Snonaka 
78238763f6Snonaka 	_reg_write_1(EXTINTR_MASK1, 0);
79238763f6Snonaka 	_reg_write_1(EXTINTR_MASK2, 0);
80238763f6Snonaka 	_reg_write_1(EXTINTR_MASK3, 0);
81238763f6Snonaka 	_reg_write_1(EXTINTR_MASK4, 0);
82238763f6Snonaka }
83238763f6Snonaka 
84238763f6Snonaka /*ARGSUSED*/
85238763f6Snonaka static int
fakeintr(void * arg)86238763f6Snonaka fakeintr(void *arg)
87238763f6Snonaka {
88238763f6Snonaka 
89238763f6Snonaka 	return 0;
90238763f6Snonaka }
91238763f6Snonaka 
92238763f6Snonaka void *
extintr_establish(int irq,int trigger,int level,int (* ih_fun)(void *),void * ih_arg)93238763f6Snonaka extintr_establish(int irq, int trigger, int level,
94238763f6Snonaka     int (*ih_fun)(void *), void *ih_arg)
95238763f6Snonaka {
96238763f6Snonaka 	static struct intrhand fakehand = {fakeintr};
97238763f6Snonaka 	struct extintr_handler *eih;
98238763f6Snonaka 	struct intrhand **p, *q, *ih;
99238763f6Snonaka 	const char *name;
100238763f6Snonaka 	int evtcode;
101238763f6Snonaka 	int s;
102238763f6Snonaka 
103238763f6Snonaka 	KDASSERT(irq >= 1 && irq <= 14);
104238763f6Snonaka 
105*d206c345Sthorpej 	ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
106238763f6Snonaka 
107238763f6Snonaka 	s = _cpu_intr_suspend();
108238763f6Snonaka 
109238763f6Snonaka 	switch (level) {
110238763f6Snonaka 	default:
111238763f6Snonaka #if defined(DEBUG)
112238763f6Snonaka 		panic("extintr_establish: unknown level %d", level);
113238763f6Snonaka 		/*NOTREACHED*/
114238763f6Snonaka #endif
115238763f6Snonaka 	case IPL_VM:
116238763f6Snonaka 		break;
117238763f6Snonaka 	}
118238763f6Snonaka 
119238763f6Snonaka 	eih = &extintr_handler[irq];
120238763f6Snonaka 	if (eih->eih_func == NULL) {
121238763f6Snonaka 		evtcode = 0x200 + (irq << 5);
122238763f6Snonaka 		eih->eih_func = intc_intr_establish(evtcode, trigger, level,
123238763f6Snonaka 		    extintr_intr_handler, eih);
124238763f6Snonaka 	}
125238763f6Snonaka 
126238763f6Snonaka 	/*
127238763f6Snonaka 	 * Figure out where to put the handler.
128238763f6Snonaka 	 * This is O(N^2), but we want to preserve the order, and N is
129238763f6Snonaka 	 * generally small.
130238763f6Snonaka 	 */
131238763f6Snonaka 	for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
132238763f6Snonaka 		continue;
133238763f6Snonaka 
134238763f6Snonaka 	/*
135238763f6Snonaka 	 * Actually install a fake handler momentarily, since we might be doing
136238763f6Snonaka 	 * this with interrupts enabled and don't want the real routine called
137238763f6Snonaka 	 * until masking is set up.
138238763f6Snonaka 	 */
139238763f6Snonaka 	fakehand.ih_level = level;
140238763f6Snonaka 	*p = &fakehand;
141238763f6Snonaka 
142238763f6Snonaka 	/*
143238763f6Snonaka 	 * Poke the real handler in now.
144238763f6Snonaka 	 */
145238763f6Snonaka 	memset(ih, 0, sizeof(*ih));
146238763f6Snonaka 	ih->ih_fun = ih_fun;
147238763f6Snonaka 	ih->ih_arg = ih_arg;
148238763f6Snonaka 	ih->ih_next = NULL;
149238763f6Snonaka 	ih->ih_enable = 1;
150238763f6Snonaka 	ih->ih_level = level;
151238763f6Snonaka 	ih->ih_irq = irq;
152238763f6Snonaka 	name = extintr_names[irq];
153238763f6Snonaka 	evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "ext", name);
154238763f6Snonaka 	*p = ih;
155238763f6Snonaka 
156238763f6Snonaka 	if (++eih->eih_nih == 1) {
157238763f6Snonaka 		uint8_t reg;
158238763f6Snonaka 
159238763f6Snonaka 		/* Unmask interrupt */
160238763f6Snonaka 		switch (irq) {
161238763f6Snonaka 		case 1: case 2:
162238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK4);
163238763f6Snonaka 			reg |= 1 << (2 - irq);
164238763f6Snonaka 			_reg_write_1(EXTINTR_MASK4, reg);
165238763f6Snonaka 			break;
166238763f6Snonaka 
167238763f6Snonaka 		case 3: case 4: case 5: case 6:
168238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK3);
169238763f6Snonaka 			reg |= 1 << (6 - irq);
170238763f6Snonaka 			_reg_write_1(EXTINTR_MASK3, reg);
171238763f6Snonaka 			break;
172238763f6Snonaka 
173238763f6Snonaka 		case 7: case 8: case 9: case 10:
174238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK2);
175238763f6Snonaka 			reg |= 1 << (10 - irq);
176238763f6Snonaka 			_reg_write_1(EXTINTR_MASK2, reg);
177238763f6Snonaka 			break;
178238763f6Snonaka 
179238763f6Snonaka 		case 11: case 12: case 13: case 14:
180238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK1);
181238763f6Snonaka 			reg |= 1 << (14 - irq);
182238763f6Snonaka 			_reg_write_1(EXTINTR_MASK1, reg);
183238763f6Snonaka 			break;
184238763f6Snonaka 
185238763f6Snonaka 		default:
186238763f6Snonaka 			panic("unknown irq%d\n", irq);
187238763f6Snonaka 			/*NOTREACHED*/
188238763f6Snonaka 			break;
189238763f6Snonaka 		}
190238763f6Snonaka 	}
191238763f6Snonaka 
192238763f6Snonaka 	splx(s);
193238763f6Snonaka 
194238763f6Snonaka 	return (ih);
195238763f6Snonaka }
196238763f6Snonaka 
197238763f6Snonaka void
extintr_disestablish(void * cookie)198238763f6Snonaka extintr_disestablish(void *cookie)
199238763f6Snonaka {
200238763f6Snonaka 	struct intrhand *ih = (struct intrhand *)cookie;
201238763f6Snonaka 	struct intrhand **p, *q;
202238763f6Snonaka 	struct extintr_handler *eih;
203238763f6Snonaka 	int irq;
204238763f6Snonaka 	int s;
205238763f6Snonaka 
206238763f6Snonaka 	KDASSERT(ih != NULL);
207238763f6Snonaka 
208238763f6Snonaka 	s = _cpu_intr_suspend();
209238763f6Snonaka 
210238763f6Snonaka 	irq = ih->ih_irq;
211238763f6Snonaka 	eih = &extintr_handler[irq];
212238763f6Snonaka 
213238763f6Snonaka 	/*
214238763f6Snonaka 	 * Remove the handler from the chain.
215238763f6Snonaka 	 * This is O(n^2), too.
216238763f6Snonaka 	 */
217238763f6Snonaka 	for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
218238763f6Snonaka 		continue;
219238763f6Snonaka 	if (q == NULL)
220238763f6Snonaka 		panic("extintr_disestablish: handler not registered");
221238763f6Snonaka 
222238763f6Snonaka 	*p = q->ih_next;
223238763f6Snonaka 
224238763f6Snonaka 	evcnt_detach(&ih->ih_evcnt);
225238763f6Snonaka 
226*d206c345Sthorpej 	kmem_free((void *)ih, sizeof(*ih));
227238763f6Snonaka 
228238763f6Snonaka 	if (--eih->eih_nih == 0) {
229238763f6Snonaka 		uint8_t reg;
230238763f6Snonaka 
231238763f6Snonaka 		intc_intr_disestablish(eih->eih_func);
232238763f6Snonaka 		eih->eih_func = NULL;
233238763f6Snonaka 
234238763f6Snonaka 		/* Mask interrupt */
235238763f6Snonaka 		switch (irq) {
236238763f6Snonaka 		case 1: case 2:
237238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK4);
238238763f6Snonaka 			reg &= ~(1 << (2 - irq));
239238763f6Snonaka 			_reg_write_1(EXTINTR_MASK4, reg);
240238763f6Snonaka 			break;
241238763f6Snonaka 
242238763f6Snonaka 		case 3: case 4: case 5: case 6:
243238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK3);
244238763f6Snonaka 			reg &= ~(1 << (6 - irq));
245238763f6Snonaka 			_reg_write_1(EXTINTR_MASK3, reg);
246238763f6Snonaka 			break;
247238763f6Snonaka 
248238763f6Snonaka 		case 7: case 8: case 9: case 10:
249238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK2);
250238763f6Snonaka 			reg &= ~(1 << (10 - irq));
251238763f6Snonaka 			_reg_write_1(EXTINTR_MASK2, reg);
252238763f6Snonaka 			break;
253238763f6Snonaka 
254238763f6Snonaka 		case 11: case 12: case 13: case 14:
255238763f6Snonaka 			reg = _reg_read_1(EXTINTR_MASK1);
256238763f6Snonaka 			reg &= ~(1 << (14 - irq));
257238763f6Snonaka 			_reg_write_1(EXTINTR_MASK1, reg);
258238763f6Snonaka 			break;
259238763f6Snonaka 
260238763f6Snonaka 		default:
261238763f6Snonaka 			panic("unknown irq%d\n", irq);
262238763f6Snonaka 			/*NOTREACHED*/
263238763f6Snonaka 			break;
264238763f6Snonaka 		}
265238763f6Snonaka 	}
266238763f6Snonaka 
267238763f6Snonaka 	splx(s);
268238763f6Snonaka }
269238763f6Snonaka 
270238763f6Snonaka static int
extintr_intr_handler(void * arg)271238763f6Snonaka extintr_intr_handler(void *arg)
272238763f6Snonaka {
273238763f6Snonaka 	struct extintr_handler *eih = arg;
274238763f6Snonaka 	struct intrhand *ih;
275238763f6Snonaka 	int r;
276238763f6Snonaka 
277238763f6Snonaka 	if (__predict_true(eih != NULL)) {
278238763f6Snonaka 		for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
279238763f6Snonaka 			if (__predict_true(ih->ih_enable)) {
280238763f6Snonaka 				r = (*ih->ih_fun)(ih->ih_arg);
281238763f6Snonaka 				if (__predict_true(r != 0)) {
282238763f6Snonaka 					ih->ih_evcnt.ev_count++;
283238763f6Snonaka 				}
284238763f6Snonaka 			}
285238763f6Snonaka 		}
286238763f6Snonaka 		return 1;
287238763f6Snonaka 	}
288238763f6Snonaka 	return 0;
289238763f6Snonaka }
290