root/sys/dev/xilinx/xlnx_pcib.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 *
 * Copyright (c) 2020-2025 Ruslan Bukin <br@bsdpad.com>
 *
 * This software was developed by SRI International and the University of
 * Cambridge Computer Laboratory (Department of Computer Science and
 * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
 * DARPA SSITH research programme.
 *
 * 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 THE AUTHOR AND CONTRIBUTORS ``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 THE AUTHOR 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.
 */

#include "opt_platform.h"

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/kernel.h>
#include <sys/rman.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/endian.h>
#include <sys/cpuset.h>
#include <sys/mutex.h>
#include <sys/proc.h>

#include <machine/intr.h>
#include <machine/bus.h>

#include <vm/vm.h>
#include <vm/vm_extern.h>
#include <vm/vm_kern.h>
#include <vm/pmap.h>

#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>

#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pci_host_generic.h>
#include <dev/pci/pci_host_generic_fdt.h>
#include <dev/pci/pcib_private.h>

#include "xlnx_pcib.h"

#include "ofw_bus_if.h"
#include "msi_if.h"
#include "pcib_if.h"
#include "pic_if.h"

#define XLNX_PCIB_MAX_MSI       64

static int xlnx_pcib_fdt_attach(device_t);
static int xlnx_pcib_fdt_probe(device_t);
static int xlnx_pcib_fdt_get_id(device_t, device_t, enum pci_id_type,
    uintptr_t *);
static void xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc,
    bool mask);

struct xlnx_pcib_softc {
        struct generic_pcie_fdt_softc   fdt_sc;
        struct resource                 *res[4];
        struct mtx                      mtx;
        void                            *msi_page;
        struct xlnx_pcib_irqsrc         *isrcs;
        device_t                        dev;
        void                            *intr_cookie[3];
};

static struct resource_spec xlnx_pcib_spec[] = {
        { SYS_RES_MEMORY,       0,      RF_ACTIVE },
        { SYS_RES_IRQ,          0,      RF_ACTIVE },
        { SYS_RES_IRQ,          1,      RF_ACTIVE },
        { SYS_RES_IRQ,          2,      RF_ACTIVE },
        { -1, 0 }
};

struct xlnx_pcib_irqsrc {
        struct intr_irqsrc      isrc;
        u_int                   irq;
#define XLNX_IRQ_FLAG_USED      (1 << 0)
        u_int                   flags;
};

static struct ofw_compat_data compat_data[] = {
        { "xlnx,xdma-host-3.00",        1 },
        { "xlnx,axi-pcie-host-1.00.a",  1 },
        { NULL,                         0 },
};

static void
xlnx_pcib_clear_err_interrupts(struct generic_pcie_core_softc *sc)
{
        uint32_t reg;

        reg = bus_read_4(sc->res, XLNX_PCIE_RPERRFRR);

        if (reg & RPERRFRR_VALID) {
                device_printf(sc->dev, "Requested ID: %x\n",
                    reg & RPERRFRR_REQ_ID_M);
                bus_write_4(sc->res, XLNX_PCIE_RPERRFRR, ~0U);
        }
}

