Index: sys/arm64/arm64/gic_v3.c =================================================================== --- sys/arm64/arm64/gic_v3.c +++ sys/arm64/arm64/gic_v3.c @@ -58,6 +58,7 @@ #include "gic_v3_var.h" /* Device and PIC methods */ +static int gic_v3_bind(device_t, u_int, u_int); static void gic_v3_dispatch(device_t, struct trapframe *); static void gic_v3_eoi(device_t, u_int); static void gic_v3_mask_irq(device_t, u_int); @@ -72,6 +73,7 @@ DEVMETHOD(device_detach, gic_v3_detach), /* PIC interface */ + DEVMETHOD(pic_bind, gic_v3_bind), DEVMETHOD(pic_dispatch, gic_v3_dispatch), DEVMETHOD(pic_eoi, gic_v3_eoi), DEVMETHOD(pic_mask, gic_v3_mask_irq), @@ -244,6 +246,28 @@ /* * PIC interface. */ + +static int +gic_v3_bind(device_t dev, u_int irq, u_int cpuid) +{ + uint64_t aff; + struct gic_v3_softc *sc; + + sc = device_get_softc(dev); + + if (irq <= GIC_LAST_PPI) { + /* Can't bind PPI to another CPU but it's not an error */ + return (0); + } else if (irq >= GIC_FIRST_SPI && irq <= GIC_LAST_SPI) { + aff = CPU_AFFINITY(cpuid); + gic_d_write(sc, 4, GICD_IROUTER(irq), aff); + return (0); + } else if (irq >= GIC_FIRST_LPI) + return (lpi_migrate(dev, irq, cpuid)); + + return (EINVAL); +} + static void gic_v3_dispatch(device_t dev, struct trapframe *frame) { Index: sys/arm64/arm64/gic_v3_its.c =================================================================== --- sys/arm64/arm64/gic_v3_its.c +++ sys/arm64/arm64/gic_v3_its.c @@ -92,9 +92,13 @@ static void its_init_commandq(struct gic_v3_its_softc *); static void its_init_cpu_collection(struct gic_v3_its_softc *); static uint32_t its_get_devid(device_t); +static struct its_dev * its_device_find_locked(struct gic_v3_its_softc *, + device_t, uint32_t); static int its_cmd_send(struct gic_v3_its_softc *, struct its_cmd_desc *); +static void its_cmd_movi(struct gic_v3_its_softc *, struct its_dev *, uint32_t, + struct its_col *); static void its_cmd_mapc(struct gic_v3_its_softc *, struct its_col *, uint8_t); static void its_cmd_mapvi(struct gic_v3_its_softc *, struct its_dev *, uint32_t, uint32_t); @@ -953,6 +957,32 @@ (unmask == TRUE) ? "unmask" : "mask", irq); } +int +lpi_migrate(device_t parent, uint32_t irq, u_int cpuid) +{ + struct gic_v3_its_softc *sc; + struct its_dev *its_dev; + struct its_col *col; + + sc = its_sc; + mtx_lock_spin(&sc->its_dev_lock); + its_dev = its_device_find_locked(sc, NULL, irq); + mtx_unlock_spin(&sc->its_dev_lock); + if (its_dev == NULL) { + /* Cannot migrate not configured LPI */ + return (ENXIO); + } + + /* Find local device's interrupt identifier */ + irq = irq - its_dev->lpis.lpi_base; + /* Move interrupt to another collection */ + col = sc->its_cols[cpuid]; + its_dev->col->col_id = cpuid; + its_cmd_movi(sc, its_dev, irq, col); + + return (0); +} + void lpi_unmask_irq(device_t parent, uint32_t irq) { @@ -1053,6 +1083,20 @@ } static void +its_cmd_movi(struct gic_v3_its_softc *sc, struct its_dev *its_dev, + uint32_t id, struct its_col *col) +{ + struct its_cmd_desc desc; + + desc.cmd_type = ITS_CMD_MOVI; + desc.cmd_desc_movi.its_dev = its_dev; + desc.cmd_desc_movi.col = col; + desc.cmd_desc_movi.id = id; + + its_cmd_send(sc, &desc); +} + +static void its_cmd_mapc(struct gic_v3_its_softc *sc, struct its_col *col, uint8_t valid) { struct its_cmd_desc desc; @@ -1216,6 +1260,12 @@ target = ITS_TARGET_NONE; switch (cmd_type) { + case ITS_CMD_MOVI: /* Move interrupt ID to another collection */ + cmd_format_command(cmd, ITS_CMD_MOVI); + cmd_format_id(cmd, desc->cmd_desc_movi.id); + cmd_format_col(cmd, desc->cmd_desc_movi.its_dev->col->col_id); + cmd_format_devid(cmd, desc->cmd_desc_movi.its_dev->devid); + break; case ITS_CMD_SYNC: /* Wait for previous commands completion */ target = desc->cmd_desc_sync.col->col_target; cmd_format_command(cmd, ITS_CMD_SYNC); @@ -1367,16 +1417,28 @@ return (0); } +/* Find ITS device descriptor by pci_dev or irq number */ static struct its_dev * -its_device_find_locked(struct gic_v3_its_softc *sc, device_t pci_dev) +its_device_find_locked(struct gic_v3_its_softc *sc, device_t pci_dev, + uint32_t irq) { struct its_dev *its_dev; + struct lpi_chunk *lpis; mtx_assert(&sc->its_dev_lock, MA_OWNED); + KASSERT((pci_dev == NULL || irq == 0), + ("%s: Can't search by both pci_dev and irq number", __func__)); /* Find existing device if any */ TAILQ_FOREACH(its_dev, &sc->its_dev_list, entry) { - if (its_dev->pci_dev == pci_dev) - return (its_dev); + if (pci_dev != NULL) { + if (its_dev->pci_dev == pci_dev) + return (its_dev); + } else if (irq != 0) { + lpis = &its_dev->lpis; + if ((irq >= lpis->lpi_base) && + (irq < (lpis->lpi_base + lpis->lpi_num))) + return (its_dev); + } } return (NULL); @@ -1395,7 +1457,7 @@ mtx_lock_spin(&sc->its_dev_lock); /* Find existing device if any */ - newdev = its_device_find_locked(sc, pci_dev); + newdev = its_device_find_locked(sc, pci_dev, 0); mtx_unlock_spin(&sc->its_dev_lock); if (newdev != NULL) return (newdev); Index: sys/arm64/arm64/gic_v3_var.h =================================================================== --- sys/arm64/arm64/gic_v3_var.h +++ sys/arm64/arm64/gic_v3_var.h @@ -133,6 +133,7 @@ }; /* ITS commands encoding */ +#define ITS_CMD_MOVI (0x01) #define ITS_CMD_SYNC (0x05) #define ITS_CMD_MAPD (0x08) #define ITS_CMD_MAPC (0x09) @@ -172,6 +173,12 @@ union { struct { + struct its_dev *its_dev; + struct its_col *col; + uint32_t id; + } cmd_desc_movi; + + struct { struct its_col *col; } cmd_desc_sync; @@ -257,6 +264,7 @@ int its_init_cpu(struct gic_v3_its_softc *); +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); /*