sxiintc.c revision 1.10
1/* $OpenBSD: sxiintc.c,v 1.10 2021/10/24 17:52:28 mpi 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 134volatile int a1xsoftint_pending; 135 136struct intrq sxiintc_handler[NIRQ]; 137u_int32_t sxiintc_smask[NIPL]; 138u_int32_t sxiintc_imask[NBANKS][NIPL]; 139struct interrupt_controller sxiintc_ic; 140 141bus_space_tag_t sxiintc_iot; 142bus_space_handle_t sxiintc_ioh; 143int sxiintc_nirq; 144 145int sxiintc_match(struct device *, void *, void *); 146void sxiintc_attach(struct device *, struct device *, void *); 147int sxiintc_spllower(int); 148int sxiintc_splraise(int); 149void sxiintc_setipl(int); 150void sxiintc_calc_masks(void); 151void *sxiintc_intr_establish_fdt(void *, int *, int, struct cpu_info *, 152 int (*)(void *), void *, char *); 153 154const struct cfattach sxiintc_ca = { 155 sizeof (struct device), sxiintc_match, sxiintc_attach 156}; 157 158struct cfdriver sxiintc_cd = { 159 NULL, "sxiintc", DV_DULL 160}; 161 162int sxiintc_attached = 0; 163 164int 165sxiintc_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 172void 173sxiintc_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 219void 220sxiintc_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 262void 263sxiintc_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 272int 273sxiintc_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 281int 282sxiintc_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 303void 304sxiintc_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 326void 327sxiintc_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 != 0) 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 373void * 374sxiintc_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 418void * 419sxiintc_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 425void 426sxiintc_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 453const char * 454sxiintc_intr_string(void *cookie) 455{ 456 return "asd?"; 457} 458