1 /*      $NetBSD: loongson_intr.c,v 1.8 2020/11/21 15:36:36 thorpej Exp $      */
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Jason R. Thorpe.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: loongson_intr.c,v 1.8 2020/11/21 15:36:36 thorpej Exp $");
34 
35 #define __INTR_PRIVATE
36 
37 #include <sys/param.h>
38 #include <sys/device.h>
39 #include <sys/cpu.h>
40 #include <sys/intr.h>
41 #include <sys/bus.h>
42 #include <sys/kmem.h>
43 
44 #include <mips/mips3_clock.h>
45 #include <machine/locore.h>
46 
47 #include <evbmips/loongson/autoconf.h>
48 #include <evbmips/loongson/loongson_intr.h>
49 
50 #include <mips/locore.h>
51 
52 #include <mips/bonito/bonitoreg.h>
53 #include <mips/bonito/bonitovar.h>
54 
55 #include <dev/pci/pciidereg.h>
56 #include <dev/isa/isavar.h>
57 
58 #include "isa.h"
59 
60 #ifdef INTR_DEBUG
61 #define DPRINTF(x) printf x
62 #else
63 #define DPRINTF(x)
64 #endif
65 
66 struct bonito_intrhead bonito_intrhead[BONITO_NINTS];
67 
68 /*
69  * This is a mask of bits to clear in the SR when we go to a
70  * given hardware interrupt priority level.
71  */
72 static const struct ipl_sr_map loongson_ipl_sr_map = {
73     .sr_bits = {
74           [IPL_NONE] =                  0,
75           [IPL_SOFTCLOCK] =   MIPS_SOFT_INT_MASK_0,
76           [IPL_SOFTNET] =               MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
77           [IPL_VM] =
78               MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
79               MIPS_INT_MASK_0 |
80               MIPS_INT_MASK_1 |
81               MIPS_INT_MASK_2 |
82               MIPS_INT_MASK_3 |
83               MIPS_INT_MASK_4,
84           [IPL_SCHED] =
85               MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
86               MIPS_INT_MASK_0 |
87               MIPS_INT_MASK_1 |
88               MIPS_INT_MASK_2 |
89               MIPS_INT_MASK_3 |
90               MIPS_INT_MASK_4 |
91               MIPS_INT_MASK_5,
92           [IPL_DDB] =                   MIPS_INT_MASK,
93           [IPL_HIGH] =            MIPS_INT_MASK,
94     },
95 };
96 
97 
98 void
evbmips_intr_init(void)99 evbmips_intr_init(void)
100 {
101           const struct bonito_config * const bc = sys_platform->bonito_config;
102           const struct bonito_irqmap *irqmap;
103           int i;
104 
105           ipl_sr_map = loongson_ipl_sr_map;
106 
107           for (i = 0; i < BONITO_NDIRECT; i++) {
108                     irqmap = &sys_platform->irq_map[i];
109                     if (irqmap->name == NULL)
110                               continue;
111                     DPRINTF(("attach %d %s\n", i, irqmap->name));
112                     evcnt_attach_dynamic(&bonito_intrhead[i].intr_count,
113                         EVCNT_TYPE_INTR, NULL, "bonito", irqmap->name);
114                     LIST_INIT(&bonito_intrhead[i].intrhand_head);
115           }
116 
117           REGVAL(BONITO_GPIOIE) = bc->bc_gpioIE;
118           REGVAL(BONITO_INTEDGE) = bc->bc_intEdge;
119           /* REGVAL(BONITO_INTSTEER) = bc->bc_intSteer; XXX */
120           REGVAL(BONITO_INTPOL) = bc->bc_intPol;
121           REGVAL(BONITO_INTENCLR) = 0xffffffff;
122           (void)REGVAL(BONITO_INTENCLR);
123 
124           if (sys_platform->isa_chipset != NULL) {
125                     int irq;
126                     static char irqstr[8];
127                     for (irq = 0; irq < BONITO_NISA; irq++) {
128                               i = BONITO_ISA_IRQ(irq);
129                               snprintf(irqstr, sizeof(irqstr), "irq %d", irq);
130                               DPRINTF(("attach %d %d %s\n", i, irq, irqstr));
131                               evcnt_attach_dynamic(&bonito_intrhead[i].intr_count,
132                                   EVCNT_TYPE_INTR, NULL, "isa", irqstr);
133                               LIST_INIT(&bonito_intrhead[i].intrhand_head);
134                     }
135           }
136 }
137 
138 void
evbmips_iointr(int ppl,uint32_t ipending,struct clockframe * cf)139 evbmips_iointr(int ppl, uint32_t ipending, struct clockframe *cf)
140 {
141           struct evbmips_intrhand *ih;
142           int irq;
143           uint32_t isr0, isr, imr;
144 
145           /*
146            * Read the interrupt pending registers, mask them with the
147            * ones we have enabled, and service them in order of decreasing
148            * priority.
149            */
150           isr0 = REGVAL(BONITO_INTISR);
151           imr = REGVAL(BONITO_INTEN);
152 
153           if (ipending & sys_platform->bonito_mips_intr) {
154                     isr = isr0 & imr & LOONGSON_INTRMASK_LVL4;
155 
156                     REGVAL(BONITO_INTENCLR) = isr;
157                     (void)REGVAL(BONITO_INTENCLR);
158 
159                     for (irq = 0; irq < BONITO_NINTS; irq++) {
160                               if ((isr & (1 << irq)) == 0)
161                                         continue;
162                               bonito_intrhead[irq].intr_count.ev_count++;
163                               LIST_FOREACH (ih,
164                                   &bonito_intrhead[irq].intrhand_head, ih_q) {
165                                         if (ih->ih_arg)
166                                                   (*ih->ih_func)(ih->ih_arg);
167                                         else
168                                                   (*ih->ih_func)(cf);
169                               }
170                     }
171                     REGVAL(BONITO_INTENSET) = isr;
172                     (void)REGVAL(BONITO_INTENSET);
173           }
174           if (isr0 & LOONGSON_INTRMASK_INT0)
175                     sys_platform->isa_intr(ppl, cf->pc, ipending);
176 }
177 
178 void *
loongson_pciide_compat_intr_establish(void * v,device_t dev,const struct pci_attach_args * pa,int chan,int (* func)(void *),void * arg)179 loongson_pciide_compat_intr_establish(void *v, device_t dev,
180     const struct pci_attach_args *pa, int chan, int (*func)(void *), void *arg)
181 {
182           pci_chipset_tag_t pc = pa->pa_pc;
183           void *cookie;
184           int bus, irq;
185           char buf[PCI_INTRSTR_LEN];
186 
187           pci_decompose_tag(pc, pa->pa_tag, &bus, NULL, NULL);
188 
189           /*
190            * If this isn't PCI bus #0, all bets are off.
191            */
192           if (bus != 0)
193                     return (NULL);
194 
195           irq = PCIIDE_COMPAT_IRQ(chan);
196 #if NISA > 0
197           if (sys_platform->isa_chipset != NULL)
198                     cookie = isa_intr_establish(sys_platform->isa_chipset, irq,
199                         IST_EDGE, IPL_BIO, func, arg);
200           else
201 #endif
202                     cookie = NULL;
203           if (cookie == NULL)
204                     return (NULL);
205           printf("%s: %s channel interrupting at %s\n", device_xname(dev),
206               PCIIDE_CHANNEL_NAME(chan),
207               isa_intr_string(sys_platform->isa_chipset, irq, buf, sizeof(buf)));
208           return (cookie);
209 }
210 
211 int
loongson_pci_intr_map(const struct pci_attach_args * pa,pci_intr_handle_t * ihp)212 loongson_pci_intr_map(const struct pci_attach_args *pa,
213     pci_intr_handle_t *ihp)
214 {
215           pcitag_t bustag = pa->pa_intrtag;
216           int buspin = pa->pa_intrpin;
217           pci_chipset_tag_t pc = pa->pa_pc;
218           int device, function;
219 
220           if (buspin == 0) {
221                     /* No IRQ used. */
222                     return (1);
223           }
224 
225           if (buspin > 4) {
226                     printf("loongson_pci_intr_map: bad interrupt pin %d\n",
227                         buspin);
228                     return (1);
229           }
230 
231           pci_decompose_tag(pc, bustag, NULL, &device, &function);
232           return (sys_platform->p_pci_intr_map(device, function, buspin, ihp));
233 }
234 
235 const char *
loongson_pci_intr_string(void * v,pci_intr_handle_t ih,char * buf,size_t len)236 loongson_pci_intr_string(void *v, pci_intr_handle_t ih, char *buf, size_t len)
237 {
238 
239           const struct bonito_config *bc = v;
240           return loongson_intr_string(bc, ih, buf, len);
241 }
242 
243 const struct evcnt *
loongson_pci_intr_evcnt(void * v,pci_intr_handle_t ih)244 loongson_pci_intr_evcnt(void *v, pci_intr_handle_t ih)
245 {
246 
247           return &bonito_intrhead[ih].intr_count;
248 }
249 
250 void *
loongson_pci_intr_establish(void * v,pci_intr_handle_t ih,int level,int (* func)(void *),void * arg)251 loongson_pci_intr_establish(void *v, pci_intr_handle_t ih, int level,
252     int (*func)(void *), void *arg)
253 {
254           if (BONITO_IRQ_IS_ISA(ih)) {
255                     if (sys_platform->isa_chipset == NULL)
256                               panic("ISA interrupt on non-ISA platform");
257                     return sys_platform->isa_chipset->ic_intr_establish(v,
258                         BONITO_IRQ_TO_ISA(ih), IST_EDGE, level, func, arg);
259           }
260           return evbmips_intr_establish(ih, func, arg);
261 }
262 
263 void
loongson_pci_intr_disestablish(void * v,void * cookie)264 loongson_pci_intr_disestablish(void *v, void *cookie)
265 {
266           struct evbmips_intrhand *ih = cookie;
267           if (BONITO_IRQ_IS_ISA(ih->ih_irq)) {
268                     if (sys_platform->isa_chipset == NULL)
269                               panic("ISA interrupt on non-ISA platform");
270                     sys_platform->isa_chipset->ic_intr_disestablish(v, ih);
271                     return;
272           }
273           return (evbmips_intr_disestablish(cookie));
274 }
275 
276 void
loongson_pci_conf_interrupt(void * v,int bus,int dev,int pin,int swiz,int * iline)277 loongson_pci_conf_interrupt(void *v, int bus, int dev, int pin, int swiz,
278     int *iline)
279 {
280 
281           /*
282            * We actually don't need to do anything; everything is handled
283            * in pci_intr_map().
284            */
285           *iline = 0;
286 }
287 
288 
289 void *
evbmips_intr_establish(int irq,int (* func)(void *),void * arg)290 evbmips_intr_establish(int irq, int (*func)(void *), void *arg)
291 {
292           struct evbmips_intrhand *ih;
293           int s;
294 
295           KASSERT(irq < BONITO_NINTS);
296           DPRINTF(("loongson_intr_establish %d %p", irq, func));
297 
298           ih = kmem_zalloc(sizeof(*ih), KM_SLEEP);
299           ih->ih_func = func;
300           ih->ih_arg = arg;
301           ih->ih_irq = irq;
302           DPRINTF((" ih %p", ih));
303 
304           /* link it into tables */
305           s = splhigh();
306           LIST_INSERT_HEAD(&bonito_intrhead[irq].intrhand_head, ih, ih_q);
307           /* and enable it */
308           DPRINTF((" inten 0x%x", REGVAL(BONITO_INTEN)));
309           if (bonito_intrhead[irq].refcnt++ == 0 && !BONITO_IRQ_IS_ISA(irq))
310                     REGVAL(BONITO_INTENSET) = (1 << ih->ih_irq);
311           DPRINTF((" now 0x%x\n", REGVAL(BONITO_INTEN)));
312           splx(s);
313 
314           return (ih);
315 }
316 
317 void
evbmips_intr_disestablish(void * cookie)318 evbmips_intr_disestablish(void *cookie)
319 {
320           struct evbmips_intrhand *ih = cookie;
321           int s;
322 
323           s = splhigh();
324           LIST_REMOVE(ih, ih_q);
325           bonito_intrhead[ih->ih_irq].refcnt--;
326           if (bonito_intrhead[ih->ih_irq].refcnt == 0 &&
327               !BONITO_IRQ_IS_ISA(ih->ih_irq))
328                     REGVAL(BONITO_INTENCLR) = (1 << ih->ih_irq);
329           splx(s);
330           kmem_free(ih, sizeof(*ih));
331 }
332 
333 const char *
loongson_intr_string(const struct bonito_config * bc,int irq,char * buf,size_t len)334 loongson_intr_string(const struct bonito_config *bc, int irq, char *buf, size_t len)
335 {
336           if (BONITO_IRQ_IS_ISA(irq))
337                     snprintf(buf, len, "isa irq %d", BONITO_IRQ_TO_ISA(irq));
338           else
339                     strlcpy(buf, sys_platform->irq_map[irq].name, len);
340           return buf;
341 }
342