Index: sys/arm64/arm64/gic_v3.c =================================================================== --- sys/arm64/arm64/gic_v3.c +++ sys/arm64/arm64/gic_v3.c @@ -63,6 +63,9 @@ static void gic_v3_eoi(device_t, u_int); static void gic_v3_mask_irq(device_t, u_int); static void gic_v3_unmask_irq(device_t, u_int); +static boolean_t gic_v3_irq_enabled(device_t, u_int); +static int gic_v3_config_irq(device_t, u_int, enum intr_trigger, + enum intr_polarity); #ifdef SMP static void gic_v3_init_secondary(device_t); static void gic_v3_ipi_send(device_t, cpuset_t, u_int); @@ -78,6 +81,7 @@ DEVMETHOD(pic_eoi, gic_v3_eoi), DEVMETHOD(pic_mask, gic_v3_mask_irq), DEVMETHOD(pic_unmask, gic_v3_unmask_irq), + DEVMETHOD(pic_config, gic_v3_config_irq), #ifdef SMP DEVMETHOD(pic_init_secondary, gic_v3_init_secondary), DEVMETHOD(pic_ipi_send, gic_v3_ipi_send), @@ -355,6 +359,101 @@ panic("%s: Unsupported IRQ number %u", __func__, irq); } +static boolean_t +gic_v3_irq_enabled(device_t dev, u_int irq) +{ + struct gic_v3_softc *sc; + boolean_t enabled = FALSE; + + sc = device_get_softc(dev); + + if (irq <= GIC_LAST_PPI) { + /* SGIs and PPIs in corresponding Re-Distributor */ + if ((GICD_I_MASK(irq) & gic_r_read(sc, 4, + GICR_SGI_BASE_SIZE + GICD_ISENABLER(irq))) != 0) + enabled = TRUE; + } else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) { + /* SPIs in distributor */ + if ((GICD_I_MASK(irq) & gic_d_read(sc, 4, + GICD_ISENABLER(irq))) != 0) + enabled = TRUE; + } else { + enabled = lpi_enabled(dev, irq); + } + + return (enabled); +} + +static int +gic_v3_config_irq(device_t dev, u_int irq, enum intr_trigger trig, + enum intr_polarity pol) +{ + struct gic_v3_softc *sc; + enum gic_v3_xdist xdist; + boolean_t was_enabled; + uint32_t val, oldval; + int rv; + + rv = 0; + was_enabled = FALSE; + sc = device_get_softc(dev); + + /* + * Interrupt configuration for SGIs and LPIs cannot be changed. + * Disable configuration also for PPIs as it is implementation defined + * and access to GICR_ICFGR1 register may trigger an abort. + */ + if ((irq <= GIC_LAST_PPI) || (irq >= GIC_FIRST_LPI)) + return (EINVAL); + + /* SPIs have restrictions on the supported types */ + if (irq >= GIC_FIRST_SPI) { + if (pol != INTR_POLARITY_HIGH) + return (EINVAL); + if ((trig != INTR_TRIGGER_EDGE) && (trig != INTR_TRIGGER_LEVEL)) + return (EINVAL); + } + + /* Access Distributor registers for SPIs */ + xdist = DIST; + + /* + * Read current configuration register, and insert the config + * for "irq", depending on "trig". + */ + val = oldval = gic_xdist_read(sc, 4, GICD_ICFGR(irq), xdist); + if (trig == INTR_TRIGGER_LEVEL) + val &= ~GICD_ICFGR_CONF_MASK(irq); + else if (trig == INTR_TRIGGER_EDGE) + val |= GICD_ICFGR_CONF_MASK(irq); + + /* + * As recommended by the spec, disable the interrupt before changing + * the configuration + */ + if (gic_v3_irq_enabled(dev, irq)) { + gic_v3_mask_irq(dev, irq); + was_enabled = TRUE; + } + + /* + * Write back the new configuration, and possibly re-enable + * the interrupt. If we tried to write a new configuration and failed, + * return an error. + */ + gic_xdist_write(sc, 4, GICD_ICFGR(irq), val, xdist); + if ((gic_xdist_read(sc, 4, GICD_ICFGR(irq), xdist) != val) && + val != oldval) + rv = EINVAL; + + if (was_enabled) + gic_v3_unmask_irq(dev, irq); + + gic_v3_wait_for_rwp(sc, xdist); + + return (rv); +} + #ifdef SMP static void gic_v3_init_secondary(device_t dev) Index: sys/arm64/arm64/gic_v3_its.c =================================================================== --- sys/arm64/arm64/gic_v3_its.c +++ sys/arm64/arm64/gic_v3_its.c @@ -952,6 +952,28 @@ its_cmd_inv(sc, its_dev, lpinum); } +boolean_t +lpi_enabled(device_t gic_dev, uint32_t irq) +{ + struct gic_v3_softc *gic_sc; + struct its_dev *its_dev; + uint8_t *conf_byte; + + gic_sc = device_get_softc(gic_dev); + conf_byte = (uint8_t *)gic_sc->gic_redists.lpis.conf_base; + + TAILQ_FOREACH(its_dev, &its_sc->its_dev_list, entry) { + if (irq >= its_dev->lpis.lpi_base && + irq < (its_dev->lpis.lpi_base + its_dev->lpis.lpi_num)) { + conf_byte += (irq - GIC_FIRST_LPI); + return ((*conf_byte & LPI_CONF_ENABLE) != 0); + } + } + + /* LPI not found */ + return (FALSE); +} + static void lpi_map_to_device(struct gic_v3_its_softc *sc, struct its_dev *its_dev, uint32_t id, uint32_t pid) Index: sys/arm64/arm64/gic_v3_reg.h =================================================================== --- sys/arm64/arm64/gic_v3_reg.h +++ sys/arm64/arm64/gic_v3_reg.h @@ -88,8 +88,14 @@ #define GICD_ICFGR_TRIG_EDGE (1 << 1) #define GICD_ICFGR_TRIG_MASK (0x2) +#define GICD_ICFGR_CONF_BITS (2) #define GICD_I_PER_ICFGRn (16) +/* Configuration bit mask for specific IRQ */ +#define GICD_ICFGR_CONF_MASK(irq) \ + (GICD_ICFGR_TRIG_MASK << \ + (((irq) % GICD_I_PER_ICFGRn) * GICD_ICFGR_CONF_BITS)) + /* * Registers (v3) */ Index: sys/arm64/arm64/gic_v3_var.h =================================================================== --- sys/arm64/arm64/gic_v3_var.h +++ sys/arm64/arm64/gic_v3_var.h @@ -269,6 +269,8 @@ int lpi_migrate(device_t, uint32_t, u_int); void lpi_unmask_irq(device_t, uint32_t); void lpi_mask_irq(device_t, uint32_t); +boolean_t lpi_enabled(device_t, uint32_t); + /* * GIC Distributor accessors. * Notice that only GIC sofc can be passed. @@ -302,6 +304,17 @@ reg, val); \ }) +/* Flexible GIC accessors */ +#define gic_xdist_read(sc, len, reg, xdist) \ + ((xdist == REDIST) ? \ + gic_r_read(sc, len, reg) : \ + gic_d_read(sc, len, reg)) + +#define gic_xdist_write(sc, len, reg, val, xdist) \ + ((xdist == REDIST) ? \ + gic_r_write(sc, len, reg, val) : \ + gic_d_write(sc, len, reg, val)) + #define PCI_DEVID_GENERIC(pci_dev) \ ({ \ ((pci_get_domain(pci_dev) << PCI_RID_DOMAIN_SHIFT) | \