iAdd ACPI attachments the the GIC and GICv3 interrupt controller drivers.
For each we need to walk the MADT to find which we have, then add the driver as needed. As each may have a child they will each walk the same table to find these details. Reviewed by: mmel Obtained from: ABT Systems Ltd Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D8720
This commit is contained in:
parent
224c3776f6
commit
ee42eb7e5c
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=327836
@ -36,6 +36,7 @@
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include "opt_platform.h"
|
||||
|
||||
#include <sys/param.h>
|
||||
@ -68,6 +69,11 @@ __FBSDID("$FreeBSD$");
|
||||
#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 <arm/arm/gic.h>
|
||||
#include <arm/arm/gic_common.h>
|
||||
|
||||
@ -888,6 +894,9 @@ gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
#ifdef FDT
|
||||
struct intr_map_data_fdt *daf;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi *daa;
|
||||
#endif
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
switch (data->type) {
|
||||
@ -902,6 +911,14 @@ gic_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
("%s: Attempting to map a MSI interrupt from FDT",
|
||||
__func__));
|
||||
break;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
case INTR_MAP_DATA_ACPI:
|
||||
daa = (struct intr_map_data_acpi *)data;
|
||||
irq = daa->irq;
|
||||
pol = daa->pol;
|
||||
trig = daa->trig;
|
||||
break;
|
||||
#endif
|
||||
case INTR_MAP_DATA_MSI:
|
||||
/* Non-GICv2m MSI */
|
||||
|
346
sys/arm/arm/gic_acpi.c
Normal file
346
sys/arm/arm/gic_acpi.c
Normal file
@ -0,0 +1,346 @@
|
||||
/*-
|
||||
* Copyright (c) 2011,2016 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Andrew Turner under
|
||||
* sponsorship from the FreeBSD Foundation.
|
||||
*
|
||||
* Developed by Damjan Marion <damjan.marion@gmail.com>
|
||||
*
|
||||
* Based on OMAP4 GIC code by Ben Gray
|
||||
*
|
||||
* 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.
|
||||
* 3. The name of the company nor the name of the author may be used to
|
||||
* endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* 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_platform.h"
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/systm.h>
|
||||
#include <sys/bus.h>
|
||||
#include <sys/kernel.h>
|
||||
#include <sys/module.h>
|
||||
|
||||
#include <machine/intr.h>
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
#include <arm/arm/gic.h>
|
||||
#include <arm/arm/gic_common.h>
|
||||
|
||||
struct gic_acpi_devinfo {
|
||||
struct resource_list rl;
|
||||
};
|
||||
|
||||
static device_identify_t gic_acpi_identify;
|
||||
static device_probe_t gic_acpi_probe;
|
||||
static device_attach_t gic_acpi_attach;
|
||||
static bus_get_resource_list_t gic_acpi_get_resource_list;
|
||||
static bool arm_gic_add_children(device_t);
|
||||
|
||||
static device_method_t gic_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, gic_acpi_identify),
|
||||
DEVMETHOD(device_probe, gic_acpi_probe),
|
||||
DEVMETHOD(device_attach, gic_acpi_attach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_get_resource_list, gic_acpi_get_resource_list),
|
||||
|
||||
DEVMETHOD_END,
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(gic, gic_acpi_driver, gic_acpi_methods,
|
||||
sizeof(struct arm_gic_softc), arm_gic_driver);
|
||||
|
||||
static devclass_t gic_acpi_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(gic, acpi, gic_acpi_driver, gic_acpi_devclass, 0, 0,
|
||||
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
||||
|
||||
struct madt_table_data {
|
||||
device_t parent;
|
||||
ACPI_MADT_GENERIC_DISTRIBUTOR *dist;
|
||||
ACPI_MADT_GENERIC_INTERRUPT *intr[MAXCPU];
|
||||
};
|
||||
|
||||
static void
|
||||
madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct madt_table_data *madt_data;
|
||||
ACPI_MADT_GENERIC_INTERRUPT *intr;
|
||||
|
||||
madt_data = (struct madt_table_data *)arg;
|
||||
|
||||
switch(entry->Type) {
|
||||
case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
|
||||
if (madt_data->dist != NULL) {
|
||||
if (bootverbose)
|
||||
device_printf(madt_data->parent,
|
||||
"gic: Already have a distributor table");
|
||||
} else
|
||||
madt_data->dist =
|
||||
(ACPI_MADT_GENERIC_DISTRIBUTOR *)entry;
|
||||
break;
|
||||
case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
|
||||
intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry;
|
||||
if (intr->CpuInterfaceNumber < MAXCPU)
|
||||
madt_data->intr[intr->CpuInterfaceNumber] = intr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gic_acpi_identify(driver_t *driver, device_t parent)
|
||||
{
|
||||
struct madt_table_data madt_data;
|
||||
ACPI_MADT_GENERIC_INTERRUPT *intr;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
device_t dev;
|
||||
int i;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bzero(&madt_data, sizeof(madt_data));
|
||||
madt_data.parent = parent;
|
||||
madt_data.dist = NULL;
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_handler, &madt_data);
|
||||
|
||||
/* Check the version of the GIC we have */
|
||||
switch (madt_data.dist->Version) {
|
||||
case ACPI_MADT_GIC_VERSION_NONE:
|
||||
case ACPI_MADT_GIC_VERSION_V1:
|
||||
case ACPI_MADT_GIC_VERSION_V2:
|
||||
break;
|
||||
default:
|
||||
goto out;
|
||||
}
|
||||
|
||||
intr = NULL;
|
||||
for (i = 0; i < MAXCPU; i++) {
|
||||
if (madt_data.intr[i] != NULL) {
|
||||
if (intr == NULL) {
|
||||
intr = madt_data.intr[i];
|
||||
} else if (intr->BaseAddress !=
|
||||
madt_data.intr[i]->BaseAddress) {
|
||||
device_printf(parent,
|
||||
"gic: Not all CPU interfaces at the same address, this may fail\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (intr == NULL) {
|
||||
device_printf(parent, "gic: No CPU interfaces found\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;
|
||||
}
|
||||
|
||||
BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0,
|
||||
madt_data.dist->BaseAddress, 4 * 1024);
|
||||
BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 1,
|
||||
intr->BaseAddress, 4 * 1024);
|
||||
|
||||
acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version);
|
||||
out:
|
||||
acpi_unmap_table(madt);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_acpi_probe(device_t dev)
|
||||
{
|
||||
|
||||
switch((uintptr_t)acpi_get_private(dev)) {
|
||||
case ACPI_MADT_GIC_VERSION_NONE:
|
||||
case ACPI_MADT_GIC_VERSION_V1:
|
||||
case ACPI_MADT_GIC_VERSION_V2:
|
||||
break;
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_set_desc(dev, "ARM Generic Interrupt Controller");
|
||||
return (BUS_PROBE_NOWILDCARD);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_acpi_attach(device_t dev)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
intptr_t xref;
|
||||
int err;
|
||||
|
||||
sc->gic_bus = GIC_BUS_ACPI;
|
||||
|
||||
err = arm_gic_attach(dev);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
xref = 0;
|
||||
|
||||
/*
|
||||
* Now, when everything is initialized, it's right time to
|
||||
* register interrupt controller to interrupt framefork.
|
||||
*/
|
||||
if (intr_pic_register(dev, xref) == NULL) {
|
||||
device_printf(dev, "could not register PIC\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/*
|
||||
* Controller is root:
|
||||
*/
|
||||
if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc,
|
||||
GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
|
||||
device_printf(dev, "could not set PIC as a root\n");
|
||||
intr_pic_deregister(dev, xref);
|
||||
goto cleanup;
|
||||
}
|
||||
/* If we have children probe and attach them */
|
||||
if (arm_gic_add_children(dev)) {
|
||||
bus_generic_probe(dev);
|
||||
return (bus_generic_attach(dev));
|
||||
}
|
||||
|
||||
return (0);
|
||||
|
||||
cleanup:
|
||||
arm_gic_detach(dev);
|
||||
return(ENXIO);
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
gic_acpi_get_resource_list(device_t bus, device_t child)
|
||||
{
|
||||
struct gic_acpi_devinfo *di;
|
||||
|
||||
di = device_get_ivars(child);
|
||||
KASSERT(di != NULL, ("gic_acpi_get_resource_list: No devinfo"));
|
||||
|
||||
return (&di->rl);
|
||||
}
|
||||
|
||||
static void
|
||||
madt_gicv2m_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct arm_gic_softc *sc;
|
||||
ACPI_MADT_GENERIC_MSI_FRAME *msi;
|
||||
struct gic_acpi_devinfo *dinfo;
|
||||
device_t dev, cdev;
|
||||
|
||||
if (entry->Type == ACPI_MADT_TYPE_GENERIC_MSI_FRAME) {
|
||||
sc = arg;
|
||||
dev = sc->gic_dev;
|
||||
msi = (ACPI_MADT_GENERIC_MSI_FRAME *)entry;
|
||||
|
||||
device_printf(dev, "frame: %x %lx %x %u %u\n", msi->MsiFrameId,
|
||||
msi->BaseAddress, msi->Flags, msi->SpiCount, msi->SpiBase);
|
||||
|
||||
cdev = device_add_child(dev, NULL, -1);
|
||||
if (cdev == NULL)
|
||||
return;
|
||||
|
||||
dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
|
||||
resource_list_init(&dinfo->rl);
|
||||
resource_list_add(&dinfo->rl, SYS_RES_MEMORY, 0,
|
||||
msi->BaseAddress, msi->BaseAddress + PAGE_SIZE - 1,
|
||||
PAGE_SIZE);
|
||||
device_set_ivars(cdev, dinfo);
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
arm_gic_add_children(device_t dev)
|
||||
{
|
||||
struct arm_gic_softc *sc = device_get_softc(dev);
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
|
||||
/* This should return a valid address as it did in gic_acpi_identify */
|
||||
physaddr = acpi_find_table(ACPI_SIG_MADT);
|
||||
if (physaddr == 0)
|
||||
return (false);
|
||||
|
||||
madt = acpi_map_table(physaddr, ACPI_SIG_MADT);
|
||||
if (madt == NULL) {
|
||||
device_printf(dev, "gic: Unable to map the MADT\n");
|
||||
return (false);
|
||||
}
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_gicv2m_handler, sc);
|
||||
|
||||
acpi_unmap_table(madt);
|
||||
|
||||
return (true);
|
||||
}
|
||||
|
||||
static int
|
||||
arm_gicv2m_acpi_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (gic_get_bus(dev) != GIC_BUS_ACPI)
|
||||
return (EINVAL);
|
||||
|
||||
if (gic_get_hw_rev(dev) > 2)
|
||||
return (EINVAL);
|
||||
|
||||
device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static device_method_t arm_gicv2m_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, arm_gicv2m_acpi_probe),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(gicv2m, arm_gicv2m_acpi_driver, arm_gicv2m_acpi_methods,
|
||||
sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver);
|
||||
|
||||
static devclass_t arm_gicv2m_acpi_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(gicv2m, gic, arm_gicv2m_acpi_driver,
|
||||
arm_gicv2m_acpi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
@ -30,6 +30,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include "opt_platform.h"
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
@ -63,6 +64,11 @@ __FBSDID("$FreeBSD$");
|
||||
#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>
|
||||
@ -569,6 +575,9 @@ do_gic_v3_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp,
|
||||
struct intr_map_data_msi *dam;
|
||||
#ifdef FDT
|
||||
struct intr_map_data_fdt *daf;
|
||||
#endif
|
||||
#ifdef DEV_ACPI
|
||||
struct intr_map_data_acpi *daa;
|
||||
#endif
|
||||
u_int irq;
|
||||
|
||||
@ -582,6 +591,14 @@ do_gic_v3_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;
|
||||
pol = daa->pol;
|
||||
trig = daa->trig;
|
||||
break;
|
||||
#endif
|
||||
case INTR_MAP_DATA_MSI:
|
||||
/* SPI-mapped MSI */
|
||||
|
335
sys/arm64/arm64/gic_v3_acpi.c
Normal file
335
sys/arm64/arm64/gic_v3_acpi.c
Normal file
@ -0,0 +1,335 @@
|
||||
/*-
|
||||
* Copyright (c) 2016 The FreeBSD Foundation
|
||||
* All rights reserved.
|
||||
*
|
||||
* This software was developed by Andrew Turner under
|
||||
* the sponsorship of the FreeBSD Foundation.
|
||||
*
|
||||
* 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 <sys/cdefs.h>
|
||||
__FBSDID("$FreeBSD$");
|
||||
|
||||
#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 <machine/intr.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <contrib/dev/acpica/include/acpi.h>
|
||||
#include <dev/acpica/acpivar.h>
|
||||
|
||||
#include "gic_v3_reg.h"
|
||||
#include "gic_v3_var.h"
|
||||
|
||||
struct gic_v3_acpi_devinfo {
|
||||
struct resource_list di_rl;
|
||||
};
|
||||
|
||||
static device_identify_t gic_v3_acpi_identify;
|
||||
static device_probe_t gic_v3_acpi_probe;
|
||||
static device_attach_t gic_v3_acpi_attach;
|
||||
static bus_alloc_resource_t gic_v3_acpi_bus_alloc_res;
|
||||
|
||||
static void gic_v3_acpi_bus_attach(device_t);
|
||||
|
||||
static device_method_t gic_v3_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_identify, gic_v3_acpi_identify),
|
||||
DEVMETHOD(device_probe, gic_v3_acpi_probe),
|
||||
DEVMETHOD(device_attach, gic_v3_acpi_attach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_alloc_resource, gic_v3_acpi_bus_alloc_res),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
DEFINE_CLASS_1(gic, gic_v3_acpi_driver, gic_v3_acpi_methods,
|
||||
sizeof(struct gic_v3_softc), gic_v3_driver);
|
||||
|
||||
static devclass_t gic_v3_acpi_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(gic_v3, acpi, gic_v3_acpi_driver, gic_v3_acpi_devclass,
|
||||
0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
||||
|
||||
|
||||
struct madt_table_data {
|
||||
device_t parent;
|
||||
device_t dev;
|
||||
ACPI_MADT_GENERIC_DISTRIBUTOR *dist;
|
||||
int count;
|
||||
};
|
||||
|
||||
static void
|
||||
madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
struct madt_table_data *madt_data;
|
||||
|
||||
madt_data = (struct madt_table_data *)arg;
|
||||
|
||||
switch(entry->Type) {
|
||||
case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
|
||||
if (madt_data->dist != NULL) {
|
||||
if (bootverbose)
|
||||
device_printf(madt_data->parent,
|
||||
"gic: Already have a distributor table");
|
||||
break;
|
||||
}
|
||||
madt_data->dist = (ACPI_MADT_GENERIC_DISTRIBUTOR *)entry;
|
||||
break;
|
||||
|
||||
case ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
rdist_map(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
ACPI_MADT_GENERIC_REDISTRIBUTOR *redist;
|
||||
struct madt_table_data *madt_data;
|
||||
|
||||
madt_data = (struct madt_table_data *)arg;
|
||||
|
||||
switch(entry->Type) {
|
||||
case ACPI_MADT_TYPE_GENERIC_REDISTRIBUTOR:
|
||||
redist = (ACPI_MADT_GENERIC_REDISTRIBUTOR *)entry;
|
||||
|
||||
madt_data->count++;
|
||||
BUS_SET_RESOURCE(madt_data->parent, madt_data->dev,
|
||||
SYS_RES_MEMORY, madt_data->count, redist->BaseAddress,
|
||||
redist->Length);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gic_v3_acpi_identify(driver_t *driver, device_t parent)
|
||||
{
|
||||
struct madt_table_data madt_data;
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
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.dist = NULL;
|
||||
madt_data.count = 0;
|
||||
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
madt_handler, &madt_data);
|
||||
if (madt_data.dist == NULL) {
|
||||
device_printf(parent,
|
||||
"No gic interrupt or distributor table\n");
|
||||
goto out;
|
||||
}
|
||||
/* This is for the wrong GIC version */
|
||||
if (madt_data.dist->Version != ACPI_MADT_GIC_VERSION_V3)
|
||||
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 */
|
||||
BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0,
|
||||
madt_data.dist->BaseAddress, 128 * 1024);
|
||||
|
||||
madt_data.dev = dev;
|
||||
acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length,
|
||||
rdist_map, &madt_data);
|
||||
|
||||
acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version);
|
||||
|
||||
out:
|
||||
acpi_unmap_table(madt);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_v3_acpi_probe(device_t dev)
|
||||
{
|
||||
|
||||
switch((uintptr_t)acpi_get_private(dev)) {
|
||||
case ACPI_MADT_GIC_VERSION_V3:
|
||||
break;
|
||||
default:
|
||||
return (ENXIO);
|
||||
}
|
||||
|
||||
device_set_desc(dev, GIC_V3_DEVSTR);
|
||||
return (BUS_PROBE_NOWILDCARD);
|
||||
}
|
||||
|
||||
static int
|
||||
gic_v3_acpi_attach(device_t dev)
|
||||
{
|
||||
struct gic_v3_softc *sc;
|
||||
int err;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
sc->dev = dev;
|
||||
sc->gic_bus = GIC_BUS_ACPI;
|
||||
|
||||
/* TODO: Count these correctly */
|
||||
sc->gic_redists.nregions = 1;
|
||||
|
||||
err = gic_v3_attach(dev);
|
||||
if (err != 0)
|
||||
goto error;
|
||||
|
||||
sc->gic_pic = intr_pic_register(dev, 0);
|
||||
if (sc->gic_pic == NULL) {
|
||||
device_printf(dev, "could not register PIC\n");
|
||||
err = ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (intr_pic_claim_root(dev, 0, arm_gic_v3_intr, sc,
|
||||
GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) {
|
||||
err = ENXIO;
|
||||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to register the ITS driver to this GIC. The GIC will act as
|
||||
* a bus in that case. Failure here will not affect the main GIC
|
||||
* functionality.
|
||||
*/
|
||||
gic_v3_acpi_bus_attach(dev);
|
||||
|
||||
return (0);
|
||||
|
||||
error:
|
||||
if (bootverbose) {
|
||||
device_printf(dev,
|
||||
"Failed to attach. Error %d\n", err);
|
||||
}
|
||||
/* Failure so free resources */
|
||||
gic_v3_detach(dev);
|
||||
|
||||
return (err);
|
||||
}
|
||||
|
||||
static void
|
||||
gic_v3_add_children(ACPI_SUBTABLE_HEADER *entry, void *arg)
|
||||
{
|
||||
ACPI_MADT_GENERIC_TRANSLATOR *gict;
|
||||
struct gic_v3_acpi_devinfo *di;
|
||||
device_t child, dev;
|
||||
|
||||
if (entry->Type == ACPI_MADT_TYPE_GENERIC_TRANSLATOR) {
|
||||
/* We have an ITS, add it as a child */
|
||||
gict = (ACPI_MADT_GENERIC_TRANSLATOR *)entry;
|
||||
dev = arg;
|
||||
|
||||
child = device_add_child(dev, "its", -1);
|
||||
if (child == NULL)
|
||||
return;
|
||||
|
||||
di = malloc(sizeof(*di), M_GIC_V3, M_WAITOK | M_ZERO);
|
||||
resource_list_init(&di->di_rl);
|
||||
resource_list_add(&di->di_rl, SYS_RES_MEMORY, 0,
|
||||
gict->BaseAddress, gict->BaseAddress + 128 * 1024 - 1,
|
||||
128 * 1024);
|
||||
device_set_ivars(child, di);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gic_v3_acpi_bus_attach(device_t dev)
|
||||
{
|
||||
ACPI_TABLE_MADT *madt;
|
||||
vm_paddr_t physaddr;
|
||||
|
||||
physaddr = acpi_find_table(ACPI_SIG_MADT);
|
||||
if (physaddr == 0)
|
||||
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_v3_add_children, dev);
|
||||
|
||||
acpi_unmap_table(madt);
|
||||
|
||||
bus_generic_attach(dev);
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
gic_v3_acpi_bus_alloc_res(device_t bus, device_t child, int type, int *rid,
|
||||
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags)
|
||||
{
|
||||
struct gic_v3_acpi_devinfo *di;
|
||||
struct resource_list_entry *rle;
|
||||
|
||||
/* We only allocate memory */
|
||||
if (type != SYS_RES_MEMORY)
|
||||
return (NULL);
|
||||
|
||||
if (RMAN_IS_DEFAULT_RANGE(start, end)) {
|
||||
if ((di = device_get_ivars(child)) == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Find defaults for this rid */
|
||||
rle = resource_list_find(&di->di_rl, type, *rid);
|
||||
if (rle == NULL)
|
||||
return (NULL);
|
||||
|
||||
start = rle->start;
|
||||
end = rle->end;
|
||||
count = rle->count;
|
||||
}
|
||||
|
||||
return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
|
||||
count, flags));
|
||||
}
|
@ -32,6 +32,8 @@
|
||||
#ifndef _GIC_V3_VAR_H_
|
||||
#define _GIC_V3_VAR_H_
|
||||
|
||||
#include <arm/arm/gic_common.h>
|
||||
|
||||
#define GIC_V3_DEVSTR "ARM Generic Interrupt Controller v3.0"
|
||||
|
||||
DECLARE_CLASS(gic_v3_driver);
|
||||
@ -92,10 +94,8 @@ struct gic_v3_devinfo {
|
||||
MALLOC_DECLARE(M_GIC_V3);
|
||||
|
||||
/* ivars */
|
||||
enum {
|
||||
GICV3_IVAR_NIRQS,
|
||||
GICV3_IVAR_REDIST_VADDR,
|
||||
};
|
||||
#define GICV3_IVAR_NIRQS 1000
|
||||
#define GICV3_IVAR_REDIST_VADDR 1001
|
||||
|
||||
__BUS_ACCESSOR(gicv3, nirqs, GICV3, NIRQS, u_int);
|
||||
__BUS_ACCESSOR(gicv3, redist_vaddr, GICV3, REDIST_VADDR, void *);
|
||||
|
@ -30,6 +30,7 @@
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "opt_acpi.h"
|
||||
#include "opt_platform.h"
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
@ -1640,7 +1641,7 @@ DEFINE_CLASS_1(its, gicv3_its_fdt_driver, gicv3_its_fdt_methods,
|
||||
#undef its_baseclasses
|
||||
static devclass_t gicv3_its_fdt_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(its, gic, gicv3_its_fdt_driver,
|
||||
EARLY_DRIVER_MODULE(its_fdt, gic, gicv3_its_fdt_driver,
|
||||
gicv3_its_fdt_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
||||
|
||||
static int
|
||||
@ -1686,3 +1687,62 @@ gicv3_its_fdt_attach(device_t dev)
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEV_ACPI
|
||||
static device_probe_t gicv3_its_acpi_probe;
|
||||
static device_attach_t gicv3_its_acpi_attach;
|
||||
|
||||
static device_method_t gicv3_its_acpi_methods[] = {
|
||||
/* Device interface */
|
||||
DEVMETHOD(device_probe, gicv3_its_acpi_probe),
|
||||
DEVMETHOD(device_attach, gicv3_its_acpi_attach),
|
||||
|
||||
/* End */
|
||||
DEVMETHOD_END
|
||||
};
|
||||
|
||||
#define its_baseclasses its_acpi_baseclasses
|
||||
DEFINE_CLASS_1(its, gicv3_its_acpi_driver, gicv3_its_acpi_methods,
|
||||
sizeof(struct gicv3_its_softc), gicv3_its_driver);
|
||||
#undef its_baseclasses
|
||||
static devclass_t gicv3_its_acpi_devclass;
|
||||
|
||||
EARLY_DRIVER_MODULE(its_acpi, gic, gicv3_its_acpi_driver,
|
||||
gicv3_its_acpi_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
|
||||
|
||||
static int
|
||||
gicv3_its_acpi_probe(device_t dev)
|
||||
{
|
||||
|
||||
if (gic_get_bus(dev) != GIC_BUS_ACPI)
|
||||
return (EINVAL);
|
||||
|
||||
if (gic_get_hw_rev(dev) < 3)
|
||||
return (EINVAL);
|
||||
|
||||
device_set_desc(dev, "ARM GIC Interrupt Translation Service");
|
||||
return (BUS_PROBE_DEFAULT);
|
||||
}
|
||||
|
||||
static int
|
||||
gicv3_its_acpi_attach(device_t dev)
|
||||
{
|
||||
struct gicv3_its_softc *sc;
|
||||
int err;
|
||||
|
||||
err = gicv3_its_attach(dev);
|
||||
if (err != 0)
|
||||
return (err);
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
|
||||
sc->sc_pic = intr_pic_register(dev, 1);
|
||||
intr_pic_add_handler(device_get_parent(dev), sc->sc_pic,
|
||||
gicv3_its_intr, sc, GIC_FIRST_LPI, LPI_NIRQS);
|
||||
|
||||
/* Register this device to handle MSI interrupts */
|
||||
intr_msi_register(dev, 1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
#endif
|
||||
|
@ -63,6 +63,7 @@ arm/annapurna/alpine/alpine_serdes.c optional al_serdes fdt \
|
||||
compile-with "${CC} -c -o ${.TARGET} ${CFLAGS} -I$S/contrib/alpine-hal -I$S/contrib/alpine-hal/eth ${PROF} ${.IMPSRC}"
|
||||
arm/arm/generic_timer.c standard
|
||||
arm/arm/gic.c standard
|
||||
arm/arm/gic_acpi.c optional acpi
|
||||
arm/arm/gic_fdt.c optional fdt
|
||||
arm/arm/pmu.c standard
|
||||
arm/broadcom/bcm2835/bcm2835_audio.c optional sound vchiq fdt \
|
||||
@ -111,6 +112,7 @@ arm64/arm64/exception.S standard
|
||||
arm64/arm64/freebsd32_machdep.c optional compat_freebsd32
|
||||
arm64/arm64/gicv3_its.c optional intrng fdt
|
||||
arm64/arm64/gic_v3.c standard
|
||||
arm64/arm64/gic_v3_acpi.c optional acpi
|
||||
arm64/arm64/gic_v3_fdt.c optional fdt
|
||||
arm64/arm64/identcpu.c standard
|
||||
arm64/arm64/in_cksum.c optional inet | inet6
|
||||
|
Loading…
Reference in New Issue
Block a user