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