Changeset View
Standalone View
sys/arm/arm/gic.c
Show All 30 Lines | |||||
* SUCH DAMAGE. | * SUCH DAMAGE. | ||||
*/ | */ | ||||
#include <sys/cdefs.h> | #include <sys/cdefs.h> | ||||
__FBSDID("$FreeBSD$"); | __FBSDID("$FreeBSD$"); | ||||
#include "opt_platform.h" | #include "opt_platform.h" | ||||
#include "opt_platform.h" | |||||
#include <sys/param.h> | #include <sys/param.h> | ||||
#include <sys/systm.h> | #include <sys/systm.h> | ||||
#include <sys/bus.h> | #include <sys/bus.h> | ||||
#include <sys/kernel.h> | #include <sys/kernel.h> | ||||
#include <sys/ktr.h> | #include <sys/ktr.h> | ||||
#include <sys/module.h> | #include <sys/module.h> | ||||
#include <sys/malloc.h> | #include <sys/malloc.h> | ||||
#include <sys/rman.h> | #include <sys/rman.h> | ||||
▲ Show 20 Lines • Show All 64 Lines • ▼ Show 20 Lines | |||||
#define GICD_ICFGR_TRIG_EDGE (1 << 1) | #define GICD_ICFGR_TRIG_EDGE (1 << 1) | ||||
#define GICD_ICFGR_TRIG_MASK 0x2 | #define GICD_ICFGR_TRIG_MASK 0x2 | ||||
#ifndef GIC_DEFAULT_ICFGR_INIT | #ifndef GIC_DEFAULT_ICFGR_INIT | ||||
#define GIC_DEFAULT_ICFGR_INIT 0x00000000 | #define GIC_DEFAULT_ICFGR_INIT 0x00000000 | ||||
#endif | #endif | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
struct gic_irqsrc { | |||||
struct intr_irqsrc gi_isrc; | |||||
uint32_t gi_irq; | |||||
enum intr_polarity gi_pol; | |||||
enum intr_trigger gi_trig; | |||||
}; | |||||
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(device_t dev, struct intr_irqsrc *isrc); | static int arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc); | ||||
#ifdef SMP | #ifdef SMP | ||||
u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; | u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; | ||||
#define ISRC_IPI(isrc) sgi_to_ipi[isrc->isrc_data - GIC_FIRST_SGI] | u_int sgi_first_unused = GIC_FIRST_SGI; | ||||
#endif | #endif | ||||
#endif | #endif | ||||
struct arm_gic_softc { | struct arm_gic_softc { | ||||
device_t gic_dev; | device_t gic_dev; | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
void * gic_intrhand; | void * gic_intrhand; | ||||
struct intr_irqsrc ** gic_irqs; | struct gic_irqsrc * gic_irqs; | ||||
#endif | #endif | ||||
struct resource * gic_res[3]; | struct resource * gic_res[3]; | ||||
bus_space_tag_t gic_c_bst; | bus_space_tag_t gic_c_bst; | ||||
bus_space_tag_t gic_d_bst; | bus_space_tag_t gic_d_bst; | ||||
bus_space_handle_t gic_c_bsh; | bus_space_handle_t gic_c_bsh; | ||||
bus_space_handle_t gic_d_bsh; | bus_space_handle_t gic_d_bsh; | ||||
uint8_t ver; | uint8_t ver; | ||||
struct mtx mutex; | struct mtx mutex; | ||||
uint32_t nirqs; | uint32_t nirqs; | ||||
#ifdef GIC_DEBUG_SPURIOUS | #ifdef GIC_DEBUG_SPURIOUS | ||||
uint32_t last_irq[MAXCPU]; | uint32_t last_irq[MAXCPU]; | ||||
#endif | #endif | ||||
}; | }; | ||||
#ifdef ARM_INTRNG | |||||
#define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc) | |||||
#endif | |||||
static struct resource_spec arm_gic_spec[] = { | static struct resource_spec arm_gic_spec[] = { | ||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ | { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* Distributor registers */ | ||||
{ SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ | { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* CPU Interrupt Intf. registers */ | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */ | { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */ | ||||
#endif | #endif | ||||
{ -1, 0 } | { -1, 0 } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 80 Lines • ▼ Show 20 Lines | arm_gic_init_secondary(device_t dev) | ||||
/* Set priority mask register. */ | /* Set priority mask register. */ | ||||
gic_c_write_4(sc, GICC_PMR, 0xff); | gic_c_write_4(sc, GICC_PMR, 0xff); | ||||
/* Enable interrupt distribution */ | /* Enable interrupt distribution */ | ||||
gic_d_write_4(sc, GICD_CTLR, 0x01); | gic_d_write_4(sc, GICD_CTLR, 0x01); | ||||
/* Unmask attached SGI interrupts. */ | /* Unmask attached SGI interrupts. */ | ||||
for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) { | for (irq = GIC_FIRST_SGI; irq <= GIC_LAST_SGI; irq++) { | ||||
isrc = sc->gic_irqs[irq]; | isrc = GIC_INTR_ISRC(sc, irq); | ||||
if (isrc != NULL && isrc->isrc_handlers != 0) { | if (isrc != NULL && isrc->isrc_handlers != 0) { | ||||
CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); | CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); | ||||
gic_irq_unmask(sc, irq); | gic_irq_unmask(sc, irq); | ||||
} | } | ||||
} | } | ||||
/* Unmask attached PPI interrupts. */ | /* Unmask attached PPI interrupts. */ | ||||
for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) { | for (irq = GIC_FIRST_PPI; irq <= GIC_LAST_PPI; irq++) { | ||||
isrc = sc->gic_irqs[irq]; | isrc = GIC_INTR_ISRC(sc, irq); | ||||
if (isrc == NULL || isrc->isrc_handlers == 0) | if (isrc == NULL || isrc->isrc_handlers == 0) | ||||
continue; | continue; | ||||
if (isrc->isrc_flags & INTR_ISRCF_BOUND) { | if (isrc->isrc_flags & INTR_ISRCF_BOUND) { | ||||
if (CPU_ISSET(PCPU_GET(cpuid), &isrc->isrc_cpu)) | if (CPU_ISSET(PCPU_GET(cpuid), &isrc->isrc_cpu)) | ||||
gic_irq_unmask(sc, irq); | gic_irq_unmask(sc, irq); | ||||
} else { | } else { | ||||
CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); | CPU_SET(PCPU_GET(cpuid), &isrc->isrc_cpu); | ||||
gic_irq_unmask(sc, irq); | gic_irq_unmask(sc, irq); | ||||
▲ Show 20 Lines • Show All 100 Lines • ▼ Show 20 Lines | |||||
gic_xref(device_t dev) | gic_xref(device_t dev) | ||||
{ | { | ||||
#ifdef FDT | #ifdef FDT | ||||
return (OF_xref_from_node(ofw_bus_get_node(dev))); | return (OF_xref_from_node(ofw_bus_get_node(dev))); | ||||
#else | #else | ||||
return (0); | return (0); | ||||
#endif | #endif | ||||
} | } | ||||
static int | |||||
arm_gic_register_isrcs(struct arm_gic_softc *sc, uint32_t num) | |||||
{ | |||||
int error; | |||||
uint32_t irq; | |||||
struct gic_irqsrc *irqs; | |||||
struct intr_irqsrc *isrc; | |||||
const char *name; | |||||
irqs = malloc(num * sizeof(struct gic_irqsrc), M_DEVBUF, | |||||
M_WAITOK | M_ZERO); | |||||
andrew: Do you need to zero the memory if you're just going to write to it later withing the fuction? | |||||
Not Done Inline ActionsThis function is called exactly one time. So M_ZERO is not a big deal. In fact, ISRCs and PIC registering and deregistering is a piece of code to be think over more. I'm trying to think up (to establish) some procedure how to do it and then, I'm prepared to apply it to all PICs. And, it deserves separate commit. skra: This function is called exactly one time. So M_ZERO is not a big deal. In fact, ISRCs and PIC… | |||||
name = device_get_nameunit(sc->gic_dev); | |||||
for (irq = 0; irq < num; irq++) { | |||||
irqs[irq].gi_irq = irq; | |||||
irqs[irq].gi_pol = INTR_POLARITY_CONFORM; | |||||
irqs[irq].gi_trig = INTR_TRIGGER_CONFORM; | |||||
isrc = &irqs[irq].gi_isrc; | |||||
if (irq <= GIC_LAST_SGI) { | |||||
error = intr_isrc_register(isrc, sc->gic_dev, | |||||
INTR_ISRCF_IPI, "%s,i%u", name, irq - GIC_FIRST_SGI); | |||||
} else if (irq <= GIC_LAST_PPI) { | |||||
error = intr_isrc_register(isrc, sc->gic_dev, | |||||
INTR_ISRCF_PPI, "%s,p%u", name, irq - GIC_FIRST_PPI); | |||||
} else { | |||||
error = intr_isrc_register(isrc, sc->gic_dev, 0, | |||||
"%s,s%u", name, irq - GIC_FIRST_SPI); | |||||
} | |||||
if (error != 0) { | |||||
/* XXX call intr_isrc_deregister() */ | |||||
free(irqs, M_DEVBUF); | |||||
return (error); | |||||
} | |||||
} | |||||
sc->gic_irqs = irqs; | |||||
sc->nirqs = num; | |||||
return (0); | |||||
} | |||||
#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; | uint32_t icciidr, nirqs; | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
phandle_t pxref; | phandle_t pxref; | ||||
intptr_t xref = gic_xref(dev); | intptr_t xref = gic_xref(dev); | ||||
#endif | #endif | ||||
if (gic_sc) | if (gic_sc) | ||||
return (ENXIO); | return (ENXIO); | ||||
Show All 17 Lines | #endif | ||||
/* CPU Interface */ | /* CPU Interface */ | ||||
sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); | sc->gic_c_bst = rman_get_bustag(sc->gic_res[1]); | ||||
sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); | sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); | ||||
/* Disable interrupt forwarding to the CPU interface */ | /* Disable interrupt forwarding to the CPU interface */ | ||||
gic_d_write_4(sc, GICD_CTLR, 0x00); | gic_d_write_4(sc, GICD_CTLR, 0x00); | ||||
/* Get the number of interrupts */ | /* Get the number of interrupts */ | ||||
sc->nirqs = gic_d_read_4(sc, GICD_TYPER); | nirqs = gic_d_read_4(sc, GICD_TYPER); | ||||
sc->nirqs = 32 * ((sc->nirqs & 0x1f) + 1); | nirqs = 32 * ((nirqs & 0x1f) + 1); | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
sc->gic_irqs = malloc(sc->nirqs * sizeof (*sc->gic_irqs), M_DEVBUF, | if (arm_gic_register_isrcs(sc, nirqs)) { | ||||
M_WAITOK | M_ZERO); | device_printf(dev, "could not register irqs\n"); | ||||
goto cleanup; | |||||
} | |||||
#else | #else | ||||
sc->nirqs = nirqs; | |||||
/* Set up function pointers */ | /* Set up function pointers */ | ||||
arm_post_filter = gic_post_filter; | arm_post_filter = gic_post_filter; | ||||
arm_config_irq = gic_config_irq; | arm_config_irq = gic_config_irq; | ||||
#endif | #endif | ||||
icciidr = gic_c_read_4(sc, GICC_IIDR); | icciidr = gic_c_read_4(sc, GICC_IIDR); | ||||
device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n", | device_printf(dev,"pn 0x%x, arch 0x%x, rev 0x%x, implementer 0x%x irqs %u\n", | ||||
icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, | icciidr>>20, (icciidr>>16) & 0xF, (icciidr>>12) & 0xf, | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | #else | ||||
* - doesn't have interrupt parent | * - doesn't have interrupt parent | ||||
* - his interrupt parent is this controller | * - his interrupt parent is this controller | ||||
*/ | */ | ||||
pxref = ofw_bus_find_iparent(ofw_bus_get_node(dev)); | pxref = ofw_bus_find_iparent(ofw_bus_get_node(dev)); | ||||
if (pxref == 0 || xref == pxref) { | if (pxref == 0 || xref == pxref) { | ||||
if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc, | if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc, | ||||
GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { | GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { | ||||
device_printf(dev, "could not set PIC as a root\n"); | device_printf(dev, "could not set PIC as a root\n"); | ||||
intr_pic_unregister(dev, xref); | intr_pic_deregister(dev, xref); | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
} else { | } else { | ||||
if (sc->gic_res[2] == NULL) { | if (sc->gic_res[2] == NULL) { | ||||
device_printf(dev, | device_printf(dev, | ||||
"not root PIC must have defined interrupt\n"); | "not root PIC must have defined interrupt\n"); | ||||
intr_pic_unregister(dev, xref); | intr_pic_deregister(dev, xref); | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_CLK, | if (bus_setup_intr(dev, sc->gic_res[2], INTR_TYPE_CLK, | ||||
arm_gic_intr, NULL, sc, &sc->gic_intrhand)) { | arm_gic_intr, NULL, sc, &sc->gic_intrhand)) { | ||||
device_printf(dev, "could not setup irq handler\n"); | device_printf(dev, "could not setup irq handler\n"); | ||||
intr_pic_unregister(dev, xref); | intr_pic_deregister(dev, xref); | ||||
goto cleanup; | goto cleanup; | ||||
} | } | ||||
} | } | ||||
OF_device_register_xref(xref, dev); | OF_device_register_xref(xref, dev); | ||||
return (0); | return (0); | ||||
cleanup: | cleanup: | ||||
/* | /* | ||||
* XXX - not implemented arm_gic_detach() should be called ! | * XXX - not implemented arm_gic_detach() should be called ! | ||||
*/ | */ | ||||
if (sc->gic_irqs != NULL) | if (sc->gic_irqs != NULL) | ||||
free(sc->gic_irqs, M_DEVBUF); | free(sc->gic_irqs, M_DEVBUF); | ||||
bus_release_resources(dev, arm_gic_spec, sc->gic_res); | bus_release_resources(dev, arm_gic_spec, sc->gic_res); | ||||
return(ENXIO); | return(ENXIO); | ||||
#endif | #endif | ||||
} | } | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
static int | static int | ||||
arm_gic_intr(void *arg) | arm_gic_intr(void *arg) | ||||
{ | { | ||||
struct arm_gic_softc *sc = arg; | struct arm_gic_softc *sc = arg; | ||||
struct intr_irqsrc *isrc; | struct gic_irqsrc *gi; | ||||
uint32_t irq_active_reg, irq; | uint32_t irq_active_reg, irq; | ||||
struct trapframe *tf; | struct trapframe *tf; | ||||
irq_active_reg = gic_c_read_4(sc, GICC_IAR); | irq_active_reg = gic_c_read_4(sc, GICC_IAR); | ||||
irq = irq_active_reg & 0x3FF; | irq = irq_active_reg & 0x3FF; | ||||
/* | /* | ||||
* 1. We do EOI here because recent read value from active interrupt | * 1. We do EOI here because recent read value from active interrupt | ||||
Show All 19 Lines | device_printf(sc->gic_dev, | ||||
"Spurious interrupt detected: last irq: %d on CPU%d\n", | "Spurious interrupt detected: last irq: %d on CPU%d\n", | ||||
sc->last_irq[PCPU_GET(cpuid)], PCPU_GET(cpuid)); | sc->last_irq[PCPU_GET(cpuid)], PCPU_GET(cpuid)); | ||||
#endif | #endif | ||||
return (FILTER_HANDLED); | return (FILTER_HANDLED); | ||||
} | } | ||||
tf = curthread->td_intr_frame; | tf = curthread->td_intr_frame; | ||||
dispatch_irq: | dispatch_irq: | ||||
isrc = sc->gic_irqs[irq]; | gi = sc->gic_irqs + irq; | ||||
if (isrc == NULL) { | |||||
device_printf(sc->gic_dev, "Stray interrupt %u detected\n", irq); | |||||
gic_irq_mask(sc, irq); | |||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | |||||
goto next_irq; | |||||
} | |||||
/* | /* | ||||
* Note that GIC_FIRST_SGI is zero and is not used in 'if' statement | * Note that GIC_FIRST_SGI is zero and is not used in 'if' statement | ||||
* as compiler complains that comparing u_int >= 0 is always true. | * as compiler complains that comparing u_int >= 0 is always true. | ||||
*/ | */ | ||||
if (irq <= GIC_LAST_SGI) { | if (irq <= GIC_LAST_SGI) { | ||||
#ifdef SMP | #ifdef SMP | ||||
/* Call EOI for all IPI before dispatch. */ | /* Call EOI for all IPI before dispatch. */ | ||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | ||||
intr_ipi_dispatch(ISRC_IPI(isrc), tf); | intr_ipi_dispatch(sgi_to_ipi[gi->gi_irq], tf); | ||||
goto next_irq; | goto next_irq; | ||||
#else | #else | ||||
device_printf(sc->gic_dev, "SGI %u on UP system detected\n", | device_printf(sc->gic_dev, "SGI %u on UP system detected\n", | ||||
irq - GIC_FIRST_SGI); | irq - GIC_FIRST_SGI); | ||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | ||||
goto next_irq; | goto next_irq; | ||||
#endif | #endif | ||||
} | } | ||||
#ifdef GIC_DEBUG_SPURIOUS | #ifdef GIC_DEBUG_SPURIOUS | ||||
sc->last_irq[PCPU_GET(cpuid)] = irq; | sc->last_irq[PCPU_GET(cpuid)] = irq; | ||||
#endif | #endif | ||||
if (isrc->isrc_trig == INTR_TRIGGER_EDGE) | if (gi->gi_trig == INTR_TRIGGER_EDGE) | ||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | ||||
intr_irq_dispatch(isrc, tf); | if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) { | ||||
gic_irq_mask(sc, irq); | |||||
if (gi->gi_trig != INTR_TRIGGER_EDGE) | |||||
gic_c_write_4(sc, GICC_EOIR, irq_active_reg); | |||||
device_printf(sc->gic_dev, "Stray irq %u disabled\n", irq); | |||||
} | |||||
next_irq: | next_irq: | ||||
arm_irq_memory_barrier(irq); | arm_irq_memory_barrier(irq); | ||||
irq_active_reg = gic_c_read_4(sc, GICC_IAR); | irq_active_reg = gic_c_read_4(sc, GICC_IAR); | ||||
irq = irq_active_reg & 0x3FF; | irq = irq_active_reg & 0x3FF; | ||||
if (irq < sc->nirqs) | if (irq < sc->nirqs) | ||||
goto dispatch_irq; | goto dispatch_irq; | ||||
return (FILTER_HANDLED); | return (FILTER_HANDLED); | ||||
} | } | ||||
static int | |||||
gic_attach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq) | |||||
{ | |||||
const char *name; | |||||
/* | |||||
* 1. The link between ISRC and controller must be set atomically. | |||||
* 2. Just do things only once in rare case when consumers | |||||
* of shared interrupt came here at the same moment. | |||||
*/ | |||||
mtx_lock_spin(&sc->mutex); | |||||
if (sc->gic_irqs[irq] != NULL) { | |||||
mtx_unlock_spin(&sc->mutex); | |||||
return (sc->gic_irqs[irq] == isrc ? 0 : EEXIST); | |||||
} | |||||
sc->gic_irqs[irq] = isrc; | |||||
isrc->isrc_data = irq; | |||||
mtx_unlock_spin(&sc->mutex); | |||||
name = device_get_nameunit(sc->gic_dev); | |||||
if (irq <= GIC_LAST_SGI) | |||||
intr_irq_set_name(isrc, "%s,i%u", name, irq - GIC_FIRST_SGI); | |||||
else if (irq <= GIC_LAST_PPI) | |||||
intr_irq_set_name(isrc, "%s,p%u", name, irq - GIC_FIRST_PPI); | |||||
else | |||||
intr_irq_set_name(isrc, "%s,s%u", name, irq - GIC_FIRST_SPI); | |||||
return (0); | |||||
} | |||||
static int | |||||
gic_detach_isrc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int irq) | |||||
{ | |||||
mtx_lock_spin(&sc->mutex); | |||||
if (sc->gic_irqs[irq] != isrc) { | |||||
mtx_unlock_spin(&sc->mutex); | |||||
return (sc->gic_irqs[irq] == NULL ? 0 : EINVAL); | |||||
} | |||||
sc->gic_irqs[irq] = NULL; | |||||
isrc->isrc_data = 0; | |||||
mtx_unlock_spin(&sc->mutex); | |||||
intr_irq_set_name(isrc, ""); | |||||
return (0); | |||||
} | |||||
static void | static void | ||||
gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig, | gic_config(struct arm_gic_softc *sc, u_int irq, enum intr_trigger trig, | ||||
enum intr_polarity pol) | enum intr_polarity pol) | ||||
{ | { | ||||
uint32_t reg; | uint32_t reg; | ||||
uint32_t mask; | uint32_t mask; | ||||
if (irq < GIC_FIRST_SPI) | if (irq < GIC_FIRST_SPI) | ||||
▲ Show 20 Lines • Show All 41 Lines • ▼ Show 20 Lines | gic_bind(struct arm_gic_softc *sc, u_int irq, cpuset_t *cpus) | ||||
for (mask = 0, cpu = 0; cpu < end; cpu++) | for (mask = 0, cpu = 0; cpu < end; cpu++) | ||||
if (CPU_ISSET(cpu, cpus)) | if (CPU_ISSET(cpu, cpus)) | ||||
mask |= 1 << cpu; | mask |= 1 << cpu; | ||||
gic_d_write_1(sc, GICD_ITARGETSR(0) + irq, mask); | gic_d_write_1(sc, GICD_ITARGETSR(0) + irq, mask); | ||||
return (0); | return (0); | ||||
} | } | ||||
#ifdef FDT | |||||
static int | static int | ||||
gic_irq_from_nspc(struct arm_gic_softc *sc, u_int type, u_int num, u_int *irqp) | gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, | ||||
enum intr_polarity *polp, enum intr_trigger *trigp) | |||||
{ | { | ||||
switch (type) { | if (ncells == 1) { | ||||
case INTR_IRQ_NSPC_PLAIN: | *irqp = cells[0]; | ||||
*irqp = num; | *polp = INTR_POLARITY_CONFORM; | ||||
return (*irqp < sc->nirqs ? 0 : EINVAL); | *trigp = INTR_TRIGGER_CONFORM; | ||||
return (0); | |||||
case INTR_IRQ_NSPC_IRQ: | |||||
*irqp = num + GIC_FIRST_PPI; | |||||
return (*irqp < sc->nirqs ? 0 : EINVAL); | |||||
case INTR_IRQ_NSPC_IPI: | |||||
*irqp = num + GIC_FIRST_SGI; | |||||
return (*irqp < GIC_LAST_SGI ? 0 : EINVAL); | |||||
default: | |||||
return (EINVAL); | |||||
} | } | ||||
} | if (ncells == 3) { | ||||
static int | |||||
gic_map_nspc(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) | |||||
{ | |||||
int error; | |||||
error = gic_irq_from_nspc(sc, isrc->isrc_nspc_type, isrc->isrc_nspc_num, | |||||
irqp); | |||||
if (error != 0) | |||||
return (error); | |||||
return (gic_attach_isrc(sc, isrc, *irqp)); | |||||
} | |||||
#ifdef FDT | |||||
static int | |||||
gic_map_fdt(struct arm_gic_softc *sc, struct intr_irqsrc *isrc, u_int *irqp) | |||||
{ | |||||
u_int irq, tripol; | u_int irq, tripol; | ||||
Not Done Inline ActionsWhy not the following? switch (ncells) { case 1: ... return (0); ... } andrew: Why not the following?
switch (ncells) {
case 1:
...
return (0);
...
} | |||||
Not Done Inline ActionsI try to not use a switch if there are only two cases. Especially if the case when ncells == 1 shouldn't be there at all. Further, this function should be removed in next step and I have tried to not change things not related to this change. This file was changed only to work after changes done in intrng. skra: I try to not use a switch if there are only two cases. Especially if the case when ncells == 1… | |||||
Not Done Inline ActionsHmm, the note about removing of this function somehow sneaked in. Likely, I was thinking about another comment in parallel and my thoughts mixed up. ;) skra: Hmm, the note about removing of this function somehow sneaked in. Likely, I was thinking about… | |||||
enum intr_trigger trig; | |||||
enum intr_polarity pol; | |||||
int error; | |||||
if (isrc->isrc_ncells == 1) { | |||||
irq = isrc->isrc_cells[0]; | |||||
pol = INTR_POLARITY_CONFORM; | |||||
trig = INTR_TRIGGER_CONFORM; | |||||
} else if (isrc->isrc_ncells == 3) { | |||||
if (isrc->isrc_cells[0] == 0) | |||||
irq = isrc->isrc_cells[1] + GIC_FIRST_SPI; | |||||
else | |||||
irq = isrc->isrc_cells[1] + GIC_FIRST_PPI; | |||||
/* | /* | ||||
* In intr[2], bits[3:0] are trigger type and level flags. | * The 1st cell is the interrupt type: | ||||
* 0 = SPI | |||||
* 1 = PPI | |||||
* The 2nd cell contains the interrupt number: | |||||
* [0 - 987] for SPI | |||||
* [0 - 15] for PPI | |||||
* The 3rd cell is the flags, encoded as follows: | |||||
* bits[3:0] trigger type and level flags | |||||
* 1 = low-to-high edge triggered | * 1 = low-to-high edge triggered | ||||
* 2 = high-to-low edge triggered | * 2 = high-to-low edge triggered | ||||
* 4 = active high level-sensitive | * 4 = active high level-sensitive | ||||
* 8 = active low level-sensitive | * 8 = active low level-sensitive | ||||
* The hardware only supports active-high-level or rising-edge. | * bits[15:8] PPI interrupt cpu mask | ||||
* Each bit corresponds to each of the 8 possible cpus | |||||
* attached to the GIC. A bit set to '1' indicated | |||||
* the interrupt is wired to that CPU. | |||||
*/ | */ | ||||
tripol = isrc->isrc_cells[2]; | switch (cells[0]) { | ||||
if (tripol & 0x0a && irq >= GIC_FIRST_SPI) { | case 0: | ||||
device_printf(sc->gic_dev, | irq = GIC_FIRST_SPI + cells[1]; | ||||
"unsupported trigger/polarity configuration " | /* SPI irq is checked later. */ | ||||
"0x%02x\n", tripol & 0x0f); | break; | ||||
case 1: | |||||
irq = GIC_FIRST_PPI + cells[1]; | |||||
if (irq > GIC_LAST_PPI) { | |||||
device_printf(dev, "unsupported PPI interrupt " | |||||
"number %u\n", cells[1]); | |||||
return (EINVAL); | |||||
} | } | ||||
pol = INTR_POLARITY_CONFORM; | break; | ||||
if (tripol & 0x03) | default: | ||||
trig = INTR_TRIGGER_EDGE; | device_printf(dev, "unsupported interrupt type " | ||||
else | "configuration %u\n", cells[0]); | ||||
trig = INTR_TRIGGER_LEVEL; | |||||
} else | |||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
if (irq >= sc->nirqs) | tripol = cells[2] & 0xff; | ||||
return (EINVAL); | if (tripol & 0xf0 || (tripol & 0x0a && cells[0] == 0)) | ||||
device_printf(dev, "unsupported trigger/polarity " | |||||
"configuration 0x%02x\n", tripol); | |||||
error = gic_attach_isrc(sc, isrc, irq); | |||||
if (error != 0) | |||||
return (error); | |||||
isrc->isrc_nspc_type = INTR_IRQ_NSPC_PLAIN; | |||||
isrc->isrc_nspc_num = irq; | |||||
isrc->isrc_trig = trig; | |||||
isrc->isrc_pol = pol; | |||||
*irqp = irq; | *irqp = irq; | ||||
*polp = INTR_POLARITY_CONFORM; | |||||
*trigp = tripol & 0x03 ? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL; | |||||
return (0); | return (0); | ||||
} | } | ||||
return (EINVAL); | |||||
} | |||||
#endif | #endif | ||||
static int | static int | ||||
arm_gic_register(device_t dev, struct intr_irqsrc *isrc, boolean_t *is_percpu) | gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, | ||||
enum intr_polarity *polp, enum intr_trigger *trigp) | |||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | |||||
u_int irq; | u_int irq; | ||||
int error; | enum intr_polarity pol; | ||||
enum intr_trigger trig; | |||||
struct arm_gic_softc *sc; | |||||
if (isrc->isrc_type == INTR_ISRCT_NAMESPACE) | sc = device_get_softc(dev); | ||||
error = gic_map_nspc(sc, isrc, &irq); | switch (data->type) { | ||||
#ifdef FDT | #ifdef FDT | ||||
else if (isrc->isrc_type == INTR_ISRCT_FDT) | case INTR_MAP_DATA_FDT: | ||||
error = gic_map_fdt(sc, isrc, &irq); | if (gic_map_fdt(dev, data->fdt.ncells, data->fdt.cells, &irq, | ||||
&pol, &trig) != 0) | |||||
return (EINVAL); | |||||
break; | |||||
#endif | #endif | ||||
Not Done Inline ActionsWhen ACPI is supported I expect there will be a second attachment created. It will mean this function will only ever handle FDT. andrew: When ACPI is supported I expect there will be a second attachment created. It will mean this… | |||||
Not Done Inline ActionsAFAIK, this file is not used in ACPI like systems. If it will be then the right code must be added here. In fact, I was about to remove the ACPI parts from intrng several times as it's not used now and maybe incorrect. But it's still there to show anybody how it could be used for other cases then FDT one. skra: AFAIK, this file is not used in ACPI like systems. If it will be then the right code must be… | |||||
else | default: | ||||
return (EINVAL); | return (EINVAL); | ||||
} | |||||
if (error == 0) | if (irq >= sc->nirqs) | ||||
*is_percpu = irq < GIC_FIRST_SPI ? TRUE : FALSE; | return (EINVAL); | ||||
if (pol != INTR_POLARITY_CONFORM && pol != INTR_POLARITY_LOW && | |||||
pol != INTR_POLARITY_HIGH) | |||||
return (EINVAL); | |||||
if (trig != INTR_TRIGGER_CONFORM && trig != INTR_TRIGGER_EDGE && | |||||
trig != INTR_TRIGGER_LEVEL) | |||||
return (EINVAL); | |||||
*irqp = irq; | |||||
if (polp != NULL) | |||||
*polp = pol; | |||||
if (trigp != NULL) | |||||
*trigp = trig; | |||||
return (0); | |||||
} | |||||
static int | |||||
arm_gic_map_intr(device_t dev, struct intr_map_data *data, | |||||
struct intr_irqsrc **isrcp) | |||||
{ | |||||
int error; | |||||
u_int irq; | |||||
struct arm_gic_softc *sc; | |||||
error = gic_map_intr(dev, data, &irq, NULL, NULL); | |||||
if (error == 0) { | |||||
sc = device_get_softc(dev); | |||||
*isrcp = GIC_INTR_ISRC(sc, irq); | |||||
} | |||||
return (error); | return (error); | ||||
} | } | ||||
static void | static int | ||||
arm_gic_enable_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 arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
u_int irq = isrc->isrc_data; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
u_int irq; | |||||
enum intr_trigger trig; | |||||
enum intr_polarity pol; | |||||
if (isrc->isrc_trig == INTR_TRIGGER_CONFORM) | if (data == NULL) | ||||
isrc->isrc_trig = INTR_TRIGGER_LEVEL; | return (ENOTSUP); | ||||
/* Get config for resource. */ | |||||
if (gic_map_intr(dev, data, &irq, &pol, &trig)) | |||||
return (EINVAL); | |||||
if (gi->gi_irq != irq) | |||||
return (EINVAL); | |||||
/* Compare config if this is not first setup. */ | |||||
if (isrc->isrc_handlers != 0) { | |||||
if ((pol != INTR_POLARITY_CONFORM && pol != gi->gi_pol) || | |||||
(trig != INTR_TRIGGER_CONFORM && trig != gi->gi_trig)) | |||||
return (EINVAL); | |||||
else | |||||
return (0); | |||||
} | |||||
if (pol == INTR_POLARITY_CONFORM) | |||||
pol = INTR_POLARITY_LOW; /* just pick some */ | |||||
if (trig == INTR_TRIGGER_CONFORM) | |||||
trig = INTR_TRIGGER_EDGE; /* just pick some */ | |||||
gi->gi_pol = pol; | |||||
gi->gi_trig = trig; | |||||
/* | /* | ||||
* 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 | ||||
* 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_PERCPU) | 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, irq, isrc->isrc_trig, isrc->isrc_pol); | gic_config(sc, gi->gi_irq, trig, pol); | ||||
arm_gic_bind(dev, isrc); | arm_gic_bind_intr(dev, isrc); | ||||
return (0); | |||||
} | } | ||||
static void | static int | ||||
arm_gic_enable_source(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 arm_gic_softc *sc = device_get_softc(dev); | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
u_int irq = isrc->isrc_data; | |||||
arm_irq_memory_barrier(irq); | if (isrc->isrc_handlers == 0) { | ||||
gic_irq_unmask(sc, irq); | gi->gi_pol = INTR_POLARITY_CONFORM; | ||||
gi->gi_trig = INTR_TRIGGER_CONFORM; | |||||
} | } | ||||
return (0); | |||||
} | |||||
static void | static void | ||||
arm_gic_disable_source(device_t dev, struct intr_irqsrc *isrc) | arm_gic_enable_intr(device_t dev, struct intr_irqsrc *isrc) | ||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
u_int irq = isrc->isrc_data; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
gic_irq_mask(sc, irq); | arm_irq_memory_barrier(gi->gi_irq); | ||||
gic_irq_unmask(sc, gi->gi_irq); | |||||
} | } | ||||
static int | static void | ||||
arm_gic_unregister(device_t dev, struct intr_irqsrc *isrc) | arm_gic_disable_intr(device_t dev, struct intr_irqsrc *isrc) | ||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
u_int irq = isrc->isrc_data; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
return (gic_detach_isrc(sc, isrc, irq)); | gic_irq_mask(sc, gi->gi_irq); | ||||
} | } | ||||
static void | static void | ||||
arm_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) | arm_gic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) | ||||
{ | { | ||||
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; | |||||
arm_gic_disable_source(dev, isrc); | arm_gic_disable_intr(dev, isrc); | ||||
gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); | gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); | ||||
} | } | ||||
static void | static void | ||||
arm_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc) | arm_gic_post_ithread(device_t dev, struct intr_irqsrc *isrc) | ||||
{ | { | ||||
arm_irq_memory_barrier(0); | arm_irq_memory_barrier(0); | ||||
arm_gic_enable_source(dev, isrc); | arm_gic_enable_intr(dev, isrc); | ||||
} | } | ||||
static void | static void | ||||
arm_gic_post_filter(device_t dev, struct intr_irqsrc *isrc) | arm_gic_post_filter(device_t dev, struct intr_irqsrc *isrc) | ||||
{ | { | ||||
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; | |||||
/* EOI for edge-triggered done earlier. */ | /* EOI for edge-triggered done earlier. */ | ||||
if (isrc->isrc_trig == INTR_TRIGGER_EDGE) | if (gi->gi_trig == INTR_TRIGGER_EDGE) | ||||
return; | return; | ||||
arm_irq_memory_barrier(0); | arm_irq_memory_barrier(0); | ||||
gic_c_write_4(sc, GICC_EOIR, isrc->isrc_data); | gic_c_write_4(sc, GICC_EOIR, gi->gi_irq); | ||||
} | } | ||||
static int | static int | ||||
arm_gic_bind(device_t dev, struct intr_irqsrc *isrc) | arm_gic_bind_intr(device_t dev, struct intr_irqsrc *isrc) | ||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
uint32_t irq = isrc->isrc_data; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
if (irq < GIC_FIRST_SPI) | if (gi->gi_irq < GIC_FIRST_SPI) | ||||
return (EINVAL); | return (EINVAL); | ||||
if (CPU_EMPTY(&isrc->isrc_cpu)) { | if (CPU_EMPTY(&isrc->isrc_cpu)) { | ||||
gic_irq_cpu = intr_irq_next_cpu(gic_irq_cpu, &all_cpus); | gic_irq_cpu = intr_irq_next_cpu(gic_irq_cpu, &all_cpus); | ||||
CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu); | CPU_SETOF(gic_irq_cpu, &isrc->isrc_cpu); | ||||
} | } | ||||
return (gic_bind(sc, irq, &isrc->isrc_cpu)); | return (gic_bind(sc, gi->gi_irq, &isrc->isrc_cpu)); | ||||
} | } | ||||
#ifdef SMP | #ifdef SMP | ||||
static void | static void | ||||
arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus) | arm_gic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus) | ||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
uint32_t irq, val = 0, i; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
uint32_t val = 0, i; | |||||
irq = isrc->isrc_data; | |||||
for (i = 0; i < MAXCPU; i++) | for (i = 0; i < MAXCPU; i++) | ||||
if (CPU_ISSET(i, &cpus)) | if (CPU_ISSET(i, &cpus)) | ||||
val |= 1 << (16 + i); | val |= 1 << (16 + i); | ||||
gic_d_write_4(sc, GICD_SGIR(0), val | irq); | gic_d_write_4(sc, GICD_SGIR(0), val | gi->gi_irq); | ||||
} | } | ||||
static int | static int | ||||
arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc *isrc) | arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) | ||||
{ | { | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
u_int irq; | |||||
int error; | |||||
error = gic_map_nspc(sc, isrc, &irq); | if (sgi_first_unused > GIC_LAST_SGI) | ||||
if (error != 0) | return (ENOSPC); | ||||
return (error); | |||||
sgi_to_ipi[irq - GIC_FIRST_SGI] = ipi; | *isrcp = GIC_INTR_ISRC(sc, sgi_first_unused); | ||||
sgi_to_ipi[sgi_first_unused++] = ipi; | |||||
return (0); | return (0); | ||||
} | } | ||||
#endif | #endif | ||||
#else | #else | ||||
static int | static int | ||||
arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq) | arm_gic_next_irq(struct arm_gic_softc *sc, int last_irq) | ||||
{ | { | ||||
uint32_t active_irq; | uint32_t active_irq; | ||||
▲ Show 20 Lines • Show All 202 Lines • ▼ Show 20 Lines | |||||
#endif /* ARM_INTRNG */ | #endif /* ARM_INTRNG */ | ||||
static device_method_t arm_gic_methods[] = { | static device_method_t arm_gic_methods[] = { | ||||
/* Device interface */ | /* Device interface */ | ||||
DEVMETHOD(device_probe, arm_gic_probe), | DEVMETHOD(device_probe, arm_gic_probe), | ||||
DEVMETHOD(device_attach, arm_gic_attach), | DEVMETHOD(device_attach, arm_gic_attach), | ||||
#ifdef ARM_INTRNG | #ifdef ARM_INTRNG | ||||
/* Interrupt controller interface */ | /* Interrupt controller interface */ | ||||
DEVMETHOD(pic_disable_source, arm_gic_disable_source), | DEVMETHOD(pic_disable_intr, arm_gic_disable_intr), | ||||
DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), | DEVMETHOD(pic_enable_intr, arm_gic_enable_intr), | ||||
DEVMETHOD(pic_enable_source, arm_gic_enable_source), | DEVMETHOD(pic_map_intr, arm_gic_map_intr), | ||||
DEVMETHOD(pic_setup_intr, arm_gic_setup_intr), | |||||
DEVMETHOD(pic_teardown_intr, arm_gic_teardown_intr), | |||||
DEVMETHOD(pic_post_filter, arm_gic_post_filter), | DEVMETHOD(pic_post_filter, arm_gic_post_filter), | ||||
DEVMETHOD(pic_post_ithread, arm_gic_post_ithread), | DEVMETHOD(pic_post_ithread, arm_gic_post_ithread), | ||||
DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread), | DEVMETHOD(pic_pre_ithread, arm_gic_pre_ithread), | ||||
DEVMETHOD(pic_register, arm_gic_register), | |||||
DEVMETHOD(pic_unregister, arm_gic_unregister), | |||||
#ifdef SMP | #ifdef SMP | ||||
DEVMETHOD(pic_bind, arm_gic_bind), | DEVMETHOD(pic_bind_intr, arm_gic_bind_intr), | ||||
DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), | DEVMETHOD(pic_init_secondary, arm_gic_init_secondary), | ||||
DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), | DEVMETHOD(pic_ipi_send, arm_gic_ipi_send), | ||||
DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup), | DEVMETHOD(pic_ipi_setup, arm_gic_ipi_setup), | ||||
#endif | #endif | ||||
#endif | #endif | ||||
{ 0, 0 } | { 0, 0 } | ||||
}; | }; | ||||
Show All 12 Lines |
Do you need to zero the memory if you're just going to write to it later withing the fuction?