diff --git a/fpga/lib/CMakeLists.txt b/fpga/lib/CMakeLists.txt index 1c03327a8..30a98fddc 100644 --- a/fpga/lib/CMakeLists.txt +++ b/fpga/lib/CMakeLists.txt @@ -12,6 +12,7 @@ set(SOURCES ips/fifo.c ips/dma.c ips/intc.cpp + ips/intc.c ips/gpio.c ips/rtds_axis.c diff --git a/fpga/lib/ips/intc.c b/fpga/lib/ips/intc.c new file mode 100644 index 000000000..7fcf109d6 --- /dev/null +++ b/fpga/lib/ips/intc.c @@ -0,0 +1,180 @@ +/** AXI-PCIe Interrupt controller + * + * @author Steffen Vogel + * @copyright 2017, Steffen Vogel + * @license GNU General Public License (version 3) + * + * VILLASfpga + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *********************************************************************************/ + +#include + +#include "config.h" +#include "log.h" +#include "plugin.h" + +#include "kernel/vfio.h" +#include "kernel/kernel.h" + +#include "fpga/ip.h" +#include "fpga/card.h" +#include "fpga/ips/intc.h" + +int intc_start(struct fpga_ip *c) +{ + int ret; + + struct fpga_card *f = c->card; + struct intc *intc = (struct intc *) c->_vd; + + uintptr_t base = (uintptr_t) f->map + c->baseaddr; + + if (c != f->intc) + error("There can be only one interrupt controller per FPGA"); + + intc->num_irqs = vfio_pci_msi_init(&f->vfio_device, intc->efds); + if (intc->num_irqs < 0) + return -1; + + ret = vfio_pci_msi_find(&f->vfio_device, intc->nos); + if (ret) + return -2; + + /* For each IRQ */ + for (int i = 0; i < intc->num_irqs; i++) { + /* Pin to core */ + ret = kernel_irq_setaffinity(intc->nos[i], f->affinity, NULL); + if (ret) + serror("Failed to change affinity of VFIO-MSI interrupt"); + + /* Setup vector */ + XIntc_Out32(base + XIN_IVAR_OFFSET + i * 4, i); + } + + XIntc_Out32(base + XIN_IMR_OFFSET, 0); /* Use manual acknowlegement for all IRQs */ + XIntc_Out32(base + XIN_IAR_OFFSET, 0xFFFFFFFF); /* Acknowlege all pending IRQs manually */ + XIntc_Out32(base + XIN_IMR_OFFSET, 0xFFFFFFFF); /* Use fast acknowlegement for all IRQs */ + XIntc_Out32(base + XIN_IER_OFFSET, 0x00000000); /* Disable all IRQs by default */ + XIntc_Out32(base + XIN_MER_OFFSET, XIN_INT_HARDWARE_ENABLE_MASK | XIN_INT_MASTER_ENABLE_MASK); + + debug(4, "FPGA: enabled interrupts"); + + return 0; +} + +int intc_destroy(struct fpga_ip *c) +{ + struct fpga_card *f = c->card; + struct intc *intc = (struct intc *) c->_vd; + + vfio_pci_msi_deinit(&f->vfio_device, intc->efds); + + return 0; +} + +int intc_enable(struct fpga_ip *c, uint32_t mask, int flags) +{ + struct fpga_card *f = c->card; + struct intc *intc = (struct intc *) c->_vd; + + uint32_t ier, imr; + uintptr_t base = (uintptr_t) f->map + c->baseaddr; + + /* Current state of INTC */ + ier = XIntc_In32(base + XIN_IER_OFFSET); + imr = XIntc_In32(base + XIN_IMR_OFFSET); + + /* Clear pending IRQs */ + XIntc_Out32(base + XIN_IAR_OFFSET, mask); + + for (int i = 0; i < intc->num_irqs; i++) { + if (mask & (1 << i)) + intc->flags[i] = flags; + } + + if (flags & INTC_POLLING) { + XIntc_Out32(base + XIN_IMR_OFFSET, imr & ~mask); + XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask); + } + else { + XIntc_Out32(base + XIN_IER_OFFSET, ier | mask); + XIntc_Out32(base + XIN_IMR_OFFSET, imr | mask); + } + + debug(3, "New ier = %#x", XIntc_In32(base + XIN_IER_OFFSET)); + debug(3, "New imr = %#x", XIntc_In32(base + XIN_IMR_OFFSET)); + debug(3, "New isr = %#x", XIntc_In32(base + XIN_ISR_OFFSET)); + + debug(8, "FPGA: Interupt enabled: mask=%#x flags=%#x", mask, flags); + + return 0; +} + +int intc_disable(struct fpga_ip *c, uint32_t mask) +{ + struct fpga_card *f = c->card; + + uintptr_t base = (uintptr_t) f->map + c->baseaddr; + uint32_t ier = XIntc_In32(base + XIN_IER_OFFSET); + + XIntc_Out32(base + XIN_IER_OFFSET, ier & ~mask); + + return 0; +} + +uint64_t intc_wait(struct fpga_ip *c, int irq) +{ + struct fpga_card *f = c->card; + struct intc *intc = (struct intc *) c->_vd; + + uintptr_t base = (uintptr_t) f->map + c->baseaddr; + + if (intc->flags[irq] & INTC_POLLING) { + uint32_t isr, mask = 1 << irq; + + do { + isr = XIntc_In32(base + XIN_ISR_OFFSET); + pthread_testcancel(); + } while ((isr & mask) != mask); + + XIntc_Out32(base + XIN_IAR_OFFSET, mask); + + return 1; + } + else { + uint64_t cnt; + ssize_t ret = read(intc->efds[irq], &cnt, sizeof(cnt)); + if (ret != sizeof(cnt)) + return 0; + + return cnt; + } +} + +static struct plugin p = { + .name = "Xilinx's programmable interrupt controller", + .description = "", + .type = PLUGIN_TYPE_FPGA_IP, + .ip = { + .vlnv = { "acs.eonerc.rwth-aachen.de", "user", "axi_pcie_intc", NULL }, + .type = FPGA_IP_TYPE_MISC, + .start = intc_start, + .destroy = intc_destroy, + .size = sizeof(struct intc) + } +}; + +REGISTER_PLUGIN(&p)