static int
xlnx_pcib_intr(void *arg)
{
        struct generic_pcie_fdt_softc *fdt_sc;
        struct generic_pcie_core_softc *sc;
        struct xlnx_pcib_softc *xlnx_sc;
        uint32_t val, mask, status;

        xlnx_sc = arg;
        fdt_sc = &xlnx_sc->fdt_sc;
        sc = &fdt_sc->base;

        val = bus_read_4(sc->res, XLNX_PCIE_IDR);
        mask = bus_read_4(sc->res, XLNX_PCIE_IMR);

        status = val & mask;
        if (!status)
                return (FILTER_HANDLED);

        if (status & IMR_LINK_DOWN)
                device_printf(sc->dev, "Link down");

        if (status & IMR_HOT_RESET)
                device_printf(sc->dev, "Hot reset");

        if (status & IMR_CORRECTABLE)
                xlnx_pcib_clear_err_interrupts(sc);

        if (status & IMR_FATAL)
                xlnx_pcib_clear_err_interrupts(sc);

        if (status & IMR_NON_FATAL)
                xlnx_pcib_clear_err_interrupts(sc);

        if (status & IMR_MSI) {
                device_printf(sc->dev, "MSI interrupt");

                /* FIFO mode MSI not implemented. */
        }

        if (status & IMR_INTX) {
                device_printf(sc->dev, "INTx received");

                /* Not implemented. */
        }

        if (status & IMR_SLAVE_UNSUPP_REQ)
                device_printf(sc->dev, "Slave unsupported request");

        if (status & IMR_SLAVE_UNEXP_COMPL)
                device_printf(sc->dev, "Slave unexpected completion");

        if (status & IMR_SLAVE_COMPL_TIMOUT)
                device_printf(sc->dev, "Slave completion timeout");

        if (status & IMR_SLAVE_ERROR_POISON)
                device_printf(sc->dev, "Slave error poison");

        if (status & IMR_SLAVE_COMPL_ABORT)
                device_printf(sc->dev, "Slave completion abort");

        if (status & IMR_SLAVE_ILLEG_BURST)
                device_printf(sc->dev, "Slave illegal burst");

        if (status & IMR_MASTER_DECERR)
                device_printf(sc->dev, "Master decode error");

        if (status & IMR_MASTER_SLVERR)
                device_printf(sc->dev, "Master slave error");

        bus_write_4(sc->res, XLNX_PCIE_IDR, val);

        return (FILTER_HANDLED);
}

static void
xlnx_pcib_handle_msi_intr(void *arg, int msireg)
{ 
        struct generic_pcie_fdt_softc *fdt_sc;
        struct generic_pcie_core_softc *sc;
        struct xlnx_pcib_softc *xlnx_sc;
        struct xlnx_pcib_irqsrc *xi;
        struct trapframe *tf;
        int irq;
        int reg;
        int i;

        xlnx_sc = arg;
        fdt_sc = &xlnx_sc->fdt_sc;
        sc = &fdt_sc->base;
        tf = curthread->td_intr_frame;

        do {
                reg = bus_read_4(sc->res, msireg);

                for (i = 0; i < sizeof(uint32_t) * 8; i++) {
                        if (reg & (1 << i)) {
                                bus_write_4(sc->res, msireg, (1 << i));

                                irq = i;
                                if (msireg == XLNX_PCIE_RPMSIID2)
                                        irq += 32;

                                xi = &xlnx_sc->isrcs[irq];
                                if (intr_isrc_dispatch(&xi->isrc, tf) != 0) {
                                        /* Disable stray. */
                                        xlnx_pcib_msi_mask(sc->dev,
                                            &xi->isrc, 1);
                                        device_printf(sc->dev,
                                            "Stray irq %u disabled\n", irq);
                                }
                        }
                }
        } while (reg != 0);
}

static int
xlnx_pcib_msi0_intr(void *arg)
{

        xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID1);

        return (FILTER_HANDLED);
}

static int
xlnx_pcib_msi1_intr(void *arg)
{

        xlnx_pcib_handle_msi_intr(arg, XLNX_PCIE_RPMSIID2);

        return (FILTER_HANDLED);
}

static int
xlnx_pcib_register_msi(struct xlnx_pcib_softc *sc)
{
        const char *name;
        int error;
        int irq;

        sc->isrcs = malloc(sizeof(*sc->isrcs) * XLNX_PCIB_MAX_MSI, M_DEVBUF,
            M_WAITOK | M_ZERO);

        name = device_get_nameunit(sc->dev);

        for (irq = 0; irq < XLNX_PCIB_MAX_MSI; irq++) {
                sc->isrcs[irq].irq = irq;
                error = intr_isrc_register(&sc->isrcs[irq].isrc,
                    sc->dev, 0, "%s,%u", name, irq);
                if (error != 0)
                        return (error); /* XXX deregister ISRCs */
        }

        if (intr_msi_register(sc->dev,
            OF_xref_from_node(ofw_bus_get_node(sc->dev))) != 0)
                return (ENXIO);

        return (0);
}

