Changeset View
Standalone View
sys/arm/arm/gic.c
Show First 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | |||||
#include <sys/proc.h> | #include <sys/proc.h> | ||||
#include <sys/cpuset.h> | #include <sys/cpuset.h> | ||||
#include <sys/lock.h> | #include <sys/lock.h> | ||||
#include <sys/mutex.h> | #include <sys/mutex.h> | ||||
#include <sys/smp.h> | #include <sys/smp.h> | ||||
#ifdef INTRNG | #ifdef INTRNG | ||||
#include <sys/sched.h> | #include <sys/sched.h> | ||||
#endif | #endif | ||||
#include <vm/vm.h> | |||||
#include <vm/pmap.h> | |||||
#include <machine/bus.h> | #include <machine/bus.h> | ||||
#include <machine/intr.h> | #include <machine/intr.h> | ||||
#include <machine/smp.h> | #include <machine/smp.h> | ||||
#include <dev/fdt/fdt_common.h> | #include <dev/fdt/fdt_common.h> | ||||
#include <dev/ofw/openfirm.h> | #include <dev/ofw/openfirm.h> | ||||
#include <dev/ofw/ofw_bus.h> | #include <dev/ofw/ofw_bus.h> | ||||
#include <dev/ofw/ofw_bus_subr.h> | #include <dev/ofw/ofw_bus_subr.h> | ||||
#ifdef INTRNG | #ifdef INTRNG | ||||
#include "pic_if.h" | #include "pic_if.h" | ||||
#include "msi_if.h" | |||||
#endif | #endif | ||||
#define GIC_DEBUG_SPURIOUS | #define GIC_DEBUG_SPURIOUS | ||||
/* We are using GICv2 register naming */ | /* We are using GICv2 register naming */ | ||||
/* Distributor Registers */ | /* Distributor Registers */ | ||||
#define GICD_CTLR 0x000 /* v1 ICDDCR */ | #define GICD_CTLR 0x000 /* v1 ICDDCR */ | ||||
▲ Show 20 Lines • Show All 43 Lines • ▼ Show 20 Lines | |||||
#ifdef INTRNG | #ifdef INTRNG | ||||
struct gic_irqsrc { | struct gic_irqsrc { | ||||
struct intr_irqsrc gi_isrc; | struct intr_irqsrc gi_isrc; | ||||
uint32_t gi_irq; | uint32_t gi_irq; | ||||
enum intr_polarity gi_pol; | enum intr_polarity gi_pol; | ||||
enum intr_trigger gi_trig; | enum intr_trigger gi_trig; | ||||
#define GI_FLAG_EARLY_EOI (1 << 0) | #define GI_FLAG_EARLY_EOI (1 << 0) | ||||
#define GI_FLAG_MSI (1 << 1) /* This interrupt source should only */ | |||||
/* be used for MSI/MSI-X interrupts */ | |||||
emaste: Could these two have a comment to explain the relationship? | |||||
#define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */ | |||||
/* for a MSI/MSI-X interrupt */ | |||||
u_int gi_flags; | u_int gi_flags; | ||||
}; | }; | ||||
static u_int gic_irq_cpu; | static u_int gic_irq_cpu; | ||||
static int arm_gic_intr(void *); | static int arm_gic_intr(void *); | ||||
static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc); | static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc); | ||||
#ifdef SMP | #ifdef SMP | ||||
▲ Show 20 Lines • Show All 423 Lines • ▼ Show 20 Lines | if (cdev == NULL) { | ||||
free(dinfo, M_DEVBUF); | free(dinfo, M_DEVBUF); | ||||
continue; | continue; | ||||
} | } | ||||
device_set_ivars(cdev, dinfo); | device_set_ivars(cdev, dinfo); | ||||
} | } | ||||
return (true); | return (true); | ||||
} | } | ||||
static void | |||||
arm_gic_reserve_msi_range(device_t dev, u_int start, u_int count) | |||||
{ | |||||
struct arm_gic_softc *sc; | |||||
int i; | |||||
sc = device_get_softc(dev); | |||||
KASSERT((start + count) < sc->nirqs, | |||||
("%s: Trying to allocate too many MSI IRQs: %d + %d > %d", __func__, | |||||
start, count, sc->nirqs)); | |||||
for (i = 0; i < count; i++) { | |||||
KASSERT(sc->gic_irqs[start + i].gi_pol == INTR_POLARITY_CONFORM, | |||||
("%s: MSI interrupt %d already has a polarity", __func__, | |||||
count + i)); | |||||
KASSERT(sc->gic_irqs[start + i].gi_trig == INTR_TRIGGER_CONFORM, | |||||
("%s: MSI interrupt %d already has a trigger", __func__, | |||||
count + i)); | |||||
Not Done Inline ActionsWhat is the reason for these two KASSERTs? skra: What is the reason for these two KASSERTs? | |||||
Not Done Inline ActionsWell, I have a reason for the question. For example, testing isrc_handlers could be better if you are checking that the interrupt is not used by anyone. skra: Well, I have a reason for the question. For example, testing isrc_handlers could be better if… | |||||
sc->gic_irqs[start + i].gi_pol = INTR_POLARITY_HIGH; | |||||
Not Done Inline ActionsAll MSI and MSI-X interrupts use INTR_TRIGGER_EDGE and INTR_POLARITY_HIGH in gic. IMO, they may be set only once here and never cleared. There is already GI_FLAG_MSI flag in ISRC, so it should be save. skra: All MSI and MSI-X interrupts use INTR_TRIGGER_EDGE and INTR_POLARITY_HIGH in gic. IMO, they may… | |||||
sc->gic_irqs[start + i].gi_trig = INTR_TRIGGER_EDGE; | |||||
sc->gic_irqs[start + i].gi_flags |= GI_FLAG_MSI; | |||||
} | |||||
} | |||||
#endif | #endif | ||||
static int | static int | ||||
arm_gic_attach(device_t dev) | arm_gic_attach(device_t dev) | ||||
{ | { | ||||
struct arm_gic_softc *sc; | struct arm_gic_softc *sc; | ||||
int i; | int i; | ||||
uint32_t icciidr, mask, nirqs; | uint32_t icciidr, mask, nirqs; | ||||
▲ Show 20 Lines • Show All 436 Lines • ▼ Show 20 Lines | gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, | ||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
switch (data->type) { | switch (data->type) { | ||||
#ifdef FDT | #ifdef FDT | ||||
case INTR_MAP_DATA_FDT: | case INTR_MAP_DATA_FDT: | ||||
if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, | if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, | ||||
&pol, &trig) != 0) | &pol, &trig) != 0) | ||||
return (EINVAL); | return (EINVAL); | ||||
KASSERT(irq >= sc->nirqs || | |||||
(sc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) == 0, | |||||
("%s: Attempting to map a MSI interrupt from FDT", | |||||
__func__)); | |||||
break; | break; | ||||
#endif | #endif | ||||
default: | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | } | ||||
if (irq >= sc->nirqs) | if (irq >= sc->nirqs) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW && | if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW && | ||||
Not Done Inline ActionsRelated to note in intr_internal_map_irq(). skra: Related to note in intr_internal_map_irq().
| |||||
pol != INTR_POLARITY_HIGH) | pol != INTR_POLARITY_HIGH) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE && | if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE && | ||||
trig != INTR_TRIGGER_LEVEL) | trig != INTR_TRIGGER_LEVEL) | ||||
return (EINVAL); | return (EINVAL); | ||||
*irqp = irq; | *irqp = irq; | ||||
if (polp != NULL) | if (polp != NULL) | ||||
Show All 23 Lines | |||||
arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc, | arm_gic_setup_intr(device_t dev, struct intr_irqsrc *isrc, | ||||
struct resource *res, struct intr_map_data *data) | struct resource *res, struct intr_map_data *data) | ||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
u_int irq; | u_int irq; | ||||
enum intr_trigger trig; | enum intr_trigger trig; | ||||
enum intr_polarity pol; | enum intr_polarity pol; | ||||
if (data == NULL) | if ((gi->gi_flags & GI_FLAG_MSI) == GI_FLAG_MSI) { | ||||
Not Done Inline ActionsHmm, this is my bad. The idea is that if an interrupt does not need to be configured explicitly, the data may be NULL. Thus, all methods which takes struct intr_map_data as an argument should be changed to deal with it now, when it may be a case. It's related to note in arm_gic_reserve_msi_range(). skra: Hmm, this is my bad. The idea is that if an interrupt does not need to be configured explicitly… | |||||
return (ENOTSUP); | irq = gi->gi_irq; | ||||
pol = gi->gi_pol; | |||||
trig = gi->gi_trig; | |||||
KASSERT(pol == INTR_POLARITY_HIGH, | |||||
("%s: MSI interrupts must be active-high", __func__)); | |||||
KASSERT(trig == INTR_TRIGGER_EDGE, | |||||
("%s: MSI interrupts must be edge triggered", __func__)); | |||||
} else if (data != NULL) { | |||||
/* Get config for resource. */ | /* Get config for resource. */ | ||||
if (gic_map_intr(dev, data, &irq, &pol, &trig)) | if (gic_map_intr(dev, data, &irq, &pol, &trig)) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (gi->gi_irq != irq) | if (gi->gi_irq != irq) | ||||
return (EINVAL); | return (EINVAL); | ||||
} else | |||||
return (ENOTSUP); | |||||
/* Compare config if this is not first setup. */ | /* Compare config if this is not first setup. */ | ||||
if (isrc->isrc_handlers != 0) { | if (isrc->isrc_handlers != 0) { | ||||
if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) || | if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) || | ||||
(trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig)) | (trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig)) | ||||
return (EINVAL); | return (EINVAL); | ||||
else | else | ||||
return (0); | return (0); | ||||
} | } | ||||
/* For MSI/MSI-X we should have already configured these */ | |||||
if ((gi->gi_flags & GI_FLAG_MSI) == 0) { | |||||
if (pol == INTR_POLARITY_CONFORM) | if (pol == INTR_POLARITY_CONFORM) | ||||
pol = INTR_POLARITY_LOW; /* just pick some */ | pol = INTR_POLARITY_LOW; /* just pick some */ | ||||
if (trig == INTR_TRIGGER_CONFORM) | if (trig == INTR_TRIGGER_CONFORM) | ||||
trig = INTR_TRIGGER_EDGE; /* just pick some */ | trig = INTR_TRIGGER_EDGE; /* just pick some */ | ||||
gi->gi_pol = pol; | gi->gi_pol = pol; | ||||
gi->gi_trig = trig; | gi->gi_trig = trig; | ||||
/* Edge triggered interrupts need an early EOI sent */ | /* Edge triggered interrupts need an early EOI sent */ | ||||
if (gi->gi_pol == INTR_TRIGGER_EDGE) | if (gi->gi_pol == INTR_TRIGGER_EDGE) | ||||
gi->gi_flags |= GI_FLAG_EARLY_EOI; | gi->gi_flags |= GI_FLAG_EARLY_EOI; | ||||
} | |||||
/* | /* | ||||
* XXX - In case that per CPU interrupt is going to be enabled in time | * XXX - In case that per CPU interrupt is going to be enabled in time | ||||
* when SMP is already started, we need some IPI call which | * when SMP is already started, we need some IPI call which | ||||
* enables it on others CPUs. Further, it's more complicated as | * enables it on others CPUs. Further, it's more complicated as | ||||
* pic_enable_source() and pic_disable_source() should act on | * pic_enable_source() and pic_disable_source() should act on | ||||
Not Done Inline ActionsRelated to note in arm_gic_reserve_msi_range(). skra: Related to note in arm_gic_reserve_msi_range(). | |||||
* per CPU basis only. Thus, it should be solved here somehow. | * per CPU basis only. Thus, it should be solved here somehow. | ||||
*/ | */ | ||||
if (isrc->isrc_flags & INTR_ISRCF_PPI) | if (isrc->isrc_flags & INTR_ISRCF_PPI) | ||||
CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); | CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); | ||||
gic_config(sc, gi->gi_irq, trig, pol); | gic_config(sc, gi->gi_irq, gi->gi_trig, gi->gi_pol); | ||||
arm_gic_bind_intr(dev, isrc); | arm_gic_bind_intr(dev, isrc); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | static int | ||||
arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, | arm_gic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, | ||||
struct resource *res, struct intr_map_data *data) | struct resource *res, struct intr_map_data *data) | ||||
{ | { | ||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
if (isrc->isrc_handlers == 0) { | if (isrc->isrc_handlers == 0 && (gi->gi_flags & GI_FLAG_MSI) == 0) { | ||||
gi->gi_pol = INTR_POLARITY_CONFORM; | gi->gi_pol = INTR_POLARITY_CONFORM; | ||||
gi->gi_trig = INTR_TRIGGER_CONFORM; | gi->gi_trig = INTR_TRIGGER_CONFORM; | ||||
} | } | ||||
return (0); | return (0); | ||||
} | } | ||||
static void | static void | ||||
arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) | arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) | ||||
▲ Show 20 Lines • Show All 367 Lines • ▼ Show 20 Lines | |||||
#define MSI_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff) | #define MSI_TYPER_SPI_COUNT(x) (((x) >> 0) & 0x3ff) | ||||
#define GICv2M_MSI_SETSPI_NS 0x040 | #define GICv2M_MSI_SETSPI_NS 0x040 | ||||
#define GICV2M_MSI_IIDR 0xFCC | #define GICV2M_MSI_IIDR 0xFCC | ||||
struct arm_gicv2m_softc { | struct arm_gicv2m_softc { | ||||
struct resource *sc_mem; | struct resource *sc_mem; | ||||
struct mtx sc_mutex; | struct mtx sc_mutex; | ||||
u_int sc_spi_start; | u_int sc_spi_start; | ||||
u_int sc_spi_end; | |||||
u_int sc_spi_count; | u_int sc_spi_count; | ||||
u_int sc_spi_offset; | |||||
}; | }; | ||||
static struct ofw_compat_data gicv2m_compat_data[] = { | static struct ofw_compat_data gicv2m_compat_data[] = { | ||||
{"arm,gic-v2m-frame", true}, | {"arm,gic-v2m-frame", true}, | ||||
{NULL, false} | {NULL, false} | ||||
}; | }; | ||||
static int | static int | ||||
Show All 9 Lines | arm_gicv2m_probe(device_t dev) | ||||
device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX"); | device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX"); | ||||
return (BUS_PROBE_DEFAULT); | return (BUS_PROBE_DEFAULT); | ||||
} | } | ||||
static int | static int | ||||
arm_gicv2m_attach(device_t dev) | arm_gicv2m_attach(device_t dev) | ||||
{ | { | ||||
struct arm_gicv2m_softc *sc; | struct arm_gicv2m_softc *sc; | ||||
struct arm_gic_softc *psc; | |||||
uint32_t typer; | uint32_t typer; | ||||
int rid; | int rid; | ||||
psc = device_get_softc(device_get_parent(dev)); | |||||
sc = device_get_softc(dev); | sc = device_get_softc(dev); | ||||
rid = 0; | rid = 0; | ||||
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, | sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, | ||||
RF_ACTIVE); | RF_ACTIVE); | ||||
if (sc->sc_mem == NULL) { | if (sc->sc_mem == NULL) { | ||||
device_printf(dev, "Unable to allocate resources\n"); | device_printf(dev, "Unable to allocate resources\n"); | ||||
return (ENXIO); | return (ENXIO); | ||||
} | } | ||||
typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER); | typer = bus_read_4(sc->sc_mem, GICV2M_MSI_TYPER); | ||||
sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer); | sc->sc_spi_start = MSI_TYPER_SPI_BASE(typer); | ||||
sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer); | sc->sc_spi_count = MSI_TYPER_SPI_COUNT(typer); | ||||
sc->sc_spi_end = sc->sc_spi_start + sc->sc_spi_count; | |||||
/* Reserve these interrupts for MSI/MSI-X use */ | |||||
arm_gic_reserve_msi_range(device_get_parent(dev), sc->sc_spi_start, | |||||
sc->sc_spi_count); | |||||
mtx_init(&sc->sc_mutex, "GICv2m lock", "", MTX_DEF); | mtx_init(&sc->sc_mutex, "GICv2m lock", "", MTX_DEF); | ||||
intr_msi_register(dev, gic_xref(dev)); | |||||
if (bootverbose) | if (bootverbose) | ||||
device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start, | device_printf(dev, "using spi %u to %u\n", sc->sc_spi_start, | ||||
sc->sc_spi_start + sc->sc_spi_count - 1); | sc->sc_spi_start + sc->sc_spi_count - 1); | ||||
return (0); | return (0); | ||||
} | } | ||||
static int | |||||
arm_gicv2m_alloc_msi(device_t dev, device_t child, int count, int maxcount, | |||||
device_t *pic, struct intr_irqsrc **srcs) | |||||
{ | |||||
struct arm_gic_softc *psc; | |||||
struct arm_gicv2m_softc *sc; | |||||
int i, irq, end_irq; | |||||
bool found; | |||||
KASSERT(powerof2(count), ("%s: bad count", __func__)); | |||||
KASSERT(powerof2(maxcount), ("%s: bad maxcount", __func__)); | |||||
psc = device_get_softc(device_get_parent(dev)); | |||||
sc = device_get_softc(dev); | |||||
mtx_lock(&sc->sc_mutex); | |||||
found = false; | |||||
for (irq = sc->sc_spi_start; irq < sc->sc_spi_end && !found; irq++) { | |||||
/* Start on an aligned interrupt */ | |||||
if ((irq & (maxcount - 1)) != 0) | |||||
continue; | |||||
Not Done Inline Actions@jhb Is this correct to align the interrupt allocation? andrew: @jhb Is this correct to align the interrupt allocation? | |||||
Not Done Inline ActionsFor MSI (but not MSI-X), an allocation of multiple messages is required to have the 'data' value (the value written to the 'data' register in the MSI capability in PCI config space) aligned such that the low N bits are zero. The PCI function stores the specific message number in the low N bits to generate the 'data' value in the MSI message it sends out to the bus. If you are storing the IRQ value as the 'data' value (x86 stores the IDT vector in the low 8 bits for example), then, yes, the IRQ must be aligned. jhb: For MSI (but not MSI-X), an allocation of multiple messages is required to have the 'data'… | |||||
/* Assume we found a valid range until shown otherwise */ | |||||
found = true; | |||||
/* Check this range is valid */ | |||||
for (end_irq = irq; end_irq != irq + count - 1; end_irq++) { | |||||
/* No free interrupts */ | |||||
if (end_irq == sc->sc_spi_end) { | |||||
found = false; | |||||
break; | |||||
} | |||||
KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI)!= 0, | |||||
("%s: Non-MSI interrupt found", __func__)); | |||||
/* This is already used */ | |||||
if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == | |||||
GI_FLAG_MSI_USED) { | |||||
found = false; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
/* Not enough interrupts were found */ | |||||
if (!found || irq == sc->sc_spi_end) { | |||||
mtx_unlock(&sc->sc_mutex); | |||||
return (ENXIO); | |||||
} | |||||
for (i = 0; i < count; i++) { | |||||
/* Mark the interrupt as used */ | |||||
psc->gic_irqs[irq + i].gi_flags |= GI_FLAG_MSI_USED; | |||||
} | |||||
mtx_unlock(&sc->sc_mutex); | |||||
Not Done Inline ActionsRelated to note in arm_gic_reserve_msi_range(). skra: Related to note in arm_gic_reserve_msi_range(). | |||||
for (i = 0; i < count; i++) | |||||
srcs[i] = (struct intr_irqsrc *)&psc->gic_irqs[irq + i]; | |||||
*pic = device_get_parent(dev); | |||||
return (0); | |||||
} | |||||
static int | |||||
arm_gicv2m_release_msi(device_t dev, device_t child, int count, | |||||
struct intr_irqsrc **isrc) | |||||
{ | |||||
struct arm_gicv2m_softc *sc; | |||||
struct gic_irqsrc *gi; | |||||
int i; | |||||
sc = device_get_softc(dev); | |||||
mtx_lock(&sc->sc_mutex); | |||||
for (i = 0; i < count; i++) { | |||||
gi = (struct gic_irqsrc *)isrc; | |||||
KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED, | |||||
("%s: Trying to release an unused MSI-X interrupt", | |||||
__func__)); | |||||
gi->gi_flags &= ~GI_FLAG_MSI_USED; | |||||
mtx_unlock(&sc->sc_mutex); | |||||
} | |||||
return (0); | |||||
} | |||||
Not Done Inline ActionsRelated to note in arm_gic_reserve_msi_range(). skra: Related to note in arm_gic_reserve_msi_range(). | |||||
static int | |||||
arm_gicv2m_alloc_msix(device_t dev, device_t child, device_t *pic, | |||||
struct intr_irqsrc **isrcp) | |||||
{ | |||||
struct arm_gicv2m_softc *sc; | |||||
struct arm_gic_softc *psc; | |||||
int irq; | |||||
psc = device_get_softc(device_get_parent(dev)); | |||||
sc = device_get_softc(dev); | |||||
mtx_lock(&sc->sc_mutex); | |||||
/* Find an unused interrupt */ | |||||
for (irq = sc->sc_spi_start; irq < sc->sc_spi_end; irq++) { | |||||
KASSERT((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI) != 0, | |||||
("%s: Non-MSI interrupt found", __func__)); | |||||
if ((psc->gic_irqs[irq].gi_flags & GI_FLAG_MSI_USED) == 0) | |||||
break; | |||||
} | |||||
/* No free interrupt was found */ | |||||
if (irq == sc->sc_spi_end) { | |||||
mtx_unlock(&sc->sc_mutex); | |||||
return (ENXIO); | |||||
} | |||||
/* Mark the interrupt as used */ | |||||
psc->gic_irqs[irq].gi_flags |= GI_FLAG_MSI_USED; | |||||
mtx_unlock(&sc->sc_mutex); | |||||
*isrcp = (struct intr_irqsrc *)&psc->gic_irqs[irq]; | |||||
*pic = device_get_parent(dev); | |||||
return (0); | |||||
} | |||||
static int | |||||
Not Done Inline ActionsRelated to note in arm_gic_reserve_msi_range(). skra: Related to note in arm_gic_reserve_msi_range(). | |||||
arm_gicv2m_release_msix(device_t dev, device_t child, struct intr_irqsrc *isrc) | |||||
{ | |||||
struct arm_gicv2m_softc *sc; | |||||
struct gic_irqsrc *gi; | |||||
sc = device_get_softc(dev); | |||||
gi = (struct gic_irqsrc *)isrc; | |||||
KASSERT((gi->gi_flags & GI_FLAG_MSI_USED) == GI_FLAG_MSI_USED, | |||||
("%s: Trying to release an unused MSI-X interrupt", __func__)); | |||||
mtx_lock(&sc->sc_mutex); | |||||
gi->gi_flags &= ~GI_FLAG_MSI_USED; | |||||
mtx_unlock(&sc->sc_mutex); | |||||
return (0); | |||||
} | |||||
static int | |||||
arm_gicv2m_map_msi(device_t dev, device_t child, struct intr_irqsrc *isrc, | |||||
uint64_t *addr, uint32_t *data) | |||||
{ | |||||
struct arm_gicv2m_softc *sc = device_get_softc(dev); | |||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | |||||
Not Done Inline ActionsRelated to note in arm_gic_reserve_msi_range(). skra: Related to note in arm_gic_reserve_msi_range(). | |||||
*addr = vtophys(rman_get_virtual(sc->sc_mem)) + GICv2M_MSI_SETSPI_NS; | |||||
*data = gi->gi_irq; | |||||
return (0); | |||||
} | |||||
static device_method_t arm_gicv2m_methods[] = { | static device_method_t arm_gicv2m_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, arm_gicv2m_probe), | DEVMETHOD(device_probe, arm_gicv2m_probe), | ||||
DEVMETHOD(device_attach, arm_gicv2m_attach), | DEVMETHOD(device_attach, arm_gicv2m_attach), | ||||
/* MSI/MSI-X */ | |||||
DEVMETHOD(msi_alloc_msi, arm_gicv2m_alloc_msi), | |||||
DEVMETHOD(msi_release_msi, arm_gicv2m_release_msi), | |||||
DEVMETHOD(msi_alloc_msix, arm_gicv2m_alloc_msix), | |||||
DEVMETHOD(msi_release_msix, arm_gicv2m_release_msix), | |||||
DEVMETHOD(msi_map_msi, arm_gicv2m_map_msi), | |||||
/* End */ | /* End */ | ||||
DEVMETHOD_END | DEVMETHOD_END | ||||
}; | }; | ||||
DEFINE_CLASS_0(gicv2m, arm_gicv2m_driver, arm_gicv2m_methods, | DEFINE_CLASS_0(gicv2m, arm_gicv2m_driver, arm_gicv2m_methods, | ||||
sizeof(struct arm_gicv2m_softc)); | sizeof(struct arm_gicv2m_softc)); | ||||
static devclass_t arm_gicv2m_devclass; | static devclass_t arm_gicv2m_devclass; | ||||
EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_driver, | EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_driver, | ||||
arm_gicv2m_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); | arm_gicv2m_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); | ||||
#endif | #endif |
Could these two have a comment to explain the relationship?