1 /* $OpenBSD: intc.c,v 1.7 2016/08/06 18:21:34 patrick 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 volatile int softint_pending; 95 96 struct intrq intc_handler[INTC_MAX_IRQ]; 97 u_int32_t intc_smask[NIPL]; 98 u_int32_t intc_imask[INTC_MAX_BANKS][NIPL]; 99 struct interrupt_controller intc_ic; 100 101 bus_space_tag_t intc_iot; 102 bus_space_handle_t intc_ioh; 103 int intc_nirq; 104 105 int intc_match(struct device *, void *, void *); 106 void intc_attach(struct device *, struct device *, void *); 107 int intc_spllower(int new); 108 int intc_splraise(int new); 109 void intc_setipl(int new); 110 void intc_calc_mask(void); 111 void *intc_intr_establish_fdt(void *, int *, int, int (*)(void *), 112 void *, char *); 113 114 struct cfattach intc_ca = { 115 sizeof (struct device), intc_match, intc_attach 116 }; 117 118 struct cfdriver intc_cd = { 119 NULL, "intc", DV_DULL 120 }; 121 122 int intc_attached = 0; 123 124 int 125 intc_match(struct device *parent, void *match, void *aux) 126 { 127 struct fdt_attach_args *faa = aux; 128 129 return (OF_is_compatible(faa->fa_node, "ti,omap3-intc") || 130 OF_is_compatible(faa->fa_node, "ti,am33xx-intc")); 131 } 132 133 void 134 intc_attach(struct device *parent, struct device *self, void *aux) 135 { 136 struct fdt_attach_args *faa = aux; 137 int i; 138 u_int32_t rev; 139 140 intc_iot = faa->fa_iot; 141 if (bus_space_map(intc_iot, faa->fa_reg[0].addr, 142 faa->fa_reg[0].size, 0, &intc_ioh)) 143 panic("intc_attach: bus_space_map failed!"); 144 145 rev = bus_space_read_4(intc_iot, intc_ioh, INTC_REVISION); 146 147 printf(" rev %d.%d\n", rev >> 4 & 0xf, rev & 0xf); 148 149 /* software reset of the part? */ 150 /* set protection bit (kernel only)? */ 151 #if 0 152 bus_space_write_4(intc_iot, intc_ioh, INTC_PROTECTION, 153 INTC_PROTECTION_PROT); 154 #endif 155 156 /* enable interface clock power saving mode */ 157 bus_space_write_4(intc_iot, intc_ioh, INTC_SYSCONFIG, 158 INTC_SYSCONFIG_AUTOIDLE); 159 160 if (OF_is_compatible(faa->fa_node, "ti,am33xx-intc")) 161 intc_nirq = 128; 162 else 163 intc_nirq = 96; 164 165 /* mask all interrupts */ 166 for (i = 0; i < INTC_NUM_BANKS; i++) 167 bus_space_write_4(intc_iot, intc_ioh, INTC_MIRn(i), 0xffffffff); 168 169 for (i = 0; i < INTC_NUM_IRQ; i++) { 170 bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(i), 171 INTC_ILR_PRIs(INTC_MIN_PRI)|INTC_ILR_IRQ); 172 173 TAILQ_INIT(&intc_handler[i].iq_list); 174 } 175 176 intc_calc_mask(); 177 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, 178 INTC_CONTROL_NEWIRQ); 179 180 intc_attached = 1; 181 182 /* insert self as interrupt handler */ 183 arm_set_intr_handler(intc_splraise, intc_spllower, intc_splx, 184 intc_setipl, 185 intc_intr_establish, intc_intr_disestablish, intc_intr_string, 186 intc_irq_handler); 187 188 intc_setipl(IPL_HIGH); /* XXX ??? */ 189 enable_interrupts(PSR_I); 190 191 intc_ic.ic_node = faa->fa_node; 192 intc_ic.ic_establish = intc_intr_establish_fdt; 193 arm_intr_register_fdt(&intc_ic); 194 } 195 196 void 197 intc_calc_mask(void) 198 { 199 struct cpu_info *ci = curcpu(); 200 int irq; 201 struct intrhand *ih; 202 int i; 203 204 for (irq = 0; irq < INTC_NUM_IRQ; irq++) { 205 int max = IPL_NONE; 206 int min = IPL_HIGH; 207 TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { 208 if (ih->ih_ipl > max) 209 max = ih->ih_ipl; 210 211 if (ih->ih_ipl < min) 212 min = ih->ih_ipl; 213 } 214 215 intc_handler[irq].iq_irq = max; 216 217 if (max == IPL_NONE) 218 min = IPL_NONE; 219 220 #ifdef DEBUG_INTC 221 if (min != IPL_NONE) { 222 printf("irq %d to block at %d %d reg %d bit %d\n", 223 irq, max, min, INTC_IRQ_TO_REG(irq), 224 INTC_IRQ_TO_REGi(irq)); 225 } 226 #endif 227 /* Enable interrupts at lower levels, clear -> enable */ 228 for (i = 0; i < min; i++) 229 intc_imask[INTC_IRQ_TO_REG(irq)][i] &= 230 ~(1 << INTC_IRQ_TO_REGi(irq)); 231 for (; i <= IPL_HIGH; i++) 232 intc_imask[INTC_IRQ_TO_REG(irq)][i] |= 233 1 << INTC_IRQ_TO_REGi(irq); 234 /* XXX - set enable/disable, priority */ 235 bus_space_write_4(intc_iot, intc_ioh, INTC_ILRn(irq), 236 INTC_ILR_PRIs(NIPL-max)|INTC_ILR_IRQ); 237 } 238 arm_init_smask(); 239 intc_setipl(ci->ci_cpl); 240 } 241 242 void 243 intc_splx(int new) 244 { 245 struct cpu_info *ci = curcpu(); 246 intc_setipl(new); 247 248 if (ci->ci_ipending & arm_smask[ci->ci_cpl]) 249 arm_do_pending_intr(ci->ci_cpl); 250 } 251 252 int 253 intc_spllower(int new) 254 { 255 struct cpu_info *ci = curcpu(); 256 int old = ci->ci_cpl; 257 intc_splx(new); 258 return (old); 259 } 260 261 int 262 intc_splraise(int new) 263 { 264 struct cpu_info *ci = curcpu(); 265 int old; 266 old = ci->ci_cpl; 267 268 /* 269 * setipl must always be called because there is a race window 270 * where the variable is updated before the mask is set 271 * an interrupt occurs in that window without the mask always 272 * being set, the hardware might not get updated on the next 273 * splraise completely messing up spl protection. 274 */ 275 if (old > new) 276 new = old; 277 278 intc_setipl(new); 279 280 return (old); 281 } 282 283 void 284 intc_setipl(int new) 285 { 286 struct cpu_info *ci = curcpu(); 287 int i; 288 int psw; 289 if (intc_attached == 0) 290 return; 291 292 psw = disable_interrupts(PSR_I); 293 #if 0 294 { 295 volatile static int recursed = 0; 296 if (recursed == 0) { 297 recursed = 1; 298 if (new != 12) 299 printf("setipl %d\n", new); 300 recursed = 0; 301 } 302 } 303 #endif 304 ci->ci_cpl = new; 305 for (i = 0; i < INTC_NUM_BANKS; i++) 306 bus_space_write_4(intc_iot, intc_ioh, 307 INTC_MIRn(i), intc_imask[i][new]); 308 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, 309 INTC_CONTROL_NEWIRQ); 310 restore_interrupts(psw); 311 } 312 313 void 314 intc_intr_bootstrap(vaddr_t addr) 315 { 316 int i, j; 317 extern struct bus_space armv7_bs_tag; 318 intc_iot = &armv7_bs_tag; 319 intc_ioh = addr; 320 for (i = 0; i < INTC_NUM_BANKS; i++) 321 for (j = 0; j < NIPL; j++) 322 intc_imask[i][j] = 0xffffffff; 323 } 324 325 void 326 intc_irq_handler(void *frame) 327 { 328 int irq, pri, s; 329 struct intrhand *ih; 330 void *arg; 331 332 irq = bus_space_read_4(intc_iot, intc_ioh, INTC_SIR_IRQ); 333 #ifdef DEBUG_INTC 334 printf("irq %d fired\n", irq); 335 #endif 336 337 pri = intc_handler[irq].iq_irq; 338 s = intc_splraise(pri); 339 TAILQ_FOREACH(ih, &intc_handler[irq].iq_list, ih_list) { 340 if (ih->ih_arg != 0) 341 arg = ih->ih_arg; 342 else 343 arg = frame; 344 345 if (ih->ih_func(arg)) 346 ih->ih_count.ec_count++; 347 348 } 349 bus_space_write_4(intc_iot, intc_ioh, INTC_CONTROL, 350 INTC_CONTROL_NEWIRQ); 351 352 intc_splx(s); 353 } 354 355 void * 356 intc_intr_establish(int irqno, int level, int (*func)(void *), 357 void *arg, char *name) 358 { 359 int psw; 360 struct intrhand *ih; 361 362 if (irqno < 0 || irqno >= INTC_NUM_IRQ) 363 panic("intc_intr_establish: bogus irqnumber %d: %s", 364 irqno, name); 365 psw = disable_interrupts(PSR_I); 366 367 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 368 ih->ih_func = func; 369 ih->ih_arg = arg; 370 ih->ih_ipl = level; 371 ih->ih_irq = irqno; 372 ih->ih_name = name; 373 374 TAILQ_INSERT_TAIL(&intc_handler[irqno].iq_list, ih, ih_list); 375 376 if (name != NULL) 377 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 378 379 #ifdef DEBUG_INTC 380 printf("intc_intr_establish irq %d level %d [%s]\n", irqno, level, 381 name); 382 #endif 383 intc_calc_mask(); 384 385 restore_interrupts(psw); 386 return (ih); 387 } 388 389 void * 390 intc_intr_establish_fdt(void *cookie, int *cell, int level, 391 int (*func)(void *), void *arg, char *name) 392 { 393 return intc_intr_establish(cell[0], level, func, arg, name); 394 } 395 396 void 397 intc_intr_disestablish(void *cookie) 398 { 399 int psw; 400 struct intrhand *ih = cookie; 401 int irqno = ih->ih_irq; 402 psw = disable_interrupts(PSR_I); 403 TAILQ_REMOVE(&intc_handler[irqno].iq_list, ih, ih_list); 404 if (ih->ih_name != NULL) 405 evcount_detach(&ih->ih_count); 406 free(ih, M_DEVBUF, 0); 407 restore_interrupts(psw); 408 } 409 410 const char * 411 intc_intr_string(void *cookie) 412 { 413 return "huh?"; 414 } 415 416 417 #if 0 418 int intc_tst(void *a); 419 420 int 421 intc_tst(void *a) 422 { 423 printf("inct_tst called\n"); 424 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); 425 return 1; 426 } 427 428 void intc_test(void); 429 void intc_test(void) 430 { 431 void * ih; 432 printf("about to register handler\n"); 433 ih = intc_intr_establish(1, IPL_BIO, intc_tst, NULL, "intctst"); 434 435 printf("about to set bit\n"); 436 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_SETn(0), 2); 437 438 printf("about to clear bit\n"); 439 bus_space_write_4(intc_iot, intc_ioh, INTC_ISR_CLEARn(0), 2); 440 441 printf("about to remove handler\n"); 442 intc_intr_disestablish(ih); 443 444 printf("done\n"); 445 } 446 #endif 447