sxiintc.c revision 1.12
1/* $OpenBSD: sxiintc.c,v 1.12 2024/04/29 12:33:17 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) 37int sxiintcdebug = 10; 38char *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 117struct 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 127struct 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 134struct intrq sxiintc_handler[NIRQ]; 135u_int32_t sxiintc_smask[NIPL]; 136u_int32_t sxiintc_imask[NBANKS][NIPL]; 137struct interrupt_controller sxiintc_ic; 138 139bus_space_tag_t sxiintc_iot; 140bus_space_handle_t sxiintc_ioh; 141int sxiintc_nirq; 142 143int sxiintc_match(struct device *, void *, void *); 144void sxiintc_attach(struct device *, struct device *, void *); 145int sxiintc_spllower(int); 146int sxiintc_splraise(int); 147void sxiintc_setipl(int); 148void sxiintc_calc_masks(void); 149void *sxiintc_intr_establish_fdt(void *, int *, int, struct cpu_info *, 150 int (*)(void *), void *, char *); 151 152const struct cfattach sxiintc_ca = { 153 sizeof (struct device), sxiintc_match, sxiintc_attach 154}; 155 156struct cfdriver sxiintc_cd = { 157 NULL, "sxiintc", DV_DULL 158}; 159 160int sxiintc_attached = 0; 161 162int 163sxiintc_match(struct device *parent, void *match, void *aux) 164{ 165 struct fdt_attach_args *faa = aux; 166 167 return OF_is_compatible(faa->fa_node, "allwinner,sun4i-a10-ic"); 168} 169 170void 171sxiintc_attach(struct device *parent, struct device *self, void *aux) 172{ 173 struct fdt_attach_args *faa = aux; 174 int i, j; 175 176 sxiintc_iot = faa->fa_iot; 177 if (bus_space_map(sxiintc_iot, faa->fa_reg[0].addr, 178 faa->fa_reg[0].size, 0, &sxiintc_ioh)) 179 panic("sxiintc_attach: bus_space_map failed!"); 180 181 /* disable/mask/clear all interrupts */ 182 for (i = 0; i < NBANKS; i++) { 183 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_ENABLE_REG(i), 0); 184 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_MASK_REG(i), 0); 185 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_IRQ_PENDING_REG(i), 186 0xffffffff); 187 for (j = 0; j < NIPL; j++) 188 sxiintc_imask[i][j] = 0; 189 } 190 191 /* XXX */ 192 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_PROTECTION_REG, 1); 193 bus_space_write_4(sxiintc_iot, sxiintc_ioh, INTC_NMI_CTRL_REG, 0); 194 195 for (i = 0; i < NIRQ; i++) 196 TAILQ_INIT(&sxiintc_handler[i].iq_list); 197 198 sxiintc_calc_masks(); 199 200 arm_init_smask(); 201 sxiintc_attached = 1; 202 203 /* insert self as interrupt handler */ 204 arm_set_intr_handler(sxiintc_splraise, sxiintc_spllower, sxiintc_splx, 205 sxiintc_setipl, 206 sxiintc_intr_establish, sxiintc_intr_disestablish, sxiintc_intr_string, 207 sxiintc_irq_handler); 208 sxiintc_setipl(IPL_HIGH); /* XXX ??? */ 209 enable_interrupts(PSR_I); 210 printf("\n"); 211 212 sxiintc_ic.ic_node = faa->fa_node; 213 sxiintc_ic.ic_establish = sxiintc_intr_establish_fdt; 214 arm_intr_register_fdt(&sxiintc_ic); 215} 216 217void 218sxiintc_calc_masks(void) 219{ 220 struct cpu_info *ci = curcpu(); 221 int irq; 222 struct intrhand *ih; 223 int i; 224 225 for (irq = 0; irq < NIRQ; irq++) { 226 int max = IPL_NONE; 227 int min = IPL_HIGH; 228 TAILQ_FOREACH(ih, &sxiintc_handler[irq].iq_list, ih_list) { 229 if (ih->ih_ipl > max) 230 max = ih->ih_ipl; 231 if (ih->ih_ipl < min) 232 min = ih->ih_ipl; 233 } 234 235 sxiintc_handler[irq].iq_irq = max; 236 237 if (max == IPL_NONE) 238 min = IPL_NONE; 239 240#ifdef DEBUG_INTC 241 if (min != IPL_NONE) { 242 printf("irq %d to block at %d %d reg %d bit %d\n", 243 irq, max, min, IRQ2REG32(irq), 244 IRQ2BIT32(irq)); 245 } 246#endif 247 /* Enable interrupts at lower levels, clear -> enable */ 248 for (i = 0; i < min; i++) 249 sxiintc_imask[IRQ2REG32(irq)][i] &= 250 ~(1 << IRQ2BIT32(irq)); 251 for (; i < NIPL; i++) 252 sxiintc_imask[IRQ2REG32(irq)][i] |= 253 (1 << IRQ2BIT32(irq)); 254 /* XXX - set enable/disable, priority */ 255 } 256 257 sxiintc_setipl(ci->ci_cpl); 258} 259 260void 261sxiintc_splx(int new) 262{ 263 struct cpu_info *ci = curcpu(); 264 sxiintc_setipl(new); 265 266 if (ci->ci_ipending & arm_smask[ci->ci_cpl]) 267 arm_do_pending_intr(ci->ci_cpl); 268} 269 270int 271sxiintc_spllower(int new) 272{ 273 struct cpu_info *ci = curcpu(); 274 int old = ci->ci_cpl; 275 sxiintc_splx(new); 276 return (old); 277} 278 279int 280sxiintc_splraise(int new) 281{ 282 struct cpu_info *ci = curcpu(); 283 int old; 284 old = ci->ci_cpl; 285 286 /* 287 * setipl must always be called because there is a race window 288 * where the variable is updated before the mask is set 289 * an interrupt occurs in that window without the mask always 290 * being set, the hardware might not get updated on the next 291 * splraise completely messing up spl protection. 292 */ 293 if (old > new) 294 new = old; 295 296 sxiintc_setipl(new); 297 298 return (old); 299} 300 301void 302sxiintc_setipl(int new) 303{ 304 struct cpu_info *ci = curcpu(); 305 int i, psw; 306#if 1 307 /* 308 * XXX not needed, because all interrupts are disabled 309 * by default, so touching maskregs has no effect, i hope. 310 */ 311 if (sxiintc_attached == 0) { 312 ci->ci_cpl = new; 313 return; 314 } 315#endif 316 psw = disable_interrupts(PSR_I); 317 ci->ci_cpl = new; 318 for (i = 0; i < NBANKS; i++) 319 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 320 INTC_MASK_REG(i), sxiintc_imask[i][new]); 321 restore_interrupts(psw); 322} 323 324void 325sxiintc_irq_handler(void *frame) 326{ 327 struct intrhand *ih; 328 void *arg; 329 uint32_t pr; 330 int irq, prio, s; 331 332 irq = bus_space_read_4(sxiintc_iot, sxiintc_ioh, INTC_VECTOR_REG) >> 2; 333 if (irq == 0) 334 return; 335 336 prio = sxiintc_handler[irq].iq_irq; 337 s = sxiintc_splraise(prio); 338 splassert(prio); 339 340 pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 341 INTC_ENABLE_REG(IRQ2REG32(irq))); 342 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 343 INTC_ENABLE_REG(IRQ2REG32(irq)), 344 pr & ~(1 << IRQ2BIT32(irq))); 345 346 /* clear pending */ 347 pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 348 INTC_IRQ_PENDING_REG(IRQ2REG32(irq))); 349 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 350 INTC_IRQ_PENDING_REG(IRQ2REG32(irq)), 351 pr | (1 << IRQ2BIT32(irq))); 352 353 pr = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 354 INTC_ENABLE_REG(IRQ2REG32(irq))); 355 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 356 INTC_ENABLE_REG(IRQ2REG32(irq)), 357 pr | (1 << IRQ2BIT32(irq))); 358 359 TAILQ_FOREACH(ih, &sxiintc_handler[irq].iq_list, ih_list) { 360 if (ih->ih_arg) 361 arg = ih->ih_arg; 362 else 363 arg = frame; 364 365 if (ih->ih_func(arg)) 366 ih->ih_count.ec_count++; 367 } 368 sxiintc_splx(s); 369} 370 371void * 372sxiintc_intr_establish(int irq, int level, struct cpu_info *ci, 373 int (*func)(void *), void *arg, char *name) 374{ 375 int psw; 376 struct intrhand *ih; 377 uint32_t er; 378 379 if (irq <= 0 || irq >= NIRQ) 380 panic("intr_establish: bogus irq %d %s", irq, name); 381 382 if (ci == NULL) 383 ci = &cpu_info_primary; 384 else if (!CPU_IS_PRIMARY(ci)) 385 return NULL; 386 387 DPRINTF(("intr_establish: irq %d level %d [%s]\n", irq, level, 388 name != NULL ? name : "NULL")); 389 390 psw = disable_interrupts(PSR_I); 391 392 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 393 ih->ih_func = func; 394 ih->ih_arg = arg; 395 ih->ih_ipl = level & IPL_IRQMASK; 396 ih->ih_irq = irq; 397 ih->ih_name = name; 398 399 TAILQ_INSERT_TAIL(&sxiintc_handler[irq].iq_list, ih, ih_list); 400 401 if (name != NULL) 402 evcount_attach(&ih->ih_count, name, &ih->ih_irq); 403 404 er = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 405 INTC_ENABLE_REG(IRQ2REG32(irq))); 406 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 407 INTC_ENABLE_REG(IRQ2REG32(irq)), 408 er | (1 << IRQ2BIT32(irq))); 409 410 sxiintc_calc_masks(); 411 412 restore_interrupts(psw); 413 return (ih); 414} 415 416void * 417sxiintc_intr_establish_fdt(void *cookie, int *cell, int level, 418 struct cpu_info *ci, int (*func)(void *), void *arg, char *name) 419{ 420 return sxiintc_intr_establish(cell[0], level, ci, func, arg, name); 421} 422 423void 424sxiintc_intr_disestablish(void *cookie) 425{ 426 struct intrhand *ih = cookie; 427 int irq = ih->ih_irq; 428 int psw; 429 uint32_t er; 430 431 psw = disable_interrupts(PSR_I); 432 433 TAILQ_REMOVE(&sxiintc_handler[irq].iq_list, ih, ih_list); 434 435 if (ih->ih_name != NULL) 436 evcount_detach(&ih->ih_count); 437 438 free(ih, M_DEVBUF, 0); 439 440 er = bus_space_read_4(sxiintc_iot, sxiintc_ioh, 441 INTC_ENABLE_REG(IRQ2REG32(irq))); 442 bus_space_write_4(sxiintc_iot, sxiintc_ioh, 443 INTC_ENABLE_REG(IRQ2REG32(irq)), 444 er & ~(1 << IRQ2BIT32(irq))); 445 446 sxiintc_calc_masks(); 447 448 restore_interrupts(psw); 449} 450 451const char * 452sxiintc_intr_string(void *cookie) 453{ 454 return "asd?"; 455} 456