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