static void
xlnx_pcib_init(struct xlnx_pcib_softc *sc)
{
        bus_addr_t addr;
        int reg;

        /* Disable interrupts. */
        bus_write_4(sc->res[0], XLNX_PCIE_IMR, 0);

        /* Clear pending interrupts.*/
        reg = bus_read_4(sc->res[0], XLNX_PCIE_IDR);
        bus_write_4(sc->res[0], XLNX_PCIE_IDR, reg);

        /* Setup an MSI page. */
        sc->msi_page = kmem_alloc_contig(PAGE_SIZE, M_WAITOK, 0,
            BUS_SPACE_MAXADDR, PAGE_SIZE, 0, VM_MEMATTR_DEFAULT);
        addr = vtophys(sc->msi_page);
        bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR1, (addr >> 32));
        bus_write_4(sc->res[0], XLNX_PCIE_RPMSIBR2, (addr >>  0));

        /* Enable the bridge. */
        reg = bus_read_4(sc->res[0], XLNX_PCIE_RPSCR);
        reg |= RPSCR_BE;
        bus_write_4(sc->res[0], XLNX_PCIE_RPSCR, reg);

        /* Enable interrupts. */
        reg = IMR_LINK_DOWN
                | IMR_HOT_RESET
                | IMR_CFG_COMPL_STATUS_M
                | IMR_CFG_TIMEOUT
                | IMR_CORRECTABLE
                | IMR_NON_FATAL
                | IMR_FATAL
                | IMR_INTX
                | IMR_MSI
                | IMR_SLAVE_UNSUPP_REQ
                | IMR_SLAVE_UNEXP_COMPL
                | IMR_SLAVE_COMPL_TIMOUT
                | IMR_SLAVE_ERROR_POISON
                | IMR_SLAVE_COMPL_ABORT
                | IMR_SLAVE_ILLEG_BURST
                | IMR_MASTER_DECERR
                | IMR_MASTER_SLVERR;
        bus_write_4(sc->res[0], XLNX_PCIE_IMR, reg);
}

static int
xlnx_pcib_fdt_probe(device_t dev)
{

        if (!ofw_bus_status_okay(dev))
                return (ENXIO);

        if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
                return (ENXIO);

        device_set_desc(dev, "Xilinx XDMA PCIe Controller");

        return (BUS_PROBE_DEFAULT);
}

static int
xlnx_pcib_fdt_attach(device_t dev)
{
        struct xlnx_pcib_softc *sc;
        int error;

        sc = device_get_softc(dev);
        sc->dev = dev;

        mtx_init(&sc->mtx, "msi_mtx", NULL, MTX_DEF);

        if (bus_alloc_resources(dev, xlnx_pcib_spec, sc->res)) {
                device_printf(dev, "could not allocate resources\n");
                return (ENXIO);
        }

        /* Setup MISC interrupt handler. */
        error = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
            xlnx_pcib_intr, NULL, sc, &sc->intr_cookie[0]);
        if (error != 0) {
                device_printf(dev, "could not setup interrupt handler.\n");
                return (ENXIO);
        }

        /* Setup MSI0 interrupt handler. */
        error = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
            xlnx_pcib_msi0_intr, NULL, sc, &sc->intr_cookie[1]);
        if (error != 0) {
                device_printf(dev, "could not setup interrupt handler.\n");
                return (ENXIO);
        }

        /* Setup MSI1 interrupt handler. */
        error = bus_setup_intr(dev, sc->res[3], INTR_TYPE_MISC | INTR_MPSAFE,
            xlnx_pcib_msi1_intr, NULL, sc, &sc->intr_cookie[2]);
        if (error != 0) {
                device_printf(dev, "could not setup interrupt handler.\n");
                return (ENXIO);
        }

        xlnx_pcib_init(sc);

        /*
         * Allow the core driver to map registers.
         * We will be accessing the device memory using core_softc.
         */
        bus_release_resources(dev, xlnx_pcib_spec, sc->res);

        error = xlnx_pcib_register_msi(sc);
        if (error)
                return (error);

        return (pci_host_generic_fdt_attach(dev));
}

