1 /* $OpenBSD: aplintc.c,v 1.1 2021/02/23 17:01:17 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/device.h> 20 #include <sys/evcount.h> 21 #include <sys/malloc.h> 22 #include <sys/systm.h> 23 24 #include <machine/bus.h> 25 #include <machine/fdt.h> 26 #include <machine/intr.h> 27 28 #include <dev/ofw/openfirm.h> 29 #include <dev/ofw/fdt.h> 30 31 #include <ddb/db_output.h> 32 33 #define CNTV_CTL_IMASK (1 << 1) 34 35 #define AIC_INFO 0x0004 36 #define AIC_INFO_NIRQ(val) ((val) & 0xffff) 37 #define AIC_WHOAMI 0x2000 38 #define AIC_EVENT 0x2004 39 #define AIC_EVENT_TYPE(val) ((val) >> 16) 40 #define AIC_EVENT_TYPE_IRQ 1 41 #define AIC_EVENT_IRQ(val) ((val) & 0xffff) 42 #define AIC_TARGET_CPU(irq) (0x3000 + ((irq) << 2)) 43 #define AIC_SW_SET(irq) (0x4000 + (((irq) >> 5) << 2)) 44 #define AIC_SW_CLR(irq) (0x4080 + (((irq) >> 5) << 2)) 45 #define AIC_SW_BIT(irq) (1U << ((irq) & 0x1f)) 46 #define AIC_MASK_SET(irq) (0x4100 + (((irq) >> 5) << 2)) 47 #define AIC_MASK_CLR(irq) (0x4180 + (((irq) >> 5) << 2)) 48 #define AIC_MASK_BIT(irq) (1U << ((irq) & 0x1f)) 49 50 #define HREAD4(sc, reg) \ 51 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 52 #define HWRITE4(sc, reg, val) \ 53 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 54 55 struct intrhand { 56 TAILQ_ENTRY(intrhand) ih_list; 57 int (*ih_func)(void *); 58 void *ih_arg; 59 int ih_ipl; 60 int ih_flags; 61 int ih_irq; 62 struct evcount ih_count; 63 const char *ih_name; 64 struct cpu_info *ih_ci; 65 }; 66 67 struct aplintc_softc { 68 struct device sc_dev; 69 bus_space_tag_t sc_iot; 70 bus_space_handle_t sc_ioh; 71 72 struct interrupt_controller sc_ic; 73 74 struct intrhand *sc_fiq_handler; 75 int sc_fiq_pending; 76 struct intrhand **sc_irq_handler; 77 int sc_nirq; 78 TAILQ_HEAD(, intrhand) sc_irq_list[NIPL]; 79 }; 80 81 struct aplintc_softc *aplintc_sc; 82 83 int aplintc_match(struct device *, void *, void *); 84 void aplintc_attach(struct device *, struct device *, void *); 85 86 struct cfattach aplintc_ca = { 87 sizeof (struct aplintc_softc), aplintc_match, aplintc_attach 88 }; 89 90 struct cfdriver aplintc_cd = { 91 NULL, "aplintc", DV_DULL 92 }; 93 94 void aplintc_irq_handler(void *); 95 void aplintc_fiq_handler(void *); 96 void aplintc_intr_barrier(void *); 97 int aplintc_splraise(int); 98 int aplintc_spllower(int); 99 void aplintc_splx(int); 100 void aplintc_setipl(int); 101 102 void *aplintc_intr_establish(void *, int *, int, struct cpu_info *, 103 int (*)(void *), void *, char *); 104 void aplintc_intr_disestablish(void *); 105 106 int 107 aplintc_match(struct device *parent, void *match, void *aux) 108 { 109 struct fdt_attach_args *faa = aux; 110 111 return OF_is_compatible(faa->fa_node, "apple,aic"); 112 } 113 114 void 115 aplintc_attach(struct device *parent, struct device *self, void *aux) 116 { 117 struct aplintc_softc *sc = (struct aplintc_softc *)self; 118 struct fdt_attach_args *faa = aux; 119 uint32_t info; 120 int ipl; 121 122 if (faa->fa_nreg < 1) { 123 printf(": no registers\n"); 124 return; 125 } 126 127 sc->sc_iot = faa->fa_iot; 128 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 129 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 130 printf(": can't map registers\n"); 131 return; 132 } 133 134 info = HREAD4(sc, AIC_INFO); 135 sc->sc_nirq = AIC_INFO_NIRQ(info); 136 sc->sc_irq_handler = mallocarray(sc->sc_nirq, 137 sizeof(*sc->sc_irq_handler), M_DEVBUF, M_WAITOK | M_ZERO); 138 for (ipl = 0; ipl < NIPL; ipl++) 139 TAILQ_INIT(&sc->sc_irq_list[ipl]); 140 141 printf(" nirq %d\n", sc->sc_nirq); 142 143 arm_init_smask(); 144 145 aplintc_sc = sc; 146 arm_set_intr_handler(aplintc_splraise, aplintc_spllower, aplintc_splx, 147 aplintc_setipl, aplintc_irq_handler, aplintc_fiq_handler); 148 149 sc->sc_ic.ic_node = faa->fa_node; 150 sc->sc_ic.ic_cookie = self; 151 sc->sc_ic.ic_establish = aplintc_intr_establish; 152 sc->sc_ic.ic_disestablish = aplintc_intr_disestablish; 153 sc->sc_ic.ic_barrier = aplintc_intr_barrier; 154 arm_intr_register_fdt(&sc->sc_ic); 155 } 156 157 void 158 aplintc_irq_handler(void *frame) 159 { 160 struct aplintc_softc *sc = aplintc_sc; 161 struct cpu_info *ci = curcpu(); 162 struct intrhand *ih; 163 uint32_t event; 164 uint32_t irq, type; 165 int handled; 166 int s; 167 168 event = HREAD4(sc, AIC_EVENT); 169 irq = AIC_EVENT_IRQ(event); 170 type = AIC_EVENT_TYPE(event); 171 172 if (type != AIC_EVENT_TYPE_IRQ) 173 panic("%s: unexpected event type %d\n", __func__, type); 174 175 if (irq >= sc->sc_nirq) 176 panic("%s: unexpected irq %d\n", __func__, irq); 177 178 if (sc->sc_irq_handler[irq] == NULL) 179 return; 180 181 HWRITE4(sc, AIC_SW_CLR(irq), AIC_SW_BIT(irq)); 182 183 ih = sc->sc_irq_handler[irq]; 184 185 if (ci->ci_cpl >= ih->ih_ipl) { 186 /* Queue interrupt as pending. */ 187 TAILQ_INSERT_TAIL(&sc->sc_irq_list[ih->ih_ipl], ih, ih_list); 188 } else { 189 s = aplintc_splraise(ih->ih_ipl); 190 intr_enable(); 191 handled = ih->ih_func(ih->ih_arg); 192 intr_disable(); 193 if (handled) 194 ih->ih_count.ec_count++; 195 aplintc_splx(s); 196 197 HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq)); 198 } 199 } 200 201 void 202 aplintc_fiq_handler(void *frame) 203 { 204 struct aplintc_softc *sc = aplintc_sc; 205 struct cpu_info *ci = curcpu(); 206 uint64_t reg; 207 int s; 208 209 if (ci->ci_cpl >= IPL_CLOCK) { 210 /* Mask timer interrupt and mark as pending. */ 211 reg = READ_SPECIALREG(cntv_ctl_el0); 212 WRITE_SPECIALREG(cntv_ctl_el0, reg | CNTV_CTL_IMASK); 213 sc->sc_fiq_pending = 1; 214 return; 215 } 216 217 s = aplintc_splraise(IPL_CLOCK); 218 sc->sc_fiq_handler->ih_func(frame); 219 sc->sc_fiq_handler->ih_count.ec_count++; 220 aplintc_splx(s); 221 } 222 223 void 224 aplintc_intr_barrier(void *cookie) 225 { 226 struct intrhand *ih = cookie; 227 228 sched_barrier(ih->ih_ci); 229 } 230 231 int 232 aplintc_splraise(int new) 233 { 234 struct cpu_info *ci = curcpu(); 235 int old = ci->ci_cpl; 236 237 if (old > new) 238 new = old; 239 240 aplintc_setipl(new); 241 return old; 242 } 243 244 int 245 aplintc_spllower(int new) 246 { 247 struct cpu_info *ci = curcpu(); 248 int old = ci->ci_cpl; 249 250 aplintc_splx(new); 251 return old; 252 } 253 254 void 255 aplintc_splx(int new) 256 { 257 struct aplintc_softc *sc = aplintc_sc; 258 struct cpu_info *ci = curcpu(); 259 struct intrhand *ih; 260 uint64_t reg; 261 u_long daif; 262 int ipl; 263 264 daif = intr_disable(); 265 266 /* Process pending FIQs. */ 267 if (sc->sc_fiq_pending && new < IPL_CLOCK) { 268 sc->sc_fiq_pending = 0; 269 reg = READ_SPECIALREG(cntv_ctl_el0); 270 WRITE_SPECIALREG(cntv_ctl_el0, reg & ~CNTV_CTL_IMASK); 271 } 272 273 /* Process pending IRQs. */ 274 for (ipl = ci->ci_cpl; ipl > new; ipl--) { 275 while (!TAILQ_EMPTY(&sc->sc_irq_list[ipl])) { 276 ih = TAILQ_FIRST(&sc->sc_irq_list[ipl]); 277 TAILQ_REMOVE(&sc->sc_irq_list[ipl], ih, ih_list); 278 279 HWRITE4(sc, AIC_SW_SET(ih->ih_irq), 280 AIC_SW_BIT(ih->ih_irq)); 281 HWRITE4(sc, AIC_MASK_CLR(ih->ih_irq), 282 AIC_MASK_BIT(ih->ih_irq)); 283 } 284 } 285 286 aplintc_setipl(new); 287 intr_restore(daif); 288 289 if (ci->ci_ipending & arm_smask[new]) 290 arm_do_pending_intr(new); 291 } 292 293 void 294 aplintc_setipl(int ipl) 295 { 296 struct cpu_info *ci = curcpu(); 297 298 ci->ci_cpl = ipl; 299 } 300 301 void * 302 aplintc_intr_establish(void *cookie, int *cell, int level, 303 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 304 { 305 struct aplintc_softc *sc = cookie; 306 struct intrhand *ih; 307 uint32_t type = cell[0]; 308 uint32_t irq = cell[1]; 309 310 if (type == 0) { 311 KASSERT(level != (IPL_CLOCK | IPL_MPSAFE)); 312 if (irq >= sc->sc_nirq) { 313 panic("%s: bogus irq number %d\n", 314 sc->sc_dev.dv_xname, irq); 315 } 316 } else if (type == 1) { 317 KASSERT(level == (IPL_CLOCK | IPL_MPSAFE)); 318 if (irq >= 4) 319 panic("%s: bogus fiq number %d\n", 320 sc->sc_dev.dv_xname, irq); 321 } else { 322 panic("%s: bogus irq type %d", 323 sc->sc_dev.dv_xname, cell[0]); 324 } 325 326 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 327 ih->ih_func = func; 328 ih->ih_arg = arg; 329 ih->ih_ipl = level & IPL_IRQMASK; 330 ih->ih_flags = level & IPL_FLAGMASK; 331 ih->ih_irq = irq; 332 ih->ih_name = name; 333 ih->ih_ci = ci; 334 335 if (name != NULL) 336 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 337 338 if (type == 0) { 339 sc->sc_irq_handler[irq] = ih; 340 HWRITE4(sc, AIC_TARGET_CPU(irq), 1); 341 HWRITE4(sc, AIC_MASK_CLR(irq), AIC_MASK_BIT(irq)); 342 } else 343 sc->sc_fiq_handler = ih; 344 345 return ih; 346 } 347 348 void 349 aplintc_intr_disestablish(void *cookie) 350 { 351 struct aplintc_softc *sc = aplintc_sc; 352 struct intrhand *ih = cookie; 353 struct intrhand *tmp; 354 u_long daif; 355 356 KASSERT(ih->ih_ipl < IPL_CLOCK); 357 358 daif = intr_disable(); 359 360 HWRITE4(sc, AIC_SW_CLR(ih->ih_irq), AIC_SW_BIT(ih->ih_irq)); 361 HWRITE4(sc, AIC_MASK_SET(ih->ih_irq), AIC_MASK_BIT(ih->ih_irq)); 362 363 /* Remove ourselves from the list of pending IRQs. */ 364 TAILQ_FOREACH(tmp, &sc->sc_irq_list[ih->ih_ipl], ih_list) { 365 if (tmp == ih) { 366 TAILQ_REMOVE(&sc->sc_irq_list[ih->ih_ipl], 367 ih, ih_list); 368 break; 369 } 370 } 371 372 sc->sc_irq_handler[ih->ih_irq] = NULL; 373 if (ih->ih_name) 374 evcount_detach(&ih->ih_count); 375 376 intr_restore(daif); 377 378 free(ih, M_DEVBUF, sizeof(*ih)); 379 } 380