1 /* $OpenBSD: intc.c,v 1.14 2024/04/29 12:42:06 jsg Exp $ */ 2 /* 3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> 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/systm.h> 20 #include <sys/queue.h> 21 #include <sys/malloc.h> 22 #include <sys/device.h> 23 #include <sys/evcount.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <armv7/armv7/armv7var.h> 29 30 #include <dev/ofw/openfirm.h> 31 #include <dev/ofw/fdt.h> 32 33 #include "intc.h" 34 35 #define INTC_NUM_IRQ intc_nirq 36 #define INTC_NUM_BANKS (intc_nirq/32) 37 #define INTC_MAX_IRQ 128 38 #define INTC_MAX_BANKS (INTC_MAX_IRQ/32) 39 40 /* registers */ 41 #define INTC_REVISION 0x00 /* R */ 42 #define INTC_SYSCONFIG 0x10 /* RW */ 43 #define INTC_SYSCONFIG_AUTOIDLE 0x1 44 #define INTC_SYSCONFIG_SOFTRESET 0x2 45 #define INTC_SYSSTATUS 0x14 /* R */ 46 #define INTC_SYSSYSTATUS_RESETDONE 0x1 47 #define INTC_SIR_IRQ 0x40 /* R */ 48 #define INTC_SIR_FIQ 0x44 /* R */ 49 #define INTC_CONTROL 0x48 /* RW */ 50 #define INTC_CONTROL_NEWIRQ 0x1 51 #define INTC_CONTROL_NEWFIQ 0x2 52 #define INTC_CONTROL_GLOBALMASK 0x1 53 #define INTC_PROTECTION 0x4c /* RW */ 54 #define INTC_PROTECTION_PROT 1 /* only privileged mode */ 55 #define INTC_IDLE 0x50 /* RW */ 56 57 #define INTC_IRQ_TO_REG(i) (((i) >> 5) & 0x3) 58 #define INTC_IRQ_TO_REGi(i) ((i) & 0x1f) 59 #define INTC_ITRn(i) 0x80+(0x20*i)+0x00 /* R */ 60 #define INTC_MIRn(i) 0x80+(0x20*i)+0x04 /* RW */ 61 #define INTC_CLEARn(i) 0x80+(0x20*i)+0x08 /* RW */ 62 #define INTC_SETn(i) 0x80+(0x20*i)+0x0c /* RW */ 63 #define INTC_ISR_SETn(i) 0x80+(0x20*i)+0x10 /* RW */ 64 #define INTC_ISR_CLEARn(i) 0x80+(0x20*i)+0x14 /* RW */ 65 #define INTC_PENDING_IRQn(i) 0x80+(0x20*i)+0x18 /* R */ 66 #define INTC_PENDING_FIQn(i) 0x80+(0x20*i)+0x1c /* R */ 67 68 #define INTC_ILRn(i) 0x100+(4*i) 69 #define INTC_ILR_IRQ 0x0 /* not of FIQ */ 70 #define INTC_ILR_FIQ 0x1 71 #define INTC_ILR_PRIs(pri) ((pri) << 2) 72 #define INTC_ILR_PRI(reg) (((reg) >> 2) & 0x2f) 73 #define INTC_MIN_PRI 63 74 #define INTC_STD_PRI 32 75 #define INTC_MAX_PRI 0 76 77 struct intrhand { 78 TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ 79 int (*ih_func)(void *); /* handler */ 80 void *ih_arg; /* arg for handler */ 81 int ih_ipl; /* IPL_* */ 82 int ih_irq; /* IRQ number */ 83 struct evcount ih_count; 84 char *ih_name; 85 }; 86 87 struct intrq { 88 TAILQ_HEAD(, intrhand) iq_list; /* handler list */ 89 int iq_irq; /* IRQ to mask while handling */ 90 int iq_levels; /* IPL_*'s this IRQ has */ 91 int iq_ist; /* share type */ 92 }; 93 94 struct intrq intc_handler[INTC_MAX_IRQ]; 95 u_int32_t intc_smask[NIPL]; 96 u_int32_t intc_imask[INTC_MAX_BANKS][NIPL]; 97 struct interrupt_controller intc_ic; 98 99 bus_space_tag_t intc_iot; 100 bus_space_handle_t intc_ioh; 101 int intc_nirq; 102 103 int intc_match(struct device *, void *, void *); 104 void intc_attach(struct device *, struct device *, void *); 105 int intc_spllower(int new); 106 int intc_splraise(int new); 107 void intc_setipl(int new); 108 void intc_calc_mask(void); 109 void *intc_intr_establish_fdt(void *, int *, int, struct cpu_info *, 110 int (*)(void *), void *, char *); 111 112 const struct cfattach intc_ca = { 113 sizeof (struct device), intc_match, intc_attach 114 }; 115 116 struct cfdriver intc_cd = { 117 NULL, "intc", DV_DULL 118 }; 119 120 int intc_attached = 0; 121 122 int 123 intc_match(struct device *parent, void *match, void *aux) 124 { 125 struct fdt_attach_args *faa = aux; 126 127 return (OF_is_compatible(faa->fa_node, "ti,omap3-intc") || 128 OF_is_compatible(faa->fa_node, "ti,am33xx-intc")); 129 } 130 131 void 132 intc_attach(struct device *parent, struct device *self, void *aux) 133 { 134 struct fdt_attach_args *faa = aux; 135 int i; 136 u_int32_t rev; 137 138 intc_iot = faa->fa_iot; 139 if (bus_space_map(intc_iot, faa->fa_reg[0].addr, 140 faa->fa_reg[0].size, 0, &intc_ioh)) 141 panic("intc_attach: bus_space_map failed!"); 142 143 rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION); 144 145 printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); 146 147 /* software reset of the part? */ 148 /* set protection bit (kernel only)? */ 149 #if 0 150 bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION, 151 INTC_PROTECTION_PROT); 152 #endif 153 154 /* enable interface clock power saving mode */ 155 bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG, 156 INTC_SYSCONFIG_AUTOIDLE); 157 158 if (OF_is_compatible(faa->fa_node, "ti,am33xx-intc")) 159 intc_nirq = 128; 160 else 161 intc_nirq = 96; 162 163 /* mask all interrupts */ 164 for (i = 0; i < INTC_NUM_BANKS; i++) 165 bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff); 166 167 for (i = 0; i < INTC_NUM_IRQ; i++) { 168 bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i), 169 INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ); 170 171 TAILQ_INIT(&intc_handler[i].iq_list); 172 } 173 174 intc_calc_mask(); 175 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, 176 INTC_CONTROL_NEWIRQ); 177 178 intc_attached = 1; 179 180 /* insert self as interrupt handler */ 181 arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx, 182 intc_setipl, 183 intc_intr_establish, intc_intr_disestablish, intc_intr_string, 184 intc_irq_handler); 185 186 intc_setipl(IPL_HIGH); /* XXX ??? */ 187 enable_interrupts(PSR_I); 188 189 intc_ic.ic_node = faa->fa_node; 190 intc_ic.ic_establish = intc_intr_establish_fdt; 191 arm_intr_register_fdt(&intc_ic); 192 } 193 194 void 195 intc_calc_mask(void) 196 { 197 struct cpu_info *ci = curcpu(); 198 int irq; 199 struct intrhand *ih; 200 int i; 201 202 for (irq = 0; irq < INTC_NUM_IRQ; irq++) { 203 int max = IPL_NONE; 204 int min = IPL_HIGH; 205 TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { 206 if (ih->ih_ipl > max) 207 max = ih->ih_ipl; 208 209 if (ih->ih_ipl < min) 210 min = ih->ih_ipl; 211 } 212 213 intc_handler[irq].iq_irq = max; 214 215 if (max == IPL_NONE) 216 min = IPL_NONE; 217 218 #ifdef DEBUG_INTC 219 if (min != IPL_NONE) { 220 printf("irq %d to block at %d %d reg %d bit %d\n", 221 irq, max, min, INTC_IRQ_TO_REG(irq), 222 INTC_IRQ_TO_REGi(irq)); 223 } 224 #endif 225 /* Enable interrupts at lower levels, clear -> enable */ 226 for (i = 0; i < min; i++) 227 intc_imask[INTC_IRQ_TO_REG(irq)][i] &= 228 ~(1 << INTC_IRQ_TO_REGi(irq)); 229 for (; i <= IPL_HIGH; i++) 230 intc_imask[INTC_IRQ_TO_REG(irq)][i] |= 231 1 << INTC_IRQ_TO_REGi(irq); 232 /* XXX - set enable/disable, priority */ 233 bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq), 234 INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ); 235 } 236 arm_init_smask(); 237 intc_setipl(ci->ci_cpl); 238 } 239 240 void 241 intc_splx(int new) 242 { 243 struct cpu_info *ci = curcpu(); 244 intc_setipl(new); 245 246 if (ci->ci_ipending & arm_smask[ci->ci_cpl]) 247 arm_do_pending_intr(ci->ci_cpl); 248 } 249 250 int 251 intc_spllower(int new) 252 { 253 struct cpu_info *ci = curcpu(); 254 int old = ci->ci_cpl; 255 intc_splx(new); 256 return (old); 257 } 258 259 int 260 intc_splraise(int new) 261 { 262 struct cpu_info *ci = curcpu(); 263 int old; 264 old = ci->ci_cpl; 265 266 /* 267 * setipl must always be called because there is a race window 268 * where the variable is updated before the mask is set 269 * an interrupt occurs in that window without the mask always 270 * being set, the hardware might not get updated on the next 271 * splraise completely messing up spl protection. 272 */ 273 if (old > new) 274 new = old; 275 276 intc_setipl(new); 277 278 return (old); 279 } 280 281 void 282 intc_setipl(int new) 283 { 284 struct cpu_info *ci = curcpu(); 285 int i; 286 int psw; 287 if (intc_attached == 0) 288 return; 289 290 psw = disable_interrupts(PSR_I); 291 #if 0 292 { 293 volatile static int recursed = 0; 294 if (recursed == 0) { 295 recursed = 1; 296 if (new != 12) 297 printf("setipl %d\n", new); 298 recursed = 0; 299 } 300 } 301 #endif 302 ci->ci_cpl = new; 303 for (i = 0; i < INTC_NUM_BANKS; i++) 304 bus_space_write_4(intc_iot, intc_ioh, 305 INTC_MIRn(i), intc_imask[i][new]); 306 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, 307 INTC_CONTROL_NEWIRQ); 308 restore_interrupts(psw); 309 } 310 311 void 312 intc_irq_handler(void *frame) 313 { 314 int irq, pri, s; 315 struct intrhand *ih; 316 void *arg; 317 318 irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ); 319 #ifdef DEBUG_INTC 320 printf("irq %d fired\n", irq); 321 #endif 322 323 pri = intc_handler[irq].iq_irq; 324 s = intc_splraise(pri); 325 TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { 326 if (ih->ih_arg) 327 arg = ih->ih_arg; 328 else 329 arg = frame; 330 331 if (ih->ih_func(arg)) 332 ih->ih_count.ec_count++; 333 334 } 335 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, 336 INTC_CONTROL_NEWIRQ); 337 338 intc_splx(s); 339 } 340 341 void * 342 intc_intr_establish(int irqno, int level, struct cpu_info *ci, 343 int (*func)(void *), void *arg, char *name) 344 { 345 int psw; 346 struct intrhand *ih; 347 348 if (irqno < 0 || irqno >= INTC_NUM_IRQ) 349 panic("intc_intr_establish: bogus irqnumber %d: %s", 350 irqno, name); 351 352 if (ci == NULL) 353 ci = &cpu_info_primary; 354 else if (!CPU_IS_PRIMARY(ci)) 355 return NULL; 356 357 psw = disable_interrupts(PSR_I); 358 359 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 360 ih->ih_func = func; 361 ih->ih_arg = arg; 362 ih->ih_ipl = level & IPL_IRQMASK; 363 ih->ih_irq = irqno; 364 ih->ih_name = name; 365 366 TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list); 367 368 if (name != NULL) 369 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 370 371 #ifdef DEBUG_INTC 372 printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level, 373 name); 374 #endif 375 intc_calc_mask(); 376 377 restore_interrupts(psw); 378 return (ih); 379 } 380 381 void * 382 intc_intr_establish_fdt(void *cookie, int *cell, int level, 383 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 384 { 385 return intc_intr_establish(cell[0], level, ci, func, arg, name); 386 } 387 388 void 389 intc_intr_disestablish(void *cookie) 390 { 391 int psw; 392 struct intrhand *ih = cookie; 393 int irqno = ih->ih_irq; 394 psw = disable_interrupts(PSR_I); 395 TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list); 396 if (ih->ih_name != NULL) 397 evcount_detach(&ih->ih_count); 398 free(ih, M_DEVBUF, 0); 399 restore_interrupts(psw); 400 } 401 402 const char * 403 intc_intr_string(void *cookie) 404 { 405 return "huh?"; 406 } 407 408 409 #if 0 410 int intc_tst(void *a); 411 412 int 413 intc_tst(void *a) 414 { 415 printf("inct_tst called\n"); 416 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); 417 return 1; 418 } 419 420 void intc_test(void); 421 void intc_test(void) 422 { 423 void * ih; 424 printf("about to register handler\n"); 425 ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst"); 426 427 printf("about to set bit\n"); 428 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2); 429 430 printf("about to clear bit\n"); 431 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); 432 433 printf("about to remove handler\n"); 434 intc_intr_disestablish(ih); 435 436 printf("done\n"); 437 } 438 #endif 439