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