1 /* $OpenBSD: sxiintc.c,v 1.11 2022/01/03 03:06:50 jsg Exp $ */ 2 /* 3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> 4 * Copyright (c) 2013 Artturi Alm 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/queue.h> 22 #include <sys/malloc.h> 23 #include <sys/device.h> 24 #include <sys/evcount.h> 25 26 #include <machine/bus.h> 27 #include <machine/fdt.h> 28 29 #include <armv7/sunxi/sxiintc.h> 30 31 #include <dev/ofw/openfirm.h> 32 #include <dev/ofw/fdt.h> 33 34 #ifdef DEBUG_INTC 35 #define DPRINTF(x) do { if (sxiintcdebug) printf x; } while (0) 36 #define DPRINTFN(n,x) do { if (sxiintcdebug>(n)) printf x; } while (0) 37 int sxiintcdebug = 10; 38 char *ipl_strtbl[NIPL] = { 39 "IPL_NONE", 40 "IPL_SOFT", 41 "IPL_SOFTCLOCK", 42 "IPL_SOFTNET", 43 "IPL_SOFTTTY", 44 "IPL_BIO|IPL_USB", 45 "IPL_NET", 46 "IPL_TTY", 47 "IPL_VM", 48 "IPL_AUDIO", 49 "IPL_CLOCK", 50 "IPL_STATCLOCK", 51 "IPL_SCHED|IPL_HIGH" 52 }; 53 #else 54 #define DPRINTF(x) 55 #define DPRINTFN(n,x) 56 #endif 57 58 #define NIRQ 96 59 #define NBANKS 3 60 #define NIRQPRIOREGS 5 61 62 /* registers */ 63 #define INTC_VECTOR_REG 0x00 64 #define INTC_BASE_ADR_REG 0x04 65 #define INTC_PROTECTION_REG 0x08 66 #define INTC_NMI_CTRL_REG 0x0c 67 68 #define INTC_IRQ_PENDING_REG0 0x10 69 #define INTC_IRQ_PENDING_REG1 0x14 70 #define INTC_IRQ_PENDING_REG2 0x18 71 72 #define INTC_SELECT_REG0 0x30 73 #define INTC_SELECT_REG1 0x34 74 #define INTC_SELECT_REG2 0x38 75 76 #define INTC_ENABLE_REG0 0x40 77 #define INTC_ENABLE_REG1 0x44 78 #define INTC_ENABLE_REG2 0x48 79 80 #define INTC_MASK_REG0 0x50 81 #define INTC_MASK_REG1 0x54 82 #define INTC_MASK_REG2 0x58 83 84 #define INTC_RESP_REG0 0x60 85 #define INTC_RESP_REG1 0x64 86 #define INTC_RESP_REG2 0x68 87 88 #define INTC_PRIO_REG0 0x80 89 #define INTC_PRIO_REG1 0x84 90 #define INTC_PRIO_REG2 0x88 91 #define INTC_PRIO_REG3 0x8c 92 #define INTC_PRIO_REG4 0x90 93 94 #define INTC_IRQ_PENDING_REG(_b) (0x10 + ((_b) * 4)) 95 #define INTC_FIQ_PENDING_REG(_b) (0x20 + ((_b) * 4)) 96 #define INTC_SELECT_REG(_b) (0x30 + ((_b) * 4)) 97 #define INTC_ENABLE_REG(_b) (0x40 + ((_b) * 4)) 98 #define INTC_MASK_REG(_b) (0x50 + ((_b) * 4)) 99 #define INTC_RESP_REG(_b) (0x60 + ((_b) * 4)) 100 #define INTC_PRIO_REG(_b) (0x80 + ((_b) * 4)) 101 102 #define IRQ2REG32(i) (((i) >> 5) & 0x3) 103 #define IRQ2BIT32(i) ((i) & 0x1f) 104 105 #define IRQ2REG16(i) (((i) >> 4) & 0x5) 106 #define IRQ2BIT16(i) (((i) & 0x0f) * 2) 107 108 #define INTC_IRQ_HIPRIO 0x3 109 #define INTC_IRQ_ENABLED 0x2 110 #define INTC_IRQ_DISABLED 0x1 111 #define INTC_IRQ_LOWPRIO 0x0 112 #define INTC_PRIOCLEAR(i) (~(INTC_IRQ_HIPRIO << IRQ2BIT16((i)))) 113 #define INTC_PRIOENABLE(i) (INTC_IRQ_ENABLED << IRQ2BIT16((i))) 114 #define INTC_PRIOHI(i) (INTC_IRQ_HIPRIO << IRQ2BIT16((i))) 115 116 117 struct intrhand { 118 TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */ 119 int (*ih_func)(void *); /* handler */ 120 void *ih_arg; /* arg for handler */ 121 int ih_ipl; /* IPL_* */ 122 int ih_irq; /* IRQ number */ 123 struct evcount ih_count; 124 char *ih_name; 125 }; 126 127 struct intrq { 128 TAILQ_HEAD(, intrhand) iq_list; /* handler list */ 129 int iq_irq; /* IRQ to mask while handling */ 130 int iq_levels; /* IPL_*'s this IRQ has */ 131 int iq_ist; /* share type */ 132 }; 133 134 volatile int a1xsoftint_pending; 135 136 struct intrq sxiintc_handler[NIRQ]; 137 u_int32_t sxiintc_smask[NIPL]; 138 u_int32_t sxiintc_imask[NBANKS][NIPL]; 139 struct interrupt_controller sxiintc_ic; 140 141 bus_space_tag_t sxiintc_iot; 142 bus_space_handle_t sxiintc_ioh; 143 int sxiintc_nirq; 144 145 int sxiintc_match(struct device *, void *, void *); 146 void sxiintc_attach(struct device *, struct device *, void *); 147 int sxiintc_spllower(int); 148 int sxiintc_splraise(int); 149 void sxiintc_setipl(int); 150 void sxiintc_calc_masks(void); 151 void *sxiintc_intr_establish_fdt(void *, int *, int, struct cpu_info *, 152 int (*)(void *), void *, char *); 153 154 const struct cfattach sxiintc_ca = { 155 sizeof (struct device), sxiintc_match, sxiintc_attach 156 }; 157 158 struct cfdriver sxiintc_cd = { 159 NULL, "sxiintc", DV_DULL 160 }; 161 162 int sxiintc_attached = 0; 163 164 int 165 sxiintc_match(struct device *parent, void *match, void *aux) 166 { 167 struct fdt_attach_args *faa = aux; 168 169 return OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-ic"); 170 } 171 172 void 173 sxiintc_attach(struct device *parent, struct device *self, void *aux) 174 { 175 struct fdt_attach_args *faa = aux; 176 int i, j; 177 178 sxiintc_iot = faa->fa_iot; 179 if (bus_space_map(sxiintc_iot, faa->fa_reg[0].addr, 180 faa->fa_reg[0].size, 0, &sxiintc_ioh)) 181 panic("sxiintc_attach: bus_space_map failed!"); 182 183 /* disable/mask/clear all interrupts */ 184 for (i = 0; i < NBANKS; i++) { 185 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_ENABLE_REG(i), 0); 186 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_MASK_REG(i), 0); 187 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_IRQ_PENDING_REG(i), 188 0xffffffff); 189 for (j = 0; j < NIPL; j++) 190 sxiintc_imask[i][j] = 0; 191 } 192 193 /* XXX */ 194 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_PROTECTION_REG, 1); 195 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_NMI_CTRL_REG, 0); 196 197 for (i = 0; i < NIRQ; i++) 198 TAILQ_INIT(&sxiintc_handler[i].iq_list); 199 200 sxiintc_calc_masks(); 201 202 arm_init_smask(); 203 sxiintc_attached = 1; 204 205 /* insert self as interrupt handler */ 206 arm_set_intr_handler(sxiintc_splraise, sxiintc_spllower, sxiintc_splx, 207 sxiintc_setipl, 208 sxiintc_intr_establish, sxiintc_intr_disestablish, sxiintc_intr_string, 209 sxiintc_irq_handler); 210 sxiintc_setipl(IPL_HIGH); /* XXX ??? */ 211 enable_interrupts(PSR_I); 212 printf("\n"); 213 214 sxiintc_ic.ic_node = faa->fa_node; 215 sxiintc_ic.ic_establish = sxiintc_intr_establish_fdt; 216 arm_intr_register_fdt(&sxiintc_ic); 217 } 218 219 void 220 sxiintc_calc_masks(void) 221 { 222 struct cpu_info *ci = curcpu(); 223 int irq; 224 struct intrhand *ih; 225 int i; 226 227 for (irq = 0; irq < NIRQ; irq++) { 228 int max = IPL_NONE; 229 int min = IPL_HIGH; 230 TAILQ_FOREACH(ih, &sxiintc_handler[irq].iq_list, ih_list) { 231 if (ih->ih_ipl > max) 232 max = ih->ih_ipl; 233 if (ih->ih_ipl < min) 234 min = ih->ih_ipl; 235 } 236 237 sxiintc_handler[irq].iq_irq = max; 238 239 if (max == IPL_NONE) 240 min = IPL_NONE; 241 242 #ifdef DEBUG_INTC 243 if (min != IPL_NONE) { 244 printf("irq %d to block at %d %d reg %d bit %d\n", 245 irq, max, min, IRQ2REG32(irq), 246 IRQ2BIT32(irq)); 247 } 248 #endif 249 /* Enable interrupts at lower levels, clear -> enable */ 250 for (i = 0; i < min; i++) 251 sxiintc_imask[IRQ2REG32(irq)][i] &= 252 ~(1 << IRQ2BIT32(irq)); 253 for (; i < NIPL; i++) 254 sxiintc_imask[IRQ2REG32(irq)][i] |= 255 (1 << IRQ2BIT32(irq)); 256 /* XXX - set enable/disable, priority */ 257 } 258 259 sxiintc_setipl(ci->ci_cpl); 260 } 261 262 void 263 sxiintc_splx(int new) 264 { 265 struct cpu_info *ci = curcpu(); 266 sxiintc_setipl(new); 267 268 if (ci->ci_ipending & arm_smask[ci->ci_cpl]) 269 arm_do_pending_intr(ci->ci_cpl); 270 } 271 272 int 273 sxiintc_spllower(int new) 274 { 275 struct cpu_info *ci = curcpu(); 276 int old = ci->ci_cpl; 277 sxiintc_splx(new); 278 return (old); 279 } 280 281 int 282 sxiintc_splraise(int new) 283 { 284 struct cpu_info *ci = curcpu(); 285 int old; 286 old = ci->ci_cpl; 287 288 /* 289 * setipl must always be called because there is a race window 290 * where the variable is updated before the mask is set 291 * an interrupt occurs in that window without the mask always 292 * being set, the hardware might not get updated on the next 293 * splraise completely messing up spl protection. 294 */ 295 if (old > new) 296 new = old; 297 298 sxiintc_setipl(new); 299 300 return (old); 301 } 302 303 void 304 sxiintc_setipl(int new) 305 { 306 struct cpu_info *ci = curcpu(); 307 int i, psw; 308 #if 1 309 /* 310 * XXX not needed, because all interrupts are disabled 311 * by default, so touching maskregs has no effect, i hope. 312 */ 313 if (sxiintc_attached == 0) { 314 ci->ci_cpl = new; 315 return; 316 } 317 #endif 318 psw = disable_interrupts(PSR_I); 319 ci->ci_cpl = new; 320 for (i = 0; i < NBANKS; i++) 321 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 322 INTC_MASK_REG(i), sxiintc_imask[i][new]); 323 restore_interrupts(psw); 324 } 325 326 void 327 sxiintc_irq_handler(void *frame) 328 { 329 struct intrhand *ih; 330 void *arg; 331 uint32_t pr; 332 int irq, prio, s; 333 334 irq = bus_space_read_4(sxiintc_iot, sxiintc_ioh, INTC_VECTOR_REG) >> 2; 335 if (irq == 0) 336 return; 337 338 prio = sxiintc_handler[irq].iq_irq; 339 s = sxiintc_splraise(prio); 340 splassert(prio); 341 342 pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 343 INTC_ENABLE_REG(IRQ2REG32(irq))); 344 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 345 INTC_ENABLE_REG(IRQ2REG32(irq)), 346 pr & ~(1 << IRQ2BIT32(irq))); 347 348 /* clear pending */ 349 pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 350 INTC_IRQ_PENDING_REG(IRQ2REG32(irq))); 351 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 352 INTC_IRQ_PENDING_REG(IRQ2REG32(irq)), 353 pr | (1 << IRQ2BIT32(irq))); 354 355 pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 356 INTC_ENABLE_REG(IRQ2REG32(irq))); 357 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 358 INTC_ENABLE_REG(IRQ2REG32(irq)), 359 pr | (1 << IRQ2BIT32(irq))); 360 361 TAILQ_FOREACH(ih, &sxiintc_handler[irq].iq_list, ih_list) { 362 if (ih->ih_arg) 363 arg = ih->ih_arg; 364 else 365 arg = frame; 366 367 if (ih->ih_func(arg)) 368 ih->ih_count.ec_count++; 369 } 370 sxiintc_splx(s); 371 } 372 373 void * 374 sxiintc_intr_establish(int irq, int level, struct cpu_info *ci, 375 int (*func)(void *), void *arg, char *name) 376 { 377 int psw; 378 struct intrhand *ih; 379 uint32_t er; 380 381 if (irq <= 0 || irq >= NIRQ) 382 panic("intr_establish: bogus irq %d %s", irq, name); 383 384 if (ci == NULL) 385 ci = &cpu_info_primary; 386 else if (!CPU_IS_PRIMARY(ci)) 387 return NULL; 388 389 DPRINTF(("intr_establish: irq %d level %d [%s]\n", irq, level, 390 name != NULL ? name : "NULL")); 391 392 psw = disable_interrupts(PSR_I); 393 394 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 395 ih->ih_func = func; 396 ih->ih_arg = arg; 397 ih->ih_ipl = level & IPL_IRQMASK; 398 ih->ih_irq = irq; 399 ih->ih_name = name; 400 401 TAILQ_INSERT_TAIL(&sxiintc_handler[irq].iq_list, ih, ih_list); 402 403 if (name != NULL) 404 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 405 406 er = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 407 INTC_ENABLE_REG(IRQ2REG32(irq))); 408 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 409 INTC_ENABLE_REG(IRQ2REG32(irq)), 410 er | (1 << IRQ2BIT32(irq))); 411 412 sxiintc_calc_masks(); 413 414 restore_interrupts(psw); 415 return (ih); 416 } 417 418 void * 419 sxiintc_intr_establish_fdt(void *cookie, int *cell, int level, 420 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 421 { 422 return sxiintc_intr_establish(cell[0], level, ci, func, arg, name); 423 } 424 425 void 426 sxiintc_intr_disestablish(void *cookie) 427 { 428 struct intrhand *ih = cookie; 429 int irq = ih->ih_irq; 430 int psw; 431 uint32_t er; 432 433 psw = disable_interrupts(PSR_I); 434 435 TAILQ_REMOVE(&sxiintc_handler[irq].iq_list, ih, ih_list); 436 437 if (ih->ih_name != NULL) 438 evcount_detach(&ih->ih_count); 439 440 free(ih, M_DEVBUF, 0); 441 442 er = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 443 INTC_ENABLE_REG(IRQ2REG32(irq))); 444 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 445 INTC_ENABLE_REG(IRQ2REG32(irq)), 446 er & ~(1 << IRQ2BIT32(irq))); 447 448 sxiintc_calc_masks(); 449 450 restore_interrupts(psw); 451 } 452 453 const char * 454 sxiintc_intr_string(void *cookie) 455 { 456 return "asd?"; 457 } 458