static int
xlnx_pcib_fdt_get_id(device_t pci, device_t child, enum pci_id_type type,
    uintptr_t *id)
{
        phandle_t node;
        int bsf;

        if (type != PCI_ID_MSI)
                return (pcib_get_id(pci, child, type, id));

        node = ofw_bus_get_node(pci);
        if (OF_hasprop(node, "msi-map"))
                return (generic_pcie_get_id(pci, child, type, id));

        bsf = pci_get_rid(child);
        *id = (pci_get_domain(child) << PCI_RID_DOMAIN_SHIFT) | bsf;

        return (0);
}

static int
xlnx_pcib_req_valid(struct generic_pcie_core_softc *sc,
    u_int bus, u_int slot, u_int func, u_int reg)
{
        bus_space_handle_t h;
        bus_space_tag_t t;
        uint32_t val;

        t = rman_get_bustag(sc->res);
        h = rman_get_bushandle(sc->res);

        if ((bus < sc->bus_start) || (bus > sc->bus_end))
                return (0);
        if ((slot > PCI_SLOTMAX) || (func > PCI_FUNCMAX) ||
            (reg > PCIE_REGMAX))
                return (0);

        if (bus == 0 && slot > 0)
                return (0);

        val = bus_space_read_4(t, h, XLNX_PCIE_PHYSCR);
        if ((val & PHYSCR_LINK_UP) == 0) {
                /* Link is down */
                return (0);
        }

        /* Valid */

        return (1);
}

static uint32_t
xlnx_pcib_read_config(device_t dev, u_int bus, u_int slot,
    u_int func, u_int reg, int bytes)
{
        struct generic_pcie_fdt_softc *fdt_sc;
        struct xlnx_pcib_softc *xlnx_sc;
        struct generic_pcie_core_softc *sc;
        bus_space_handle_t h;
        bus_space_tag_t t;
        uint64_t offset;
        uint32_t data;

        xlnx_sc = device_get_softc(dev);
        fdt_sc = &xlnx_sc->fdt_sc;
        sc = &fdt_sc->base;

        if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg))
                return (~0U);

        offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg);
        t = rman_get_bustag(sc->res);
        h = rman_get_bushandle(sc->res);

        data = bus_space_read_4(t, h, offset & ~3);

        switch (bytes) {
        case 1:
                data >>= (offset & 3) * 8;
                data &= 0xff;
                break;
        case 2:
                data >>= (offset & 3) * 8;
                data = le16toh(data);
                break;
        case 4:
                data = le32toh(data);
                break;
        default:
                return (~0U);
        }

        return (data);
}

static void
xlnx_pcib_write_config(device_t dev, u_int bus, u_int slot,
    u_int func, u_int reg, uint32_t val, int bytes)
{
        struct generic_pcie_fdt_softc *fdt_sc;
        struct xlnx_pcib_softc *xlnx_sc;
        struct generic_pcie_core_softc *sc;
        bus_space_handle_t h;
        bus_space_tag_t t;
        uint64_t offset;
        uint32_t data;

        xlnx_sc = device_get_softc(dev);
        fdt_sc = &xlnx_sc->fdt_sc;
        sc = &fdt_sc->base;

        if (!xlnx_pcib_req_valid(sc, bus, slot, func, reg))
                return;

        offset = PCIE_ADDR_OFFSET(bus - sc->bus_start, slot, func, reg);

        t = rman_get_bustag(sc->res);
        h = rman_get_bushandle(sc->res);

        /*
         * 32-bit access used due to a bug in the Xilinx bridge that
         * requires to write primary and secondary buses in one blast.
         *
         * TODO: This is probably wrong on big-endian.
         */
        switch (bytes) {
        case 1:
                data = bus_space_read_4(t, h, offset & ~3);
                data &= ~(0xff << ((offset & 3) * 8));
                data |= (val & 0xff) << ((offset & 3) * 8);
                bus_space_write_4(t, h, offset & ~3, htole32(data));
                break;
        case 2:
                data = bus_space_read_4(t, h, offset & ~3);
                data &= ~(0xffff << ((offset & 3) * 8));
                data |= (val & 0xffff) << ((offset & 3) * 8);
                bus_space_write_4(t, h, offset & ~3, htole32(data));
                break;
        case 4:
                bus_space_write_4(t, h, offset, htole32(val));
                break;
        default:
                return;
        }
}

