mirror of
https://github.com/freebsd/freebsd-src.git
synced 2026-11-07 04:36:52 -10:00
arm64: Add ACPI support to GICv5 driver
Add ACPI support to the GICv5 IRS, ITS and IWB drivers. Device parameters are derived from the MADT and IORT tables. Reviewed by: andrew Sponsored by: Arm Ltd Differential Revision: https://reviews.freebsd.org/D59171
This commit is contained in:
1 parent
9f24c7d542
commit
9556306213
8 files changed
+745
-3
No files matched your search
@@ -139,6 +139,9 @@ gic_acpi_identify(driver_t *driver, device_t parent)
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_handler, &madt_data);
|
||||
|
||||
if (!madt_data.dist)
|
||||
return;
|
||||
|
||||
/* Check the version of the GIC we have */
|
||||
switch (madt_data.dist->Version) {
|
||||
case ACPI_MADT_GIC_VERSION_NONE:
|
||||
|
||||
@@ -2399,7 +2399,7 @@ gicv3_its_acpi_probe(device_t dev)
|
||||
if (gic_get_bus(dev) != GIC_BUS_ACPI)
|
||||
return (EINVAL);
|
||||
|
||||
if (gic_get_hw_rev(dev) < 3)
|
||||
if (gic_get_hw_rev(dev) < 3 || gic_get_hw_rev(dev) > 4)
|
||||
return (EINVAL);
|
||||
|
||||
if (!gic_get_support_lpis(dev))
|
||||
|
||||
@@ -55,6 +55,11 @@
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
#endif
|
||||
|
||||
#include "pic_if.h"
|
||||
|
||||
#include <arm/arm/gic_common.h>
|
||||
@@ -1166,6 +1171,26 @@ gic_map_fdt(device_t dev, u_int ncells, pcell_t *cells, bool *ppi, u_int *irqp,
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
static int
|
||||
do_gicv5_map_iwb_intr(struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
struct intr_map_data_acpi *daa;
|
||||
device_t iwb;
|
||||
int iwb_id;
|
||||
|
||||
daa = (struct intr_map_data_acpi *)data;
|
||||
iwb_id = (daa->irq & GSI_IWB_ID_MASK) >> GSI_IWB_ID_SHIFT;
|
||||
iwb = acpi_iort_get_iwb_dev(iwb_id);
|
||||
|
||||
if (iwb == NULL)
|
||||
return (ENODEV);
|
||||
|
||||
return (PIC_MAP_INTR(iwb, data, isrcp));
|
||||
}
|
||||
#endif
|
||||
|
||||
static int
|
||||
do_gicv5_map_intr(device_t dev, struct intr_map_data *data, bool *ppip,
|
||||
u_int *irqp, enum intr_polarity *polp, enum intr_trigger *trigp)
|
||||
@@ -1175,6 +1200,9 @@ do_gicv5_map_intr(device_t dev, struct intr_map_data *data, bool *ppip,
|
||||
enum intr_trigger trig;
|
||||
#ifdef FDT
|
||||
struct intr_map_data_fdt *daf;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi *daa;
|
||||
#endif
|
||||
u_int irq;
|
||||
bool ppi;
|
||||
@@ -1189,6 +1217,17 @@ do_gicv5_map_intr(device_t dev, struct intr_map_data *data, bool *ppip,
|
||||
&trig) != 0)
|
||||
return (EINVAL);
|
||||
break;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
case INTR_MAP_DATA_ACPI:
|
||||
daa = (struct intr_map_data_acpi *)data;
|
||||
if ((daa->irq & GSI_INT_TYPE_MASK) == GSI_INT_TYPE_IWB)
|
||||
return (EINVAL);
|
||||
irq = daa->irq & GSI_INT_ID_MASK;
|
||||
pol = daa->pol;
|
||||
trig = daa->trig;
|
||||
ppi = ((daa->irq & GSI_INT_TYPE_MASK) == GSI_INT_TYPE_PPI);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return (EINVAL);
|
||||
@@ -1231,11 +1270,22 @@ static int
|
||||
gicv5_map_intr(device_t dev, struct intr_map_data *data,
|
||||
struct intr_irqsrc **isrcp)
|
||||
{
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi *daa;
|
||||
#endif
|
||||
struct gicv5_softc *sc;
|
||||
u_int irq;
|
||||
int error;
|
||||
bool ppi;
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
if (data->type == INTR_MAP_DATA_ACPI) {
|
||||
daa = (struct intr_map_data_acpi *)data;
|
||||
if ((daa->irq & GSI_INT_TYPE_MASK) == GSI_INT_TYPE_IWB)
|
||||
return (do_gicv5_map_iwb_intr(data, isrcp));
|
||||
}
|
||||
#endif
|
||||
|
||||
error = do_gicv5_map_intr(dev, data, &ppi, &irq, NULL, NULL);
|
||||
if (error == 0) {
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
@@ -0,0 +1,455 @@
|
||||
/*-
|
||||
* Copyright (c) 2026 Arm Ltd
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "opt_acpi.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/malloc.h>
|
||||
#include <sys/module.h>
|
||||
#include <sys/rman.h>
|
||||
#include <sys/cpuset.h>
|
||||
|
||||
#include <machine/intr.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
#include <arm/arm/gic_common.h>
|
||||
#include "gicv5var.h"
|
||||
|
||||
struct gic_v5_acpi_devinfo {
|
||||
struct gicv5_devinfo di_base;
|
||||
};
|
||||
|
||||
static device_identify_t gic_v5_acpi_identify;
|
||||
static device_probe_t gic_v5_acpi_probe;
|
||||
static device_attach_t gic_v5_acpi_attach;
|
||||
static bus_get_resource_list_t gic_v5_acpi_get_resource_list;
|
||||
|
||||
static void gic_v5_acpi_bus_attach(device_t);
|
||||
|
||||
static device_method_t gic_v5_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, gic_v5_acpi_identify),
|
||||
DEVMETHOD(device_probe, gic_v5_acpi_probe),
|
||||
DEVMETHOD(device_attach, gic_v5_acpi_attach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_get_resource_list, gic_v5_acpi_get_resource_list),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(gic, gic_v5_acpi_driver, gic_v5_acpi_methods,
|
||||
sizeof(struct gicv5_softc), gicv5_driver);
|
||||
|
||||
EARLY_DRIVER_MODULE(gic_v5, acpi, gic_v5_acpi_driver, 0, 0,
|
||||
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
||||
|
||||
struct madt_table_data {
|
||||
device_t parent;
|
||||
device_t dev;
|
||||
ACPI_MADT_GICV5_IRS *irs;
|
||||
int count;
|
||||
};
|
||||
|
||||
#define IRS_REGISTER_SIZE 0x10000
|
||||
#define ITS_REGISTER_SIZE 0x10000
|
||||
#define ITS_TRANSLATE_REGISTER_SIZE 0x10000
|
||||
#define IWB_REGISTER_SIZE 0x10000
|
||||
|
||||
static void
|
||||
madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct madt_table_data *madt_data;
|
||||
ACPI_MADT_GICV5_IRS *irs;
|
||||
|
||||
madt_data = (struct madt_table_data *)arg;
|
||||
|
||||
switch (entry->Type) {
|
||||
case ACPI_MADT_TYPE_GICV5_IRS:
|
||||
if (madt_data->irs) {
|
||||
if (bootverbose)
|
||||
device_printf(madt_data->parent,
|
||||
"gic: Already have an IRS");
|
||||
break;
|
||||
}
|
||||
|
||||
irs = (ACPI_MADT_GICV5_IRS *)entry;
|
||||
|
||||
if (irs->Version != 5)
|
||||
break;
|
||||
|
||||
madt_data->irs = irs;
|
||||
madt_data->count++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rdist_map(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
ACPI_MADT_GICV5_IRS *irs;
|
||||
|
||||
struct madt_table_data *madt_data;
|
||||
|
||||
madt_data = (struct madt_table_data *)arg;
|
||||
|
||||
switch (entry->Type) {
|
||||
case ACPI_MADT_TYPE_GICV5_IRS:
|
||||
if (madt_data->irs) {
|
||||
if (bootverbose)
|
||||
device_printf(madt_data->parent,
|
||||
"gic: Already have an IRS");
|
||||
break;
|
||||
}
|
||||
|
||||
irs = (ACPI_MADT_GICV5_IRS *)entry;
|
||||
|
||||
if (irs->Version != 5)
|
||||
break;
|
||||
|
||||
madt_data->irs = irs;
|
||||
|
||||
BUS_SET_RESOURCE(madt_data->parent, madt_data->dev,
|
||||
SYS_RES_MEMORY, madt_data->count, irs->ConfigBaseAddress,
|
||||
IRS_REGISTER_SIZE);
|
||||
madt_data->count++;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gic_v5_acpi_identify(driver_t *driver, device_t parent)
|
||||
{
|
||||
struct madt_table_data madt_data;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
uintptr_t private;
|
||||
device_t dev;
|
||||
|
||||
physaddr = acpi_find_table(ACPI_SIG_MADT);
|
||||
if (physaddr == 0)
|
||||
return;
|
||||
|
||||
madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
|
||||
if (madt == NULL) {
|
||||
device_printf(parent, "gic: Unable to map the MADT\n");
|
||||
return;
|
||||
}
|
||||
|
||||
madt_data.parent = parent;
|
||||
madt_data.count = 0;
|
||||
madt_data.irs = NULL;
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_handler, &madt_data);
|
||||
if (madt_data.count == 0) {
|
||||
device_printf(parent, "No GICv5 IRS\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE,
|
||||
"gic", -1);
|
||||
if (dev == NULL) {
|
||||
device_printf(parent, "add gic child failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Add the MADT data */
|
||||
private = madt_data.irs->Version;
|
||||
acpi_set_private(dev, (void *)private);
|
||||
|
||||
madt_data.dev = dev;
|
||||
madt_data.count = 0;
|
||||
madt_data.irs = NULL;
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
rdist_map, &madt_data);
|
||||
|
||||
out:
|
||||
acpi_unmap_table(madt);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_v5_acpi_probe(device_t dev)
|
||||
{
|
||||
switch ((uintptr_t)acpi_get_private(dev)) {
|
||||
case 5:
|
||||
break;
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_set_desc(dev, "ARM Generic Interrupt Controller v5");
|
||||
return (BUS_PROBE_NOWILDCARD);
|
||||
}
|
||||
|
||||
static void
|
||||
madt_count_irs(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct gicv5_softc *sc = arg;
|
||||
|
||||
if (entry->Type == ACPI_MADT_TYPE_GICV5_IRS)
|
||||
sc->gic_nirs++;
|
||||
}
|
||||
|
||||
static int
|
||||
gic_v5_acpi_count_nirs(struct gicv5_softc *sc, ACPI_TABLE_MADT *madt)
|
||||
{
|
||||
sc->gic_nirs = 0;
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_count_irs, sc);
|
||||
|
||||
return (sc->gic_nirs > 0 ? 0 : ENXIO);
|
||||
}
|
||||
|
||||
struct process_gicc_args {
|
||||
struct gicv5_softc *sc;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
uint32_t irs_id;
|
||||
cpuset_t cpuset;
|
||||
};
|
||||
|
||||
struct process_irs_args {
|
||||
device_t dev;
|
||||
struct gicv5_softc *sc;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
};
|
||||
|
||||
static void
|
||||
madt_process_gicc(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct process_gicc_args *gicc_args;
|
||||
ACPI_MADT_GENERIC_INTERRUPT *gicc;
|
||||
|
||||
gicc_args = (struct process_gicc_args *)arg;
|
||||
|
||||
if (entry->Type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) {
|
||||
gicc = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
|
||||
|
||||
if (gicc->IrsId == gicc_args->irs_id)
|
||||
CPU_SET(gicc->CpuInterfaceNumber, &gicc_args->cpuset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
madt_process_irs(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct process_gicc_args gicc_args;
|
||||
struct process_irs_args *irs_args;
|
||||
ACPI_MADT_GICV5_IRS *irs;
|
||||
struct gicv5_softc *sc;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
int error;
|
||||
|
||||
irs_args = (struct process_irs_args *)arg;
|
||||
sc = irs_args->sc;
|
||||
madt = irs_args->madt;
|
||||
|
||||
if (entry->Type == ACPI_MADT_TYPE_GICV5_IRS) {
|
||||
irs = (ACPI_MADT_GICV5_IRS *)entry;
|
||||
|
||||
gicc_args.sc = sc;
|
||||
gicc_args.madt = madt;
|
||||
gicc_args.irs_id = irs->IrsId;
|
||||
CPU_ZERO(&gicc_args.cpuset);
|
||||
|
||||
/* Build gicc_args.cpuset from GICC tables */
|
||||
acpi_walk_subtables(madt + 1,
|
||||
(char *)madt + madt->Header.Length,
|
||||
madt_process_gicc, &gicc_args);
|
||||
|
||||
sc->gic_nirs++;
|
||||
|
||||
gicv5_irs_init(irs_args->dev, sc->gic_nirs - 1,
|
||||
&gicc_args.cpuset);
|
||||
|
||||
error = bus_set_resource(irs_args->dev, SYS_RES_MEMORY,
|
||||
sc->gic_nirs - 1, irs->ConfigBaseAddress,
|
||||
IRS_REGISTER_SIZE);
|
||||
if (error != 0)
|
||||
panic("%s: Unable to set memory resourec",
|
||||
device_get_nameunit(irs_args->dev));
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
gic_v5_acpi_process_irs(device_t dev, struct gicv5_softc *sc,
|
||||
ACPI_TABLE_MADT *madt)
|
||||
{
|
||||
struct process_irs_args args;
|
||||
|
||||
args.dev = dev;
|
||||
args.sc = sc;
|
||||
args.madt = madt;
|
||||
|
||||
sc->gic_nirs = 0;
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_process_irs, &args);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_v5_acpi_attach(device_t dev)
|
||||
{
|
||||
struct gicv5_softc *sc;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
int err;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->gic_dev = dev;
|
||||
sc->gic_bus = GIC_BUS_ACPI;
|
||||
|
||||
physaddr = acpi_find_table(ACPI_SIG_MADT);
|
||||
if (physaddr == 0)
|
||||
return (ENXIO);
|
||||
|
||||
madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
|
||||
if (madt == NULL) {
|
||||
device_printf(dev, "Unable to map the MADT\n");
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
err = gic_v5_acpi_count_nirs(sc, madt);
|
||||
if (err != 0)
|
||||
goto count_error;
|
||||
|
||||
sc->gic_irs = mallocarray(sc->gic_nirs, sizeof(sc->gic_irs[0]),
|
||||
M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
|
||||
// for each IRS
|
||||
// walk GICC entries to find matching IRS IDs
|
||||
err = gic_v5_acpi_process_irs(dev, sc, madt);
|
||||
|
||||
gicv5_attach(dev);
|
||||
|
||||
sc->gic_pic = intr_pic_register(dev, ACPI_INTR_XREF);
|
||||
if (sc->gic_pic == NULL)
|
||||
panic("%s: could not register PIC", device_get_nameunit(dev));
|
||||
|
||||
intr_ipi_pic_register(dev, 0);
|
||||
|
||||
err = intr_pic_claim_root(dev, ACPI_INTR_XREF, gicv5_intr, sc,
|
||||
INTR_ROOT_IRQ);
|
||||
if (err != 0)
|
||||
panic("%s: Unable to claim PIC root", device_get_nameunit(dev));
|
||||
|
||||
gic_v5_acpi_bus_attach(dev);
|
||||
|
||||
bus_attach_children(dev);
|
||||
|
||||
acpi_unmap_table(madt);
|
||||
|
||||
return (0);
|
||||
|
||||
count_error:
|
||||
if (bootverbose) {
|
||||
device_printf(dev,
|
||||
"Failed to attach. Error %d\n", err);
|
||||
}
|
||||
|
||||
acpi_unmap_table(madt);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static void
|
||||
gic_v5_add_children(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
ACPI_MADT_GICv5_ITS *its;
|
||||
struct gic_v5_acpi_devinfo *di;
|
||||
struct gicv5_softc *sc;
|
||||
device_t dev;
|
||||
|
||||
if (entry->Type == ACPI_MADT_TYPE_GICV5_ITS) {
|
||||
/* We have an ITS, add it as a child */
|
||||
its = (ACPI_MADT_GICv5_ITS *)entry;
|
||||
dev = arg;
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
di = malloc(sizeof(*di), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
|
||||
resource_list_init(&di->di_base.di_rl);
|
||||
resource_list_add(&di->di_base.di_rl, SYS_RES_MEMORY, 0,
|
||||
its->BaseAddress, its->BaseAddress + ITS_REGISTER_SIZE - 1,
|
||||
ITS_REGISTER_SIZE);
|
||||
|
||||
di->di_base.di_irs = sc->gic_irs[0];
|
||||
|
||||
gicv5_add_child(dev, &di->di_base);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gic_v5_acpi_bus_attach(device_t dev)
|
||||
{
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
|
||||
physaddr = acpi_find_table(ACPI_SIG_MADT);
|
||||
if (physaddr == 0) {
|
||||
device_printf(dev, "gic_v5_acpi_bus_attach: no madt\n");
|
||||
return;
|
||||
}
|
||||
|
||||
madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
|
||||
if (madt == NULL) {
|
||||
device_printf(dev, "Unable to map the MADT to add children\n");
|
||||
return;
|
||||
}
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
gic_v5_add_children, dev);
|
||||
|
||||
acpi_unmap_table(madt);
|
||||
|
||||
bus_attach_children(dev);
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
gic_v5_acpi_get_resource_list(device_t bus, device_t child)
|
||||
{
|
||||
struct gic_v5_acpi_devinfo *di;
|
||||
|
||||
di = device_get_ivars(child);
|
||||
KASSERT(di != NULL, ("%s: No devinfo", __func__));
|
||||
|
||||
return (&di->di_base.di_rl);
|
||||
}
|
||||
@@ -56,6 +56,11 @@
|
||||
#include <dev/ofw/ofw_subr.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
#endif
|
||||
|
||||
#include <dev/pci/pcireg.h>
|
||||
#include <dev/pci/pcivar.h>
|
||||
|
||||
@@ -1237,3 +1242,136 @@ gicv5_its_fdt_attach(device_t dev)
|
||||
return (gicv5_its_attach(dev));
|
||||
}
|
||||
#endif /* FDT */
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
static int
|
||||
gicv5_its_acpi_probe(device_t dev);
|
||||
static int
|
||||
gicv5_its_acpi_attach(device_t dev);
|
||||
|
||||
static device_method_t gicv5_its_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, gicv5_its_acpi_probe),
|
||||
DEVMETHOD(device_attach, gicv5_its_acpi_attach),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
#define its_baseclasses itsv5_acpi_baseclasses
|
||||
DEFINE_CLASS_1(its, gicv5_its_acpi_driver, gicv5_its_acpi_methods,
|
||||
sizeof(struct gicv5_its_softc), gicv5_its_driver);
|
||||
#undef its_baseclasses
|
||||
|
||||
EARLY_DRIVER_MODULE(itsv5_acpi, gic, gicv5_its_acpi_driver, 0, 0,
|
||||
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
||||
|
||||
static int
|
||||
gicv5_its_acpi_probe(device_t dev)
|
||||
{
|
||||
device_t parent = device_get_parent(dev);
|
||||
|
||||
if (!parent)
|
||||
return (ENXIO);
|
||||
|
||||
if (strcmp(device_get_name(parent), "gic"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "ARM GICv5 Interrupt Translation Service");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
struct find_its_translate_data {
|
||||
bus_addr_t its_frame_paddr;
|
||||
uint8_t flags;
|
||||
int its_found;
|
||||
int its_translate_found;
|
||||
u_int its_translate_xref;
|
||||
int its_translate_pxm;
|
||||
};
|
||||
|
||||
static void
|
||||
find_its_translate(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
ACPI_MADT_GICv5_ITS_TRANSLATE *its_translate;
|
||||
ACPI_MADT_GICv5_ITS *its;
|
||||
struct find_its_translate_data *find_data;
|
||||
int err;
|
||||
|
||||
find_data = (struct find_its_translate_data *)arg;
|
||||
|
||||
switch (entry->Type) {
|
||||
case ACPI_MADT_TYPE_GICV5_ITS:
|
||||
its = (ACPI_MADT_GICv5_ITS *)entry;
|
||||
|
||||
find_data->its_found++;
|
||||
find_data->flags = its->Flags;
|
||||
break;
|
||||
|
||||
case ACPI_MADT_TYPE_GICV5_ITS_TRANSLATE:
|
||||
its_translate = (ACPI_MADT_GICv5_ITS_TRANSLATE *)entry;
|
||||
|
||||
err = acpi_iort_its_lookup(its_translate->TranslateFrameId,
|
||||
&find_data->its_translate_xref,
|
||||
&find_data->its_translate_pxm);
|
||||
if (err != 0)
|
||||
break;
|
||||
|
||||
find_data->its_translate_found++;
|
||||
find_data->its_frame_paddr = its_translate->BaseAddress;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
gicv5_its_acpi_attach(device_t dev)
|
||||
{
|
||||
struct find_its_translate_data find_data;
|
||||
struct gicv5_its_softc *sc;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
physaddr = acpi_find_table(ACPI_SIG_MADT);
|
||||
if (physaddr == 0)
|
||||
return (ENODEV);
|
||||
|
||||
madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
|
||||
if (madt == NULL) {
|
||||
device_printf(dev, "gicv5_its: Unable to map the MADT\n");
|
||||
return (ENODEV);
|
||||
}
|
||||
|
||||
find_data.its_found = 0;
|
||||
find_data.its_translate_found = 0;
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
find_its_translate, &find_data);
|
||||
|
||||
if (!find_data.its_found) {
|
||||
device_printf(dev, "No valid ITS found\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
if (find_data.its_found > 1) {
|
||||
device_printf(dev, "Multiple ITS found\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
if (!find_data.its_translate_found) {
|
||||
device_printf(dev, "No valid ITS frame found\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
if (find_data.its_translate_found > 1) {
|
||||
device_printf(dev, "Multiple ITS frames found\n");
|
||||
return (EINVAL);
|
||||
}
|
||||
|
||||
sc->its_frame.its_frame_paddr = find_data.its_frame_paddr;
|
||||
sc->its_frame.its_pic = NULL;
|
||||
sc->its_frame.its_xref = find_data.its_translate_xref;
|
||||
sc->its_coherent = !(find_data.flags &
|
||||
ACPI_MADT_GICV5_ITS_NON_COHERENT);
|
||||
|
||||
return (gicv5_its_attach(dev));
|
||||
}
|
||||
#endif /* DEV_ACPI */
|
||||
@@ -47,9 +47,17 @@
|
||||
#include <dev/ofw/ofw_bus_subr.h>
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
#endif
|
||||
|
||||
#include "pci_if.h"
|
||||
#include "pic_if.h"
|
||||
|
||||
#include "gicv5reg.h"
|
||||
#include "gicv5var.h"
|
||||
|
||||
#define IWB_IDR0 0x0000
|
||||
#define IDR0_IW_RANGE_SHIFT 0
|
||||
#define IDR0_IW_RANGE_MASK (0x7ffu << IDR0_IW_RANGE_SHIFT)
|
||||
@@ -207,6 +215,8 @@ gicv5_iwb_attach(device_t dev)
|
||||
error = PCI_ALLOC_MSI(device_get_parent(dev), dev, &count);
|
||||
if (error != 0) {
|
||||
device_printf(dev, "Unable to allocate MSI interrupts\n");
|
||||
bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid,
|
||||
sc->sc_mem);
|
||||
return (error);
|
||||
}
|
||||
|
||||
@@ -362,6 +372,9 @@ do_gicv5_iwb_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
enum intr_trigger trig;
|
||||
#ifdef FDT
|
||||
struct intr_map_data_fdt *daf;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi *daa;
|
||||
#endif
|
||||
u_int irq;
|
||||
|
||||
@@ -375,6 +388,14 @@ do_gicv5_iwb_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
&trig) != 0)
|
||||
return (EINVAL);
|
||||
break;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
case INTR_MAP_DATA_ACPI:
|
||||
daa = (struct intr_map_data_acpi *)data;
|
||||
irq = daa->irq & GSI_IWB_INT_ID_MASK;
|
||||
pol = daa->pol;
|
||||
trig = daa->trig;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
return (EINVAL);
|
||||
@@ -559,3 +580,75 @@ gicv5_iwb_fdt_attach(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
#endif /* FDT */
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
struct gicv5_iwb_acpi_softc {
|
||||
struct gicv5_iwb_softc sc_base;
|
||||
};
|
||||
|
||||
static device_probe_t gicv5_iwb_acpi_probe;
|
||||
static device_attach_t gicv5_iwb_acpi_attach;
|
||||
|
||||
static device_method_t gicv5_iwb_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, gicv5_iwb_acpi_probe),
|
||||
DEVMETHOD(device_attach, gicv5_iwb_acpi_attach),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
#define iwb_baseclasses iwbv5_acpi_baseclasses
|
||||
DEFINE_CLASS_1(iwb, gicv5_iwb_acpi_driver, gicv5_iwb_acpi_methods,
|
||||
sizeof(struct gicv5_iwb_acpi_softc), gicv5_iwb_driver);
|
||||
#undef iwb_baseclasses
|
||||
|
||||
/* This needs to be after the ITS as it sends MSI messages there */
|
||||
EARLY_DRIVER_MODULE(gicv5_iwb_acpi, acpi, gicv5_iwb_acpi_driver, 0, 0,
|
||||
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
|
||||
|
||||
static int
|
||||
gicv5_iwb_acpi_probe(device_t dev)
|
||||
{
|
||||
ACPI_HANDLE h;
|
||||
|
||||
if ((h = acpi_get_handle(dev)) == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
if (!acpi_MatchHid(h, "ARMH0003"))
|
||||
return (ENXIO);
|
||||
|
||||
device_set_desc(dev, "ARM GICv5 Interrupt Wire Bridge");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
gicv5_iwb_acpi_attach(device_t dev)
|
||||
{
|
||||
struct gicv5_iwb_acpi_softc *sc;
|
||||
u_int xref;
|
||||
int its_id;
|
||||
int error;
|
||||
int pxm;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
error = acpi_iort_lookup_its_from_iwb(dev, &its_id);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
error = acpi_iort_its_lookup(its_id, &xref, &pxm);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
error = gicv5_iwb_attach(dev);
|
||||
if (error != 0)
|
||||
return (error);
|
||||
|
||||
sc->sc_base.sc_pic = intr_pic_register(dev, xref);
|
||||
if (sc->sc_base.sc_pic == NULL)
|
||||
return (ENXIO);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif /* ACPI */
|
||||
@@ -68,4 +68,6 @@ int gicv5_intr(void *);
|
||||
void gicv5_irs_init(device_t, u_int, cpuset_t *);
|
||||
void gicv5_irs_extend_ist(device_t, device_t, u_int);
|
||||
|
||||
void gicv5_attach_iwb(device_t, device_t);
|
||||
|
||||
#endif /* _ARM64_GICV5VAR_H_ */
|
||||
@@ -60,9 +60,10 @@ arm64/arm64/gic_v3.c standard
|
||||
arm64/arm64/gic_v3_acpi.c optional acpi
|
||||
arm64/arm64/gic_v3_fdt.c optional fdt
|
||||
arm64/arm64/gicv5.c standard
|
||||
arm64/arm64/gicv5_acpi.c optional acpi
|
||||
arm64/arm64/gicv5_fdt.c optional fdt
|
||||
arm64/arm64/gicv5_its.c optional fdt
|
||||
arm64/arm64/gicv5_iwb.c optional fdt
|
||||
arm64/arm64/gicv5_its.c standard
|
||||
arm64/arm64/gicv5_iwb.c standard
|
||||
arm64/arm64/hyp_stub.S standard
|
||||
arm64/arm64/identcpu.c standard
|
||||
arm64/arm64/kexec_support.c standard
|
||||
|
||||
Reference in new issue
Block a user