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