static int
xlnx_pcib_alloc_msi(device_t pci, device_t child, int count, int maxcount,
    int *irqs)
{
        phandle_t msi_parent;

        ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
            NULL);
        msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
        return (intr_alloc_msi(pci, child, msi_parent, count, maxcount,
            irqs));
}

static int
xlnx_pcib_release_msi(device_t pci, device_t child, int count, int *irqs)
{
        phandle_t msi_parent;

        ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
            NULL);
        msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
        return (intr_release_msi(pci, child, msi_parent, count, irqs));
}

static int
xlnx_pcib_map_msi(device_t pci, device_t child, int irq, uint64_t *addr,
    uint32_t *data)
{
        phandle_t msi_parent;

        ofw_bus_msimap(ofw_bus_get_node(pci), pci_get_rid(child), &msi_parent,
            NULL);
        msi_parent = OF_xref_from_node(ofw_bus_get_node(pci));
        return (intr_map_msi(pci, child, msi_parent, irq, addr, data));
}

static int
xlnx_pcib_msi_alloc_msi(device_t dev, device_t child, int count, int maxcount,
    device_t *pic, struct intr_irqsrc **srcs)
{
        struct xlnx_pcib_softc *sc;
        int irq, end_irq, i;
        bool found;

        sc = device_get_softc(dev);

        mtx_lock(&sc->mtx);

        found = false;

        for (irq = 0; (irq + count - 1) < XLNX_PCIB_MAX_MSI; irq++) {
                /* Assume the range is valid. */
                found = true;

                /* Check this range is valid. */
                for (end_irq = irq; end_irq < irq + count; end_irq++) {
                        if (sc->isrcs[end_irq].flags & XLNX_IRQ_FLAG_USED) {
                                /* This is already used. */
                                found = false;
                                break;
                        }
                }

                if (found)
                        break;
        }

        if (!found || irq == (XLNX_PCIB_MAX_MSI - 1)) {
                /* Not enough interrupts were found. */
                mtx_unlock(&sc->mtx);
                return (ENXIO);
        }

        /* Mark the interrupt as used. */
        for (i = 0; i < count; i++)
                sc->isrcs[irq + i].flags |= XLNX_IRQ_FLAG_USED;

        mtx_unlock(&sc->mtx);

        for (i = 0; i < count; i++)
                srcs[i] = (struct intr_irqsrc *)&sc->isrcs[irq + i];

        *pic = device_get_parent(dev);

        return (0);
}

static int
xlnx_pcib_msi_release_msi(device_t dev, device_t child, int count,
    struct intr_irqsrc **isrc)
{
        struct xlnx_pcib_softc *sc;
        struct xlnx_pcib_irqsrc *xi;
        int i;

        sc = device_get_softc(dev);
        mtx_lock(&sc->mtx);
        for (i = 0; i < count; i++) {
                xi = (struct xlnx_pcib_irqsrc *)isrc[i];

                KASSERT(xi->flags & XLNX_IRQ_FLAG_USED,
                    ("%s: Releasing an unused MSI interrupt", __func__));

                xi->flags &= ~XLNX_IRQ_FLAG_USED;
        }

        mtx_unlock(&sc->mtx);
        return (0);
}

static int
xlnx_pcib_msi_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc,
    uint64_t *addr, uint32_t *data)
{
        struct xlnx_pcib_softc *sc;
        struct xlnx_pcib_irqsrc *xi;

        sc = device_get_softc(dev);
        xi = (struct xlnx_pcib_irqsrc *)isrc;

        *addr = vtophys(sc->msi_page);
        *data = xi->irq;

        return (0);
}

