Changeset View
Standalone View
sys/arm/arm/gic.c
Show First 20 Lines • Show All 73 Lines • ▼ Show 20 Lines | |||||
#include "msi_if.h" | #include "msi_if.h" | ||||
#endif | #endif | ||||
/* We are using GICv2 register naming */ | /* We are using GICv2 register naming */ | ||||
/* Distributor Registers */ | /* Distributor Registers */ | ||||
/* CPU Registers */ | /* CPU Registers */ | ||||
#define GICC_CTLR 0x0000 /* v1 ICCICR */ | #define GICC_CTLR 0x0000 /* v1 ICCICR */ | ||||
#define GICC_PMR 0x0004 /* v1 ICCPMR */ | #define GICC_PMR 0x0004 /* v1 ICCPMR */ | ||||
#define GICC_BPR 0x0008 /* v1 ICCBPR */ | #define GICC_BPR 0x0008 /* v1 ICCBPR */ | ||||
#define GICC_IAR 0x000C /* v1 ICCIAR */ | #define GICC_IAR 0x000C /* v1 ICCIAR */ | ||||
#define GICC_EOIR 0x0010 /* v1 ICCEOIR */ | #define GICC_EOIR 0x0010 /* v1 ICCEOIR */ | ||||
#define GICC_RPR 0x0014 /* v1 ICCRPR */ | #define GICC_RPR 0x0014 /* v1 ICCRPR */ | ||||
#define GICC_HPPIR 0x0018 /* v1 ICCHPIR */ | #define GICC_HPPIR 0x0018 /* v1 ICCHPIR */ | ||||
#define GICC_ABPR 0x001C /* v1 ICCABPR */ | #define GICC_ABPR 0x001C /* v1 ICCABPR */ | ||||
#define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ | #define GICC_IIDR 0x00FC /* v1 ICCIIDR*/ | ||||
/* TYPER Registers */ | /* TYPER Registers */ | ||||
#define GICD_TYPER_SECURITYEXT 0x400 | #define GICD_TYPER_SECURITYEXT 0x400 | ||||
#define GIC_SUPPORT_SECEXT(_sc) \ | #define GIC_SUPPORT_SECEXT(_sc) \ | ||||
((_sc->typer & GICD_TYPER_SECURITYEXT) == GICD_TYPER_SECURITYEXT) | ((_sc->typer & GICD_TYPER_SECURITYEXT) == GICD_TYPER_SECURITYEXT) | ||||
#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 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 */ | #define GI_FLAG_MSI (1 << 1) /* This interrupt source should only */ | ||||
/* be used for MSI/MSI-X interrupts */ | /* be used for MSI/MSI-X interrupts */ | ||||
#define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */ | #define GI_FLAG_MSI_USED (1 << 2) /* This irq is already allocated */ | ||||
/* for a MSI/MSI-X interrupt */ | /* 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_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 | ||||
static u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; | static u_int sgi_to_ipi[GIC_LAST_SGI - GIC_FIRST_SGI + 1]; | ||||
static u_int sgi_first_unused = GIC_FIRST_SGI; | static u_int sgi_first_unused = GIC_FIRST_SGI; | ||||
#endif | #endif | ||||
#define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc) | #define GIC_INTR_ISRC(sc, irq) (&sc->gic_irqs[irq].gi_isrc) | ||||
#else /* !INTRNG */ | #else /* !INTRNG */ | ||||
static struct ofw_compat_data compat_data[] = { | static struct ofw_compat_data compat_data[] = { | ||||
{"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ | {"arm,gic", true}, /* Non-standard, used in FreeBSD dts. */ | ||||
{"arm,gic-400", true}, | {"arm,gic-400", true}, | ||||
{"arm,cortex-a15-gic", true}, | {"arm,cortex-a15-gic", true}, | ||||
{"arm,cortex-a9-gic", true}, | {"arm,cortex-a9-gic", true}, | ||||
{"arm,cortex-a7-gic", true}, | {"arm,cortex-a7-gic", true}, | ||||
{"arm,arm11mp-gic", true}, | {"arm,arm11mp-gic", true}, | ||||
{"brcm,brahma-b15-gic", true}, | {"brcm,brahma-b15-gic", true}, | ||||
{"qcom,msm-qgic2", true}, | {"qcom,msm-qgic2", true}, | ||||
{NULL, false} | {NULL, false} | ||||
}; | }; | ||||
#endif | #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 INTRNG | { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, /* Virtual Interface Control */ | ||||
andrew: I don't think you need this entry. We should only ever see a maintenance interrupt on the root… | |||||
Done Inline ActionsSo where to put this maintenance interrupt? On the vGIC? mihai: So where to put this maintenance interrupt? On the vGIC? | |||||
Done Inline ActionsYou only need one entry here, it will then be either the maintenance interrupt, or the parent interrupt, or missing. andrew: You only need one entry here, it will then be either the maintenance interrupt, or the parent… | |||||
{ SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* Parent interrupt */ | { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_OPTIONAL }, /* Virtual CPU interface */ | ||||
#endif | { SYS_RES_IRQ, 0, RF_ACTIVE | RF_OPTIONAL }, /* vGIC maintenance interrupt or parent interrupt */ | ||||
{ -1, 0 } | { -1, 0 } | ||||
}; | }; | ||||
extern char hypmode_enabled[]; | |||||
#if defined(__arm__) && defined(INVARIANTS) | #if defined(__arm__) && defined(INVARIANTS) | ||||
static int gic_debug_spurious = 1; | static int gic_debug_spurious = 1; | ||||
#else | #else | ||||
static int gic_debug_spurious = 0; | static int gic_debug_spurious = 0; | ||||
#endif | #endif | ||||
TUNABLE_INT("hw.gic.debug_spurious", &gic_debug_spurious); | TUNABLE_INT("hw.gic.debug_spurious", &gic_debug_spurious); | ||||
static u_int arm_gic_map[MAXCPU]; | static u_int arm_gic_map[MAXCPU]; | ||||
static struct arm_gic_softc *gic_sc = NULL; | static struct arm_gic_softc *gic_sc = NULL; | ||||
#define gic_c_read_4(_sc, _reg) \ | #define gic_c_read_4(_sc, _reg) \ | ||||
bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg)) | bus_space_read_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg)) | ||||
#define gic_c_write_4(_sc, _reg, _val) \ | #define gic_c_write_4(_sc, _reg, _val) \ | ||||
bus_space_write_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg), (_val)) | bus_space_write_4((_sc)->gic_c_bst, (_sc)->gic_c_bsh, (_reg), (_val)) | ||||
#define gic_d_read_4(_sc, _reg) \ | #define gic_d_read_4(_sc, _reg) \ | ||||
bus_space_read_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg)) | bus_space_read_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg)) | ||||
#define gic_d_write_1(_sc, _reg, _val) \ | #define gic_d_write_1(_sc, _reg, _val) \ | ||||
bus_space_write_1((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) | bus_space_write_1((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) | ||||
#define gic_d_write_4(_sc, _reg, _val) \ | #define gic_d_write_4(_sc, _reg, _val) \ | ||||
bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) | bus_space_write_4((_sc)->gic_d_bst, (_sc)->gic_d_bsh, (_reg), (_val)) | ||||
#define gic_h_read_4(_sc, _reg) \ | |||||
bus_space_read_4((_sc)->gic_h_bst, (_sc)->gic_h_bsh, (_reg)) | |||||
#define gic_h_write_4(_sc, _reg, _val) \ | |||||
bus_space_write_4((_sc)->gic_h_bst, (_sc)->gic_h_bsh, (_reg), (_val)) | |||||
struct arm_gic_softc * | |||||
arm_gic_get_sc(void) | |||||
{ | |||||
return gic_sc; | |||||
} | |||||
uint32_t | |||||
arm_gic_get_lr_num(void) | |||||
{ | |||||
return (gic_h_read_4(gic_sc, GICH_VTR) & 0x3f) + 1; | |||||
} | |||||
#ifndef INTRNG | #ifndef INTRNG | ||||
static int gic_config_irq(int irq, enum intr_trigger trig, | static int gic_config_irq(int irq, enum intr_trigger trig, | ||||
enum intr_polarity pol); | enum intr_polarity pol); | ||||
static void gic_post_filter(void *); | static void gic_post_filter(void *); | ||||
#endif | #endif | ||||
#ifdef INTRNG | #ifdef INTRNG | ||||
static inline void | static inline void | ||||
▲ Show 20 Lines • Show All 259 Lines • ▼ Show 20 Lines | arm_gic_attach(device_t dev) | ||||
sc->gic_dev = dev; | sc->gic_dev = dev; | ||||
gic_sc = sc; | gic_sc = sc; | ||||
/* Initialize mutex */ | /* Initialize mutex */ | ||||
mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); | mtx_init(&sc->mutex, "GIC lock", "", MTX_SPIN); | ||||
/* Distributor Interface */ | /* Distributor Interface */ | ||||
sc->gic_d_bst = rman_get_bustag(sc->gic_res[0]); | sc->gic_d_bst = rman_get_bustag(sc->gic_res[DISTRIBUTOR_RES_IDX]); | ||||
sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[0]); | sc->gic_d_bsh = rman_get_bushandle(sc->gic_res[DISTRIBUTOR_RES_IDX]); | ||||
/* 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[CPU_INTERFACE_RES_IDX]); | ||||
sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[1]); | sc->gic_c_bsh = rman_get_bushandle(sc->gic_res[CPU_INTERFACE_RES_IDX]); | ||||
/* Virtual Interface Control */ | |||||
if (sc->is_root) { | |||||
if (sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX] == NULL) { | |||||
device_printf(dev, "Cannot find Virtual Interface Control Registers. Disabling Hyp-Mode...\n"); | |||||
hypmode_enabled[0] = -1; | |||||
} else { | |||||
sc->gic_h_bst = rman_get_bustag(sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX]); | |||||
sc->gic_h_bsh = rman_get_bushandle(sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX]); | |||||
Done Inline ActionsI don't think this is correct for non-root interrupt controllers. andrew: I don't think this is correct for non-root interrupt controllers. | |||||
Done Inline ActionsPlease propose an alternative. How you would see this? mihai: Please propose an alternative. How you would see this? | |||||
Done Inline ActionsYou'll need to extend the softc to add a flag to tell the difference, then set it in the fdt attachment before calling arm_gic_attach. andrew: You'll need to extend the softc to add a flag to tell the difference, then set it in the fdt… | |||||
} | |||||
} else { | |||||
hypmode_enabled[0] = -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->typer = gic_d_read_4(sc, GICD_TYPER); | sc->typer = gic_d_read_4(sc, GICD_TYPER); | ||||
nirqs = GICD_TYPER_I_NUM(sc->typer); | nirqs = GICD_TYPER_I_NUM(sc->typer); | ||||
#ifdef INTRNG | #ifdef INTRNG | ||||
▲ Show 20 Lines • Show All 178 Lines • ▼ Show 20 Lines | case GIC_IVAR_HW_REV: | ||||
return (0); | return (0); | ||||
case GIC_IVAR_BUS: | case GIC_IVAR_BUS: | ||||
KASSERT(sc->gic_bus != GIC_BUS_UNKNOWN, | KASSERT(sc->gic_bus != GIC_BUS_UNKNOWN, | ||||
("arm_gic_read_ivar: Unknown bus type")); | ("arm_gic_read_ivar: Unknown bus type")); | ||||
KASSERT(sc->gic_bus <= GIC_BUS_MAX, | KASSERT(sc->gic_bus <= GIC_BUS_MAX, | ||||
("arm_gic_read_ivar: Invalid bus type %u", sc->gic_bus)); | ("arm_gic_read_ivar: Invalid bus type %u", sc->gic_bus)); | ||||
*result = sc->gic_bus; | *result = sc->gic_bus; | ||||
return (0); | return (0); | ||||
case GIC_IVAR_VIRTUAL_INT_CTRL_RES: | |||||
*result = (uintptr_t)sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX]; | |||||
return (0); | |||||
case GIC_IVAR_VIRTUAL_INT_CTRL_VADDR: | |||||
*result = (uintptr_t)rman_get_virtual(sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX]); | |||||
return (0); | |||||
Done Inline ActionsThis will be broken on arm64. andrew: This will be broken on arm64. | |||||
Not Done Inline ActionsPlease provide some context. You are refering at the size of "result"? mihai: Please provide some context. You are refering at the size of "result"? | |||||
Done Inline Actionsrman_get_virtual returns a virtual address. These are 64 bit on arm64, but unsigned int is only 32 bit, so the top 32 bits will be truncated. andrew: `rman_get_virtual` returns a virtual address. These are 64 bit on arm64, but `unsigned int` is… | |||||
case GIC_IVAR_VIRTUAL_INT_CTRL_PADDR: | |||||
*result = (uintptr_t)rman_get_start(sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX]); | |||||
return (0); | |||||
case GIC_IVAR_VIRTUAL_INT_CTRL_SIZE: | |||||
*result = rman_get_size(sc->gic_res[VIRT_INTERFACE_CONTROL_RES_IDX]); | |||||
return (0); | |||||
case GIC_IVAR_VIRTUAL_CPU_INT_PADDR: | |||||
*result = rman_get_start(sc->gic_res[VIRT_CPU_INTERFACE_RES_IDX]); | |||||
case GIC_IVAR_VIRTUAL_CPU_INT_SIZE: | |||||
*result = rman_get_size(sc->gic_res[VIRT_CPU_INTERFACE_RES_IDX]); | |||||
return (0); | |||||
case GIC_IVAR_LR_NUM: | |||||
*result = (gic_h_read_4(gic_sc, GICH_VTR) & 0x3f) + 1; | |||||
return (0); | |||||
case GIC_IVAR_MAINTENANCE_INTR_RES: | |||||
if (sc->is_root) | |||||
*result = (uintptr_t)sc->gic_res[MAINTENANCE_INTR_RES_IDX]; | |||||
else | |||||
result = NULL; | |||||
return (0); | |||||
} | } | ||||
return (ENOENT); | return (ENOENT); | ||||
} | } | ||||
int | int | ||||
arm_gic_intr(void *arg) | arm_gic_intr(void *arg) | ||||
{ | { | ||||
▲ Show 20 Lines • Show All 445 Lines • ▼ Show 20 Lines | 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); | ||||
struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | struct gic_irqsrc *gi = (struct gic_irqsrc *)isrc; | ||||
uint32_t val = 0, i; | uint32_t val = 0, i; | ||||
for (i = 0; i < MAXCPU; i++) | for (i = 0; i < MAXCPU; i++) | ||||
if (CPU_ISSET(i, &cpus)) | if (CPU_ISSET(i, &cpus)) | ||||
val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; | val |= arm_gic_map[i] << GICD_SGI_TARGET_SHIFT; | ||||
gic_d_write_4(sc, GICD_SGIR, val | gi->gi_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 **isrcp) | arm_gic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp) | ||||
{ | { | ||||
struct intr_irqsrc *isrc; | struct intr_irqsrc *isrc; | ||||
struct arm_gic_softc *sc = device_get_softc(dev); | struct arm_gic_softc *sc = device_get_softc(dev); | ||||
▲ Show 20 Lines • Show All 252 Lines • ▼ Show 20 Lines | |||||
#ifdef INTRNG | #ifdef INTRNG | ||||
/* | /* | ||||
* GICv2m support -- the GICv2 MSI/MSI-X controller. | * GICv2m support -- the GICv2 MSI/MSI-X controller. | ||||
*/ | */ | ||||
#define GICV2M_MSI_TYPER 0x008 | #define GICV2M_MSI_TYPER 0x008 | ||||
#define MSI_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff) | #define MSI_TYPER_SPI_BASE(x) (((x) >> 16) & 0x3ff) | ||||
#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 | ||||
Done Inline ActionsThis extra indentation was to indicate it was fields in the GICV2M_MSI_TYPER register. andrew: This extra indentation was to indicate it was fields in the `GICV2M_MSI_TYPER` register. | |||||
#define GICV2M_MSI_IIDR 0xFCC | #define GICV2M_MSI_IIDR 0xFCC | ||||
int | 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; | struct arm_gic_softc *psc; | ||||
uint32_t typer; | uint32_t typer; | ||||
▲ Show 20 Lines • Show All 211 Lines • Show Last 20 Lines |
I don't think you need this entry. We should only ever see a maintenance interrupt on the root node, or a parent interrupt when the GIC is chained.