#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <uvm/uvm_extern.h>
#include <machine/intr.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_misc.h>
#include <dev/ofw/fdt.h>
struct mvgicp_softc {
struct device sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
paddr_t sc_addr;
uint32_t sc_spi_ranges[4];
void **sc_spi;
uint32_t sc_nspi;
struct interrupt_controller sc_ic;
struct interrupt_controller *sc_parent_ic;
};
int mvgicp_match(struct device *, void *, void *);
void mvgicp_attach(struct device *, struct device *, void *);
void * mvgicp_intr_establish(void *, uint64_t *, uint64_t *,
int, struct cpu_info *, int (*)(void *), void *, char *);
void mvgicp_intr_disestablish(void *);
void mvgicp_intr_barrier(void *);
const struct cfattach mvgicp_ca = {
sizeof(struct mvgicp_softc), mvgicp_match, mvgicp_attach
};
struct cfdriver mvgicp_cd = {
NULL, "mvgicp", DV_DULL
};
int
mvgicp_match(struct device *parent, void *match, void *aux)
{
struct fdt_attach_args *faa = aux;
return OF_is_compatible(faa->fa_node, "marvell,ap806-gicp");
}
void
mvgicp_attach(struct device *parent, struct device *self, void *aux)
{
struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
struct fdt_attach_args *faa = aux;
struct interrupt_controller *ic;
int node;
if (faa->fa_nreg < 1) {
printf(": no registers\n");
return;
}
OF_getpropintarray(faa->fa_node, "marvell,spi-ranges",
sc->sc_spi_ranges, sizeof(sc->sc_spi_ranges));
sc->sc_nspi = sc->sc_spi_ranges[1] + sc->sc_spi_ranges[3];
sc->sc_spi = mallocarray(sc->sc_nspi, sizeof(void *),
M_DEVBUF, M_WAITOK | M_ZERO);
sc->sc_iot = faa->fa_iot;
if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
printf(": can't map registers\n");
return;
}
if (!pmap_extract(pmap_kernel(), sc->sc_ioh, &sc->sc_addr)) {
printf(": cannot retrieve msi addr\n");
return;
}
extern int fdt_intr_get_parent(int);
node = fdt_intr_get_parent(faa->fa_node);
extern LIST_HEAD(, interrupt_controller) interrupt_controllers;
LIST_FOREACH(ic, &interrupt_controllers, ic_list) {
if (ic->ic_node == node)
break;
}
sc->sc_parent_ic = ic;
sc->sc_ic.ic_node = faa->fa_node;
sc->sc_ic.ic_cookie = sc;
sc->sc_ic.ic_establish_msi = mvgicp_intr_establish;
sc->sc_ic.ic_disestablish = mvgicp_intr_disestablish;
sc->sc_ic.ic_barrier = mvgicp_intr_barrier;
fdt_intr_register(&sc->sc_ic);
printf("\n");
}
void *
mvgicp_intr_establish(void *self, uint64_t *addr, uint64_t *data,
int level, struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
{
struct mvgicp_softc *sc = (struct mvgicp_softc *)self;
struct interrupt_controller *ic = sc->sc_parent_ic;
struct machine_intr_handle *ih;
uint32_t interrupt[3];
uint32_t flags;
void *cookie;
int i, spi;
if (ic == NULL)
return NULL;
for (i = 0; i < sc->sc_nspi; i++) {
if (sc->sc_spi[i] == NULL) {
spi = i;
break;
}
}
if (i == sc->sc_nspi)
return NULL;
flags = *data;
*addr = sc->sc_addr;
*data = spi;
for (i = 0; i < nitems(sc->sc_spi_ranges); i += 2) {
if (spi < sc->sc_spi_ranges[i + 1]) {
spi += sc->sc_spi_ranges[i];
break;
}
spi -= sc->sc_spi_ranges[i + 1];
}
if (i == nitems(sc->sc_spi_ranges))
return NULL;
interrupt[0] = 0;
interrupt[1] = spi - 32;
interrupt[2] = flags;
cookie = ic->ic_establish(ic->ic_cookie, interrupt, level,
ci, func, arg, name);
if (cookie == NULL)
return NULL;
ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK);
ih->ih_ic = ic;
ih->ih_ih = cookie;
sc->sc_spi[*data] = ih;
return &sc->sc_spi[*data];
}
void
mvgicp_intr_disestablish(void *cookie)
{
struct machine_intr_handle *ih = *(void **)cookie;
struct interrupt_controller *ic = ih->ih_ic;
ic->ic_disestablish(ih->ih_ih);
free(ih, M_DEVBUF, sizeof(*ih));
*(void **)cookie = NULL;
}
void
mvgicp_intr_barrier(void *cookie)
{
struct machine_intr_handle *ih = *(void **)cookie;
struct interrupt_controller *ic = ih->ih_ic;
ic->ic_barrier(ih->ih_ih);
}