/*- * Copyright 2003-2011 Netlogic Microsystems (Netlogic). All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * THIS SOFTWARE IS PROVIDED BY Netlogic Microsystems ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NETLOGIC OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. * * NETLOGIC_BSD */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pic_if.h" #define XLR_MAX_INTR 64 struct xlp_intrsrc { void (*busack)(int); /* Additional ack */ u_int vector; /* event corresponding to intr */ u_int irq; }; static struct xlp_intrsrc xlp_interrupts[XLR_MAX_INTR]; static void xlp_ic_identify(driver_t *driver, device_t parent) { if (device_find_child(parent, "xlpic", -1) == NULL) BUS_ADD_CHILD(parent, 0, "xlpic", 0); } static int xlp_ic_probe(device_t dev) { /* XXX: use FDT? */ device_set_desc(dev, "XLP Root Interrupt Controller"); return (BUS_PROBE_NOWILDCARD); } static int xlp_ic_attach(device_t dev) { mips_register_pic(dev, 0, XLR_MAX_INTR, 1); root_pic = dev; return (0); } void xlp_enable_irq(int irq) { uint64_t eimr; eimr = nlm_read_c0_eimr(); nlm_write_c0_eimr(eimr | (1ULL << irq)); } static void xlp_eoi(device_t dev, u_int irq) { struct xlp_intrsrc *src = &xlp_interrupts[irq]; if (src->busack) src->busack(src->irq); nlm_pic_ack(xlp_pic_base, xlp_irq_to_irt(src->irq)); } static void xlp_enable(device_t dev, u_int irq, u_int vector) { struct xlp_intrsrc *src = NULL; if (irq < 0 || irq > XLR_MAX_INTR) panic("%s called for unknown hard intr %d", __func__, irq); /* * FIXME locking - not needed now, because we do this only on * startup from CPU0 */ src = &xlp_interrupts[irq]; /* * PIC based interrupts need ack in PIC, and some SoC * components need additional acks (e.g. PCI) */ src->irq = irq; src->vector = vector; xlp_enable_irq(irq); } void xlp_set_bus_ack(u_int irq, void (*busack)(int)) { xlp_interrupts[irq].busack = busack; } static void xlp_dispatch(device_t dev, struct trapframe *tf) { uint64_t eirr, eimr; int i; critical_enter(); /* find a list of enabled interrupts */ eirr = nlm_read_c0_eirr(); eimr = nlm_read_c0_eimr(); eirr &= eimr; if (eirr == 0) { critical_exit(); return; } /* * No need to clear the EIRR here as the handler writes to * compare which ACKs the interrupt. */ if (eirr & (1 << IRQ_TIMER)) { mips_dispatch_intr(xlp_interrupts[IRQ_TIMER].vector, tf); critical_exit(); return; } /* FIXME sched pin >? LOCK>? */ for (i = sizeof(eirr) * 8 - 1; i >= 0; i--) { if ((eirr & (1ULL << i)) == 0) continue; /* Ack the IRQ on the CPU */ nlm_write_c0_eirr(1ULL << i); mips_dispatch_intr(xlp_interrupts[i].vector, tf); } critical_exit(); } static device_method_t xlp_ic_methods[] = { /* Device interface */ DEVMETHOD(device_identify, xlp_ic_identify), DEVMETHOD(device_probe, xlp_ic_probe), DEVMETHOD(device_attach, xlp_ic_attach), /* PIC interface */ DEVMETHOD(pic_dispatch, xlp_dispatch), DEVMETHOD(pic_enable, xlp_enable), DEVMETHOD(pic_eoi, xlp_eoi), DEVMETHOD_END, }; static driver_t xlp_ic_driver = { "xlpic", xlp_ic_methods, 0 }; static devclass_t xlp_ic_devclass; EARLY_DRIVER_MODULE(xlpic, nexus, xlp_ic_driver, xlp_ic_devclass, 0, 0, BUS_PASS_INTERRUPT);