1 /* $NetBSD: intr.c,v 1.4 2024/11/01 14:28:08 mlelstv Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 2023 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass, Gordon W. Ross, and 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 /* 33 * Link and dispatch interrupts. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.4 2024/11/01 14:28:08 mlelstv Exp $"); 38 39 #define _VIRT68K_INTR_PRIVATE 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kmem.h> 44 #include <sys/vmmeter.h> 45 #include <sys/device.h> 46 #include <sys/cpu.h> 47 #include <sys/bus.h> 48 #include <sys/intr.h> 49 50 #include <machine/vectors.h> 51 52 #include <uvm/uvm_extern.h> 53 54 #include <dev/goldfish/gfpicvar.h> 55 56 struct intrhand { 57 LIST_ENTRY(intrhand) ih_link; 58 int (*ih_func)(void *); 59 void *ih_arg; 60 struct evcnt *ih_evcnt; 61 int ih_irq; 62 }; 63 64 static const char * const cpu_irq_group = "cpu irq"; 65 66 static const char * const pic_irq_names[] = { 67 "irq 1", "irq 2", "irq 3", "irq 4", 68 "irq 5", "irq 6", "irq 7", "irq 8", 69 "irq 9", "irq 10", "irq 11", "irq 12", 70 "irq 13", "irq 14", "irq 15", "irq 16", 71 "irq 17", "irq 18", "irq 19", "irq 20", 72 "irq 21", "irq 22", "irq 23", "irq 24", 73 "irq 25", "irq 26", "irq 27", "irq 28", 74 "irq 29", "irq 30", "irq 31", "irq 32", 75 }; 76 77 #define PIC_EVCNTS(g) \ 78 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[0]), \ 79 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[1]), \ 80 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[2]), \ 81 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[3]), \ 82 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[4]), \ 83 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[5]), \ 84 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[6]), \ 85 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[7]), \ 86 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[8]), \ 87 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[9]), \ 88 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[10]),\ 89 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[11]),\ 90 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[12]),\ 91 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[13]),\ 92 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[14]),\ 93 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[15]),\ 94 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[16]),\ 95 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[17]),\ 96 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[18]),\ 97 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[19]),\ 98 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[20]),\ 99 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[21]),\ 100 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[22]),\ 101 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[23]),\ 102 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[24]),\ 103 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[25]),\ 104 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[26]),\ 105 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[27]),\ 106 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[28]),\ 107 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[29]),\ 108 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[30]),\ 109 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, NULL, pic_irq_names[31]) 110 111 static struct evcnt intr_evcnt[] = { 112 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "spur"), 113 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev1"), 114 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev2"), 115 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev3"), 116 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev4"), 117 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev5"), 118 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "lev6"), 119 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, cpu_irq_group, "nmi"), 120 PIC_EVCNTS(pic_irq_groups[0]), 121 PIC_EVCNTS(pic_irq_groups[1]), 122 PIC_EVCNTS(pic_irq_groups[2]), 123 PIC_EVCNTS(pic_irq_groups[3]), 124 PIC_EVCNTS(pic_irq_groups[4]), 125 PIC_EVCNTS(pic_irq_groups[5]), 126 }; 127 __CTASSERT(__arraycount(intr_evcnt) == NIRQ); 128 129 #define IHLIST_COUNT (NIRQ - IRQ_PIC_BASE) 130 #define IRQ_TO_IHLIST(x) ((x) - IRQ_PIC_BASE) 131 #define IRQ_TO_IPL(x) ((IRQ_TO_IHLIST(x) >> 5) + 1) 132 static LIST_HEAD(intrhand_list, intrhand) intrhands[IHLIST_COUNT]; 133 134 volatile unsigned int intr_depth; /* used in locore.s */ 135 136 static struct gfpic_softc *pics[NPIC]; 137 138 static inline int 139 ipl_to_pic(const int ipl) 140 { 141 if (__predict_true(ipl >= 1 && ipl <= 6)) { 142 return (ipl - 1); 143 } 144 return -1; 145 } 146 147 /* 148 * intr_init -- 149 * Initialize the interrupt subsystem. 150 */ 151 void 152 intr_init(void) 153 { 154 int i; 155 156 for (i = 0; i < IHLIST_COUNT; i++) { 157 LIST_INIT(&intrhands[i]); 158 } 159 160 /* 161 * Attach the CPU IRQ event counters first. The 162 * PIC event counters will be attached as the PICs 163 * are registered with us. 164 */ 165 for (i = 0; i < IRQ_PIC_BASE; i++) { 166 evcnt_attach_static(&intr_evcnt[i]); 167 } 168 } 169 170 /* 171 * intr_register_pic -- 172 * Register a Goldfish PIC at the specified CPU IRQ. 173 */ 174 void 175 intr_register_pic(device_t dev, int ipl) 176 { 177 const int idx = ipl_to_pic(ipl); 178 179 KASSERT(idx >= 0); 180 181 const int base = IRQ_PIC_BASE + (idx * NIRQ_PER_PIC); 182 int pirq; 183 184 KASSERT(pics[idx] == NULL); 185 pics[idx] = device_private(dev); 186 187 for (pirq = 0; pirq < NIRQ_PER_PIC; pirq++) { 188 intr_evcnt[base + pirq].ev_group = device_xname(dev); 189 evcnt_attach_static(&intr_evcnt[base + pirq]); 190 } 191 } 192 193 /* 194 * intr_establish -- 195 * Establish an interrupt handler at the specified system IRQ. 196 * XXX We don't do anything with the flags yet. 197 */ 198 void * 199 intr_establish(int (*func)(void *), void *arg, int irq, int ipl, 200 int flags __unused) 201 { 202 struct intrhand *ih; 203 int s; 204 205 if (irq < IRQ_PIC_BASE || irq >= NIRQ) { 206 return NULL; 207 } 208 if (ipl < IPL_BIO || ipl > IPL_SCHED) { 209 return NULL; 210 } 211 212 const int pic = IRQ_TO_PIC(irq); 213 if (pics[pic] == NULL) { 214 return NULL; 215 } 216 217 ih = kmem_zalloc(sizeof(*ih), KM_SLEEP); 218 ih->ih_func = func; 219 ih->ih_arg = arg; 220 ih->ih_irq = irq; 221 ih->ih_evcnt = &intr_evcnt[irq]; 222 223 s = splhigh(); 224 LIST_INSERT_HEAD(&intrhands[IRQ_TO_IHLIST(irq)], ih, ih_link); 225 gfpic_enable(pics[pic], IRQ_TO_PIRQ(irq)); 226 splx(s); 227 228 return ih; 229 } 230 231 /* 232 * intr_disestablish -- 233 * Remove an interrupt handler. 234 */ 235 void 236 intr_disestablish(void *v) 237 { 238 struct intrhand *ih = v; 239 const int pic = IRQ_TO_PIC(ih->ih_irq); 240 int s; 241 242 KASSERT(pics[pic] != NULL); 243 244 s = splhigh(); 245 LIST_REMOVE(ih, ih_link); 246 if (LIST_EMPTY(&intrhands[IRQ_TO_IHLIST(ih->ih_irq)])) { 247 gfpic_disable(pics[pic], IRQ_TO_PIRQ(ih->ih_irq)); 248 } 249 splx(s); 250 } 251 252 /* 253 * intr_string -- 254 * Return a string in the specified buffer describing the 255 * interrupt. 256 */ 257 const char * 258 intr_string(void *v, char *buf, size_t bufsize) 259 { 260 struct intrhand *ih = v; 261 const int ipl = IRQ_TO_IPL(ih->ih_irq); 262 263 snprintf(buf, bufsize, "%s %s (IPL %d)", ih->ih_evcnt->ev_group, 264 ih->ih_evcnt->ev_name, ipl); 265 return buf; 266 } 267 268 /* Auto-vectored interrupts start at vector 0x18. */ 269 #define VEC_AVINTR 0x18 270 271 void 272 intr_dispatch(struct clockframe frame) 273 { 274 const int ipl = VECO_TO_VECI(frame.cf_vo) - VEC_AVINTR; 275 const int pic = ipl_to_pic(ipl); 276 int pirq; 277 278 if (__predict_false(pic < 0)) { 279 return; 280 } 281 282 if (pics[pic] == NULL) { 283 printf("Interrupt without a cause on CPU ipl %d\n", ipl); 284 return; 285 } 286 287 const int base = pic * NIRQ_PER_PIC; 288 struct intrhand *ih; 289 bool rv = false; 290 291 while ((pirq = gfpic_pending(pics[pic])) >= 0) { 292 LIST_FOREACH(ih, &intrhands[base + pirq], ih_link) { 293 void *arg = ih->ih_arg ? ih->ih_arg : &frame; 294 if (ih->ih_func(arg)) { 295 ih->ih_evcnt->ev_count++; 296 rv = true; 297 } 298 } 299 } 300 if (!rv) { 301 printf("Spurious interrupt on CPU ipl %d\n", ipl); 302 } 303 } 304 305 bool 306 cpu_intr_p(void) 307 { 308 return intr_depth != 0; 309 } 310