static void
xlnx_pcib_msi_mask(device_t dev, struct intr_irqsrc *isrc, bool mask)
{
        struct generic_pcie_fdt_softc *fdt_sc;
        struct generic_pcie_core_softc *sc;
        struct xlnx_pcib_softc *xlnx_sc;
        struct xlnx_pcib_irqsrc *xi;
        uint32_t msireg, irq;
        uint32_t reg;

        xlnx_sc = device_get_softc(dev);
        fdt_sc = &xlnx_sc->fdt_sc;
        sc = &fdt_sc->base;

        xi = (struct xlnx_pcib_irqsrc *)isrc;

        irq = xi->irq;
        if (irq < 32)
                msireg = XLNX_PCIE_RPMSIID1_MASK;
        else
                msireg = XLNX_PCIE_RPMSIID2_MASK;

        reg = bus_read_4(sc->res, msireg);
        if (mask)
                reg &= ~(1 << irq);
        else
                reg |= (1 << irq);
        bus_write_4(sc->res, msireg, reg);
}

static void
xlnx_pcib_msi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
{

        xlnx_pcib_msi_mask(dev, isrc, true);
}

static void
xlnx_pcib_msi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
{

        xlnx_pcib_msi_mask(dev, isrc, false);
}

static void
xlnx_pcib_msi_post_filter(device_t dev, struct intr_irqsrc *isrc)
{

}

static void
xlnx_pcib_msi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
{

        xlnx_pcib_msi_mask(dev, isrc, false);
}

static void
xlnx_pcib_msi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
{

        xlnx_pcib_msi_mask(dev, isrc, true);
}

static int
xlnx_pcib_msi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
    struct resource *res, struct intr_map_data *data)
{

        return (0);
}

static int
xlnx_pcib_msi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
    struct resource *res, struct intr_map_data *data)
{

        return (0);
}

static device_method_t xlnx_pcib_fdt_methods[] = {
        /* Device interface */
        DEVMETHOD(device_probe,         xlnx_pcib_fdt_probe),
        DEVMETHOD(device_attach,        xlnx_pcib_fdt_attach),

        /* pcib interface */
        DEVMETHOD(pcib_get_id,          xlnx_pcib_fdt_get_id),
        DEVMETHOD(pcib_read_config,     xlnx_pcib_read_config),
        DEVMETHOD(pcib_write_config,    xlnx_pcib_write_config),
        DEVMETHOD(pcib_alloc_msi,       xlnx_pcib_alloc_msi),
        DEVMETHOD(pcib_release_msi,     xlnx_pcib_release_msi),
        DEVMETHOD(pcib_map_msi,         xlnx_pcib_map_msi),

        /* MSI interface */
        DEVMETHOD(msi_alloc_msi,                xlnx_pcib_msi_alloc_msi),
        DEVMETHOD(msi_release_msi,              xlnx_pcib_msi_release_msi),
        DEVMETHOD(msi_map_msi,                  xlnx_pcib_msi_map_msi),

        /* Interrupt controller interface */
        DEVMETHOD(pic_disable_intr,             xlnx_pcib_msi_disable_intr),
        DEVMETHOD(pic_enable_intr,              xlnx_pcib_msi_enable_intr),
        DEVMETHOD(pic_setup_intr,               xlnx_pcib_msi_setup_intr),
        DEVMETHOD(pic_teardown_intr,            xlnx_pcib_msi_teardown_intr),
        DEVMETHOD(pic_post_filter,              xlnx_pcib_msi_post_filter),
        DEVMETHOD(pic_post_ithread,             xlnx_pcib_msi_post_ithread),
        DEVMETHOD(pic_pre_ithread,              xlnx_pcib_msi_pre_ithread),

        /* End */
        DEVMETHOD_END
};

DEFINE_CLASS_1(pcib, xlnx_pcib_fdt_driver, xlnx_pcib_fdt_methods,
    sizeof(struct xlnx_pcib_softc), generic_pcie_fdt_driver);

DRIVER_MODULE(xlnx_pcib, simplebus, xlnx_pcib_fdt_driver, 0, 0);
DRIVER_MODULE(xlnx_pcib, ofwbus, xlnx_pcib_fdt_driver, 0, 0);