1 /*- 2 * Copyright (c) 2006 Benno Rice. 3 * Copyright (C) 2007-2011 MARVELL INTERNATIONAL LTD. 4 * Copyright (c) 2012 Semihalf. 5 * All rights reserved. 6 * 7 * Developed by Semihalf. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * from: FreeBSD: //depot/projects/arm/src/sys/arm/xscale/pxa2x0/pxa2x0_icu.c, rev 1 30 * from: FreeBSD: src/sys/arm/mv/ic.c,v 1.5 2011/02/08 01:49:30 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "opt_platform.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/bus.h> 41 #include <sys/kernel.h> 42 #include <sys/cpuset.h> 43 #include <sys/ktr.h> 44 #include <sys/kdb.h> 45 #include <sys/module.h> 46 #include <sys/lock.h> 47 #include <sys/mutex.h> 48 #include <sys/rman.h> 49 #include <sys/proc.h> 50 51 #include <machine/bus.h> 52 #include <machine/intr.h> 53 #include <machine/cpufunc.h> 54 #include <machine/smp.h> 55 56 #include <arm/mv/mvvar.h> 57 #include <arm/mv/mvreg.h> 58 59 #include <dev/ofw/ofw_bus.h> 60 #include <dev/ofw/ofw_bus_subr.h> 61 #include <dev/fdt/fdt_common.h> 62 63 #ifdef ARM_INTRNG 64 #include "pic_if.h" 65 #endif 66 67 #ifdef DEBUG 68 #define debugf(fmt, args...) do { printf("%s(): ", __func__); \ 69 printf(fmt,##args); } while (0) 70 #else 71 #define debugf(fmt, args...) 72 #endif 73 74 #define MPIC_INT_ERR 4 75 #define MPIC_INT_MSI 96 76 77 #define MPIC_IRQ_MASK 0x3ff 78 79 #define MPIC_CTRL 0x0 80 #define MPIC_SOFT_INT 0x4 81 #define MPIC_SOFT_INT_DRBL1 (1 << 5) 82 #define MPIC_ERR_CAUSE 0x20 83 #define MPIC_ISE 0x30 84 #define MPIC_ICE 0x34 85 #define MPIC_INT_CTL(irq) (0x100 + (irq)*4) 86 87 #define MPIC_INT_IRQ_FIQ_MASK(cpuid) (0x101 << (cpuid)) 88 #define MPIC_CTRL_NIRQS(ctrl) (((ctrl) >> 2) & 0x3ff) 89 90 #define MPIC_IN_DRBL 0x08 91 #define MPIC_IN_DRBL_MASK 0x0c 92 #define MPIC_PPI_CAUSE 0x10 93 #define MPIC_CTP 0x40 94 #define MPIC_IIACK 0x44 95 #define MPIC_ISM 0x48 96 #define MPIC_ICM 0x4c 97 #define MPIC_ERR_MASK 0xe50 98 99 #define MPIC_PPI 32 100 101 struct mv_mpic_softc { 102 device_t sc_dev; 103 struct resource * mpic_res[4]; 104 bus_space_tag_t mpic_bst; 105 bus_space_handle_t mpic_bsh; 106 bus_space_tag_t cpu_bst; 107 bus_space_handle_t cpu_bsh; 108 bus_space_tag_t drbl_bst; 109 bus_space_handle_t drbl_bsh; 110 struct mtx mtx; 111 112 struct intr_irqsrc ** mpic_isrcs; 113 int nirqs; 114 void * intr_hand; 115 }; 116 117 static struct resource_spec mv_mpic_spec[] = { 118 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 119 { SYS_RES_MEMORY, 1, RF_ACTIVE }, 120 { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, 121 { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, 122 { -1, 0 } 123 }; 124 125 static struct ofw_compat_data compat_data[] = { 126 {"mrvl,mpic", true}, 127 {"marvell,mpic", true}, 128 {NULL, false} 129 }; 130 131 static struct mv_mpic_softc *mv_mpic_sc = NULL; 132 133 void mpic_send_ipi(int cpus, u_int ipi); 134 135 static int mv_mpic_probe(device_t); 136 static int mv_mpic_attach(device_t); 137 uint32_t mv_mpic_get_cause(void); 138 uint32_t mv_mpic_get_cause_err(void); 139 uint32_t mv_mpic_get_msi(void); 140 static void mpic_unmask_irq(uintptr_t nb); 141 static void mpic_mask_irq(uintptr_t nb); 142 static void mpic_mask_irq_err(uintptr_t nb); 143 static void mpic_unmask_irq_err(uintptr_t nb); 144 static int mpic_intr(void *arg); 145 static void mpic_unmask_msi(void); 146 #ifndef ARM_INTRNG 147 static void arm_mask_irq_err(uintptr_t); 148 static void arm_unmask_irq_err(uintptr_t); 149 #endif 150 151 #define MPIC_WRITE(softc, reg, val) \ 152 bus_space_write_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg), (val)) 153 #define MPIC_READ(softc, reg) \ 154 bus_space_read_4((softc)->mpic_bst, (softc)->mpic_bsh, (reg)) 155 156 #define MPIC_CPU_WRITE(softc, reg, val) \ 157 bus_space_write_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg), (val)) 158 #define MPIC_CPU_READ(softc, reg) \ 159 bus_space_read_4((softc)->cpu_bst, (softc)->cpu_bsh, (reg)) 160 161 #define MPIC_DRBL_WRITE(softc, reg, val) \ 162 bus_space_write_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg), (val)) 163 #define MPIC_DRBL_READ(softc, reg) \ 164 bus_space_read_4((softc)->drbl_bst, (softc)->drbl_bsh, (reg)) 165 166 static int 167 mv_mpic_probe(device_t dev) 168 { 169 170 if (!ofw_bus_status_okay(dev)) 171 return (ENXIO); 172 173 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 174 return (ENXIO); 175 176 device_set_desc(dev, "Marvell Integrated Interrupt Controller"); 177 return (0); 178 } 179 180 static int 181 mv_mpic_attach(device_t dev) 182 { 183 struct mv_mpic_softc *sc; 184 int error; 185 uint32_t val; 186 187 sc = (struct mv_mpic_softc *)device_get_softc(dev); 188 189 if (mv_mpic_sc != NULL) 190 return (ENXIO); 191 mv_mpic_sc = sc; 192 193 sc->sc_dev = dev; 194 195 mtx_init(&sc->mtx, "MPIC lock", NULL, MTX_SPIN); 196 197 error = bus_alloc_resources(dev, mv_mpic_spec, sc->mpic_res); 198 if (error) { 199 device_printf(dev, "could not allocate resources\n"); 200 return (ENXIO); 201 } 202 #ifdef ARM_INTRNG 203 if (sc->mpic_res[3] == NULL) 204 device_printf(dev, "No interrupt to use.\n"); 205 else 206 bus_setup_intr(dev, sc->mpic_res[3], INTR_TYPE_CLK, 207 mpic_intr, NULL, sc, &sc->intr_hand); 208 #endif 209 210 sc->mpic_bst = rman_get_bustag(sc->mpic_res[0]); 211 sc->mpic_bsh = rman_get_bushandle(sc->mpic_res[0]); 212 213 sc->cpu_bst = rman_get_bustag(sc->mpic_res[1]); 214 sc->cpu_bsh = rman_get_bushandle(sc->mpic_res[1]); 215 216 if (sc->mpic_res[2] != NULL) { 217 /* This is required only if MSIs are used. */ 218 sc->drbl_bst = rman_get_bustag(sc->mpic_res[2]); 219 sc->drbl_bsh = rman_get_bushandle(sc->mpic_res[2]); 220 } 221 222 bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, 223 MPIC_CTRL, 1); 224 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_CTP, 0); 225 226 val = MPIC_READ(mv_mpic_sc, MPIC_CTRL); 227 sc->nirqs = MPIC_CTRL_NIRQS(val); 228 229 #ifdef ARM_INTRNG 230 sc->mpic_isrcs = malloc(sc->nirqs * sizeof (*sc->mpic_isrcs), M_DEVBUF, 231 M_WAITOK | M_ZERO); 232 233 if (intr_pic_register(dev, OF_xref_from_device(dev)) != 0) { 234 device_printf(dev, "could not register PIC\n"); 235 bus_release_resources(dev, mv_mpic_spec, sc->mpic_res); 236 return (ENXIO); 237 } 238 #endif 239 240 mpic_unmask_msi(); 241 242 return (0); 243 } 244 245 #ifdef ARM_INTRNG 246 static int 247 mpic_intr(void *arg) 248 { 249 struct mv_mpic_softc *sc; 250 struct trapframe *tf; 251 struct intr_irqsrc *isrc; 252 uint32_t cause, irqsrc; 253 unsigned int irq; 254 u_int cpuid; 255 256 sc = arg; 257 tf = curthread->td_intr_frame; 258 cpuid = PCPU_GET(cpuid); 259 irq = 0; 260 261 for (cause = MPIC_CPU_READ(sc, MPIC_PPI_CAUSE); cause > 0; 262 cause >>= 1, irq++) { 263 if (cause & 1) { 264 irqsrc = MPIC_READ(sc, MPIC_INT_CTL(irq)); 265 if ((irqsrc & MPIC_INT_IRQ_FIQ_MASK(cpuid)) == 0) 266 continue; 267 isrc = sc->mpic_isrcs[irq]; 268 if (isrc == NULL) { 269 device_printf(sc->sc_dev, "Stray interrupt %u detected\n", irq); 270 mpic_mask_irq(irq); 271 continue; 272 } 273 intr_irq_dispatch(isrc, tf); 274 } 275 } 276 277 return (FILTER_HANDLED); 278 } 279 280 static int 281 mpic_attach_isrc(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int irq) 282 { 283 const char *name; 284 285 mtx_lock_spin(&sc->mtx); 286 if (sc->mpic_isrcs[irq] != NULL) { 287 mtx_unlock_spin(&sc->mtx); 288 return (sc->mpic_isrcs[irq] == isrc ? 0 : EEXIST); 289 } 290 sc->mpic_isrcs[irq] = isrc; 291 isrc->isrc_data = irq; 292 mtx_unlock_spin(&sc->mtx); 293 294 name = device_get_nameunit(sc->sc_dev); 295 intr_irq_set_name(isrc, "%s", name); 296 297 return (0); 298 } 299 300 #ifdef FDT 301 static int 302 mpic_map_fdt(struct mv_mpic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) 303 { 304 u_int irq; 305 int error; 306 307 if (isrc->isrc_ncells != 1) 308 return (EINVAL); 309 310 irq = isrc->isrc_cells[0]; 311 312 error = mpic_attach_isrc(sc, isrc, irq); 313 if (error != 0) 314 return (error); 315 316 isrc->isrc_nspc_num = irq; 317 isrc->isrc_trig = INTR_TRIGGER_CONFORM; 318 isrc->isrc_pol = INTR_POLARITY_CONFORM; 319 isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; 320 321 *irqp = irq; 322 323 return (0); 324 } 325 #endif 326 327 static int 328 mpic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) 329 { 330 struct mv_mpic_softc *sc; 331 int error; 332 u_int irq = 0; 333 334 sc = device_get_softc(dev); 335 336 #ifdef FDT 337 if (isrc->isrc_type == INTR_ISRCT_FDT) 338 error = mpic_map_fdt(sc, isrc, &irq); 339 else 340 #endif 341 error = EINVAL; 342 343 if (error == 0) 344 *is_percpu = irq < MPIC_PPI; 345 346 return (error); 347 } 348 349 static void 350 mpic_disable_source(device_t dev, struct intr_irqsrc *isrc) 351 { 352 u_int irq; 353 354 irq = isrc->isrc_data; 355 mpic_mask_irq(irq); 356 } 357 358 static void 359 mpic_enable_source(device_t dev, struct intr_irqsrc *isrc) 360 { 361 u_int irq; 362 363 irq = isrc->isrc_data; 364 mpic_unmask_irq(irq); 365 } 366 static void 367 mpic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 368 { 369 370 mpic_disable_source(dev, isrc); 371 } 372 373 static void 374 mpic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 375 { 376 377 mpic_enable_source(dev, isrc); 378 } 379 #endif 380 381 static device_method_t mv_mpic_methods[] = { 382 DEVMETHOD(device_probe, mv_mpic_probe), 383 DEVMETHOD(device_attach, mv_mpic_attach), 384 385 #ifdef ARM_INTRNG 386 DEVMETHOD(pic_register, mpic_register), 387 DEVMETHOD(pic_disable_source, mpic_disable_source), 388 DEVMETHOD(pic_enable_source, mpic_enable_source), 389 DEVMETHOD(pic_post_ithread, mpic_post_ithread), 390 DEVMETHOD(pic_pre_ithread, mpic_pre_ithread), 391 #endif 392 { 0, 0 } 393 }; 394 395 static driver_t mv_mpic_driver = { 396 "mpic", 397 mv_mpic_methods, 398 sizeof(struct mv_mpic_softc), 399 }; 400 401 static devclass_t mv_mpic_devclass; 402 403 EARLY_DRIVER_MODULE(mpic, simplebus, mv_mpic_driver, mv_mpic_devclass, 0, 0, 404 BUS_PASS_INTERRUPT); 405 406 #ifndef ARM_INTRNG 407 int 408 arm_get_next_irq(int last) 409 { 410 u_int irq, next = -1; 411 412 irq = mv_mpic_get_cause() & MPIC_IRQ_MASK; 413 CTR2(KTR_INTR, "%s: irq:%#x", __func__, irq); 414 415 if (irq != MPIC_IRQ_MASK) { 416 if (irq == MPIC_INT_ERR) 417 irq = mv_mpic_get_cause_err(); 418 if (irq == MPIC_INT_MSI) 419 irq = mv_mpic_get_msi(); 420 next = irq; 421 } 422 423 CTR3(KTR_INTR, "%s: last=%d, next=%d", __func__, last, next); 424 return (next); 425 } 426 427 /* 428 * XXX We can make arm_enable_irq to operate on ICE and then mask/unmask only 429 * by ISM/ICM and remove access to ICE in masking operation 430 */ 431 void 432 arm_mask_irq(uintptr_t nb) 433 { 434 435 mpic_mask_irq(nb); 436 } 437 438 439 static void 440 arm_mask_irq_err(uintptr_t nb) 441 { 442 443 mpic_mask_irq_err(nb); 444 } 445 446 void 447 arm_unmask_irq(uintptr_t nb) 448 { 449 450 mpic_unmask_irq(nb); 451 } 452 453 void 454 arm_unmask_irq_err(uintptr_t nb) 455 { 456 457 mpic_unmask_irq_err(nb); 458 } 459 #endif 460 461 static void 462 mpic_unmask_msi(void) 463 { 464 465 mpic_unmask_irq(MPIC_INT_MSI); 466 } 467 468 static void 469 mpic_unmask_irq_err(uintptr_t nb) 470 { 471 uint32_t mask; 472 uint8_t bit_off; 473 474 bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, 475 MPIC_ISE, MPIC_INT_ERR); 476 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, MPIC_INT_ERR); 477 478 bit_off = nb - ERR_IRQ; 479 mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK); 480 mask |= (1 << bit_off); 481 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask); 482 } 483 484 static void 485 mpic_mask_irq_err(uintptr_t nb) 486 { 487 uint32_t mask; 488 uint8_t bit_off; 489 490 bit_off = nb - ERR_IRQ; 491 mask = MPIC_CPU_READ(mv_mpic_sc, MPIC_ERR_MASK); 492 mask &= ~(1 << bit_off); 493 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ERR_MASK, mask); 494 } 495 496 static void 497 mpic_unmask_irq(uintptr_t nb) 498 { 499 500 if (nb < ERR_IRQ) { 501 bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, 502 MPIC_ISE, nb); 503 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ICM, nb); 504 } else if (nb < MSI_IRQ) 505 mpic_unmask_irq_err(nb); 506 507 if (nb == 0) 508 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL_MASK, 0xffffffff); 509 } 510 511 static void 512 mpic_mask_irq(uintptr_t nb) 513 { 514 515 if (nb < ERR_IRQ) { 516 bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, 517 MPIC_ICE, nb); 518 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_ISM, nb); 519 } else if (nb < MSI_IRQ) 520 mpic_mask_irq_err(nb); 521 } 522 523 uint32_t 524 mv_mpic_get_cause(void) 525 { 526 527 return (MPIC_CPU_READ(mv_mpic_sc, MPIC_IIACK)); 528 } 529 530 uint32_t 531 mv_mpic_get_cause_err(void) 532 { 533 uint32_t err_cause; 534 uint8_t bit_off; 535 536 err_cause = bus_space_read_4(mv_mpic_sc->mpic_bst, 537 mv_mpic_sc->mpic_bsh, MPIC_ERR_CAUSE); 538 539 if (err_cause) 540 bit_off = ffs(err_cause) - 1; 541 else 542 return (-1); 543 544 debugf("%s: irq:%x cause:%x\n", __func__, bit_off, err_cause); 545 return (ERR_IRQ + bit_off); 546 } 547 548 uint32_t 549 mv_mpic_get_msi(void) 550 { 551 uint32_t cause; 552 uint8_t bit_off; 553 554 KASSERT(mv_mpic_sc->drbl_bst != NULL, ("No doorbell in mv_mpic_get_msi")); 555 cause = MPIC_DRBL_READ(mv_mpic_sc, 0); 556 557 if (cause) 558 bit_off = ffs(cause) - 1; 559 else 560 return (-1); 561 562 debugf("%s: irq:%x cause:%x\n", __func__, bit_off, cause); 563 564 cause &= ~(1 << bit_off); 565 MPIC_DRBL_WRITE(mv_mpic_sc, 0, cause); 566 567 return (MSI_IRQ + bit_off); 568 } 569 570 int 571 mv_msi_data(int irq, uint64_t *addr, uint32_t *data) 572 { 573 u_long phys, base, size; 574 phandle_t node; 575 int error; 576 577 node = ofw_bus_get_node(mv_mpic_sc->sc_dev); 578 579 /* Get physical addres of register space */ 580 error = fdt_get_range(OF_parent(node), 0, &phys, &size); 581 if (error) { 582 printf("%s: Cannot get register physical address, err:%d", 583 __func__, error); 584 return (error); 585 } 586 587 /* Get offset of MPIC register space */ 588 error = fdt_regsize(node, &base, &size); 589 if (error) { 590 printf("%s: Cannot get MPIC register offset, err:%d", 591 __func__, error); 592 return (error); 593 } 594 595 *addr = phys + base + MPIC_SOFT_INT; 596 *data = MPIC_SOFT_INT_DRBL1 | irq; 597 598 return (0); 599 } 600 601 602 #if defined(SMP) && defined(SOC_MV_ARMADAXP) 603 void 604 intr_pic_init_secondary(void) 605 { 606 } 607 608 void 609 pic_ipi_send(cpuset_t cpus, u_int ipi) 610 { 611 uint32_t val, i; 612 613 val = 0x00000000; 614 for (i = 0; i < MAXCPU; i++) 615 if (CPU_ISSET(i, &cpus)) 616 val |= (1 << (8 + i)); 617 val |= ipi; 618 bus_space_write_4(mv_mpic_sc->mpic_bst, mv_mpic_sc->mpic_bsh, 619 MPIC_SOFT_INT, val); 620 } 621 622 int 623 pic_ipi_read(int i __unused) 624 { 625 uint32_t val; 626 int ipi; 627 628 val = MPIC_CPU_READ(mv_mpic_sc, MPIC_IN_DRBL); 629 if (val) { 630 ipi = ffs(val) - 1; 631 MPIC_CPU_WRITE(mv_mpic_sc, MPIC_IN_DRBL, ~(1 << ipi)); 632 return (ipi); 633 } 634 635 return (0x3ff); 636 } 637 638 void 639 pic_ipi_clear(int ipi) 640 { 641 } 642 643 #endif 644