Teach the GICv3 driver to translate memory ranges
As with the GICv1/2 driver teach the GICv3 driver to translate memory ranges of children. This allows us to create a common bus_alloc_resource implementation for bot hACPI and FDT attachments. Sponsored by: The FreeBSD Foundation
This commit is contained in:
parent
bdc9ece97b
commit
839374bbfe
@ -39,12 +39,6 @@
|
||||
#ifndef _ARM_GIC_H_
|
||||
#define _ARM_GIC_H_
|
||||
|
||||
struct arm_gic_range {
|
||||
uint64_t bus;
|
||||
uint64_t host;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
struct arm_gic_softc {
|
||||
device_t gic_dev;
|
||||
void * gic_intrhand;
|
||||
|
@ -31,6 +31,12 @@
|
||||
#ifndef _GIC_COMMON_H_
|
||||
#define _GIC_COMMON_H_
|
||||
|
||||
struct arm_gic_range {
|
||||
uint64_t bus;
|
||||
uint64_t host;
|
||||
uint64_t size;
|
||||
};
|
||||
|
||||
#define GIC_IVAR_HW_REV 500
|
||||
#define GIC_IVAR_BUS 501
|
||||
#define GIC_IVAR_VGIC 502
|
||||
|
@ -81,6 +81,7 @@ static bus_print_child_t gic_v3_print_child;
|
||||
static bus_get_domain_t gic_v3_get_domain;
|
||||
static bus_read_ivar_t gic_v3_read_ivar;
|
||||
static bus_write_ivar_t gic_v3_write_ivar;
|
||||
static bus_alloc_resource_t gic_v3_alloc_resource;
|
||||
|
||||
static pic_disable_intr_t gic_v3_disable_intr;
|
||||
static pic_enable_intr_t gic_v3_enable_intr;
|
||||
@ -124,6 +125,8 @@ static device_method_t gic_v3_methods[] = {
|
||||
DEVMETHOD(bus_get_domain, gic_v3_get_domain),
|
||||
DEVMETHOD(bus_read_ivar, gic_v3_read_ivar),
|
||||
DEVMETHOD(bus_write_ivar, gic_v3_write_ivar),
|
||||
DEVMETHOD(bus_alloc_resource, gic_v3_alloc_resource),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
|
||||
/* Interrupt controller interface */
|
||||
DEVMETHOD(pic_disable_intr, gic_v3_disable_intr),
|
||||
@ -435,6 +438,7 @@ gic_v3_detach(device_t dev)
|
||||
for (i = 0; i <= mp_maxid; i++)
|
||||
free(sc->gic_redists.pcpu[i], M_GIC_V3);
|
||||
|
||||
free(sc->ranges, M_GIC_V3);
|
||||
free(sc->gic_res, M_GIC_V3);
|
||||
free(sc->gic_redists.regions, M_GIC_V3);
|
||||
|
||||
@ -524,6 +528,59 @@ gic_v3_write_ivar(device_t dev, device_t child, int which, uintptr_t value)
|
||||
return (ENOENT);
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
gic_v3_alloc_resource(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_softc *sc;
|
||||
struct resource_list_entry *rle;
|
||||
struct resource_list *rl;
|
||||
int j;
|
||||
|
||||
/* We only allocate memory */
|
||||
if (type != SYS_RES_MEMORY)
|
||||
return (NULL);
|
||||
|
||||
sc = device_get_softc(bus);
|
||||
|
||||
if (RMAN_IS_DEFAULT_RANGE(start, end)) {
|
||||
rl = BUS_GET_RESOURCE_LIST(bus, child);
|
||||
if (rl == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Find defaults for this rid */
|
||||
rle = resource_list_find(rl, type, *rid);
|
||||
if (rle == NULL)
|
||||
return (NULL);
|
||||
|
||||
start = rle->start;
|
||||
end = rle->end;
|
||||
count = rle->count;
|
||||
}
|
||||
|
||||
/* Remap through ranges property */
|
||||
for (j = 0; j < sc->nranges; j++) {
|
||||
if (start >= sc->ranges[j].bus && end <
|
||||
sc->ranges[j].bus + sc->ranges[j].size) {
|
||||
start -= sc->ranges[j].bus;
|
||||
start += sc->ranges[j].host;
|
||||
end -= sc->ranges[j].bus;
|
||||
end += sc->ranges[j].host;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == sc->nranges && sc->nranges != 0) {
|
||||
if (bootverbose)
|
||||
device_printf(bus, "Could not map resource "
|
||||
"%#jx-%#jx\n", (uintmax_t)start, (uintmax_t)end);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
|
||||
count, flags));
|
||||
}
|
||||
|
||||
int
|
||||
arm_gic_v3_intr(void *arg)
|
||||
{
|
||||
|
@ -59,7 +59,6 @@ struct gic_v3_acpi_devinfo {
|
||||
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 bus_get_resource_list_t gic_v3_acpi_get_resource_list;
|
||||
|
||||
static void gic_v3_acpi_bus_attach(device_t);
|
||||
@ -71,8 +70,6 @@ static device_method_t gic_v3_acpi_methods[] = {
|
||||
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),
|
||||
DEVMETHOD(bus_get_resource_list, gic_v3_acpi_get_resource_list),
|
||||
|
||||
/* End */
|
||||
@ -445,36 +442,6 @@ gic_v3_acpi_bus_attach(device_t dev)
|
||||
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 resource_list_entry *rle;
|
||||
struct resource_list *rl;
|
||||
|
||||
/* We only allocate memory */
|
||||
if (type != SYS_RES_MEMORY)
|
||||
return (NULL);
|
||||
|
||||
if (RMAN_IS_DEFAULT_RANGE(start, end)) {
|
||||
rl = BUS_GET_RESOURCE_LIST(bus, child);
|
||||
if (rl == NULL)
|
||||
return (NULL);
|
||||
|
||||
/* Find defaults for this rid */
|
||||
rle = resource_list_find(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));
|
||||
}
|
||||
|
||||
static struct resource_list *
|
||||
gic_v3_acpi_get_resource_list(device_t bus, device_t child)
|
||||
{
|
||||
|
@ -54,8 +54,6 @@ __FBSDID("$FreeBSD$");
|
||||
static int gic_v3_fdt_probe(device_t);
|
||||
static int gic_v3_fdt_attach(device_t);
|
||||
|
||||
static struct resource *gic_v3_ofw_bus_alloc_res(device_t, device_t, int, int *,
|
||||
rman_res_t, rman_res_t, rman_res_t, u_int);
|
||||
static const struct ofw_bus_devinfo *gic_v3_ofw_get_devinfo(device_t, device_t);
|
||||
static bus_get_resource_list_t gic_v3_fdt_get_resource_list;
|
||||
|
||||
@ -65,8 +63,6 @@ static device_method_t gic_v3_fdt_methods[] = {
|
||||
DEVMETHOD(device_attach, gic_v3_fdt_attach),
|
||||
|
||||
/* Bus interface */
|
||||
DEVMETHOD(bus_alloc_resource, gic_v3_ofw_bus_alloc_res),
|
||||
DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
|
||||
DEVMETHOD(bus_get_resource_list, gic_v3_fdt_get_resource_list),
|
||||
|
||||
/* ofw_bus interface */
|
||||
@ -219,51 +215,64 @@ gic_v3_ofw_get_devinfo(device_t bus __unused, device_t child)
|
||||
return (&di->di_dinfo);
|
||||
}
|
||||
|
||||
static struct resource *
|
||||
gic_v3_ofw_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)
|
||||
/* Helper functions */
|
||||
static int
|
||||
gic_v3_ofw_fill_ranges(phandle_t parent, struct gic_v3_softc *sc,
|
||||
pcell_t *addr_cellsp, pcell_t *size_cellsp)
|
||||
{
|
||||
struct resource_list_entry *rle;
|
||||
struct resource_list *rl;
|
||||
int ranges_len;
|
||||
pcell_t addr_cells, host_cells, size_cells;
|
||||
cell_t *base_ranges;
|
||||
ssize_t nbase_ranges;
|
||||
int i, j, k;
|
||||
|
||||
/* We only allocate memory */
|
||||
if (type != SYS_RES_MEMORY)
|
||||
return (NULL);
|
||||
host_cells = 1;
|
||||
OF_getencprop(OF_parent(parent), "#address-cells", &host_cells,
|
||||
sizeof(host_cells));
|
||||
addr_cells = 2;
|
||||
OF_getencprop(parent, "#address-cells", &addr_cells,
|
||||
sizeof(addr_cells));
|
||||
size_cells = 2;
|
||||
OF_getencprop(parent, "#size-cells", &size_cells,
|
||||
sizeof(size_cells));
|
||||
|
||||
if (RMAN_IS_DEFAULT_RANGE(start, end)) {
|
||||
rl = BUS_GET_RESOURCE_LIST(bus, child);
|
||||
if (rl == NULL)
|
||||
return (NULL);
|
||||
*addr_cellsp = addr_cells;
|
||||
*size_cellsp = size_cells;
|
||||
|
||||
/* Find defaults for this rid */
|
||||
rle = resource_list_find(rl, type, *rid);
|
||||
if (rle == NULL)
|
||||
return (NULL);
|
||||
nbase_ranges = OF_getproplen(parent, "ranges");
|
||||
if (nbase_ranges < 0)
|
||||
return (EINVAL);
|
||||
|
||||
start = rle->start;
|
||||
end = rle->end;
|
||||
count = rle->count;
|
||||
}
|
||||
/*
|
||||
* XXX: No ranges remap!
|
||||
* Absolute address is expected.
|
||||
*/
|
||||
if (ofw_bus_has_prop(bus, "ranges")) {
|
||||
ranges_len = OF_getproplen(ofw_bus_get_node(bus), "ranges");
|
||||
if (ranges_len != 0) {
|
||||
if (bootverbose) {
|
||||
device_printf(child,
|
||||
"Ranges remap not supported\n");
|
||||
}
|
||||
return (NULL);
|
||||
sc->nranges = nbase_ranges / sizeof(cell_t) /
|
||||
(addr_cells + host_cells + size_cells);
|
||||
if (sc->nranges == 0)
|
||||
return (0);
|
||||
|
||||
sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), M_GIC_V3,
|
||||
M_WAITOK);
|
||||
base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK);
|
||||
OF_getencprop(parent, "ranges", base_ranges, nbase_ranges);
|
||||
|
||||
for (i = 0, j = 0; i < sc->nranges; i++) {
|
||||
sc->ranges[i].bus = 0;
|
||||
for (k = 0; k < addr_cells; k++) {
|
||||
sc->ranges[i].bus <<= 32;
|
||||
sc->ranges[i].bus |= base_ranges[j++];
|
||||
}
|
||||
sc->ranges[i].host = 0;
|
||||
for (k = 0; k < host_cells; k++) {
|
||||
sc->ranges[i].host <<= 32;
|
||||
sc->ranges[i].host |= base_ranges[j++];
|
||||
}
|
||||
sc->ranges[i].size = 0;
|
||||
for (k = 0; k < size_cells; k++) {
|
||||
sc->ranges[i].size <<= 32;
|
||||
sc->ranges[i].size |= base_ranges[j++];
|
||||
}
|
||||
}
|
||||
return (bus_generic_alloc_resource(bus, child, type, rid, start, end,
|
||||
count, flags));
|
||||
}
|
||||
|
||||
/* Helper functions */
|
||||
free(base_ranges, M_DEVBUF);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bus capability support for GICv3.
|
||||
@ -278,16 +287,16 @@ gic_v3_ofw_bus_attach(device_t dev)
|
||||
device_t child;
|
||||
phandle_t parent, node;
|
||||
pcell_t addr_cells, size_cells;
|
||||
int rv;
|
||||
|
||||
sc = device_get_softc(dev);
|
||||
parent = ofw_bus_get_node(dev);
|
||||
if (parent > 0) {
|
||||
addr_cells = 2;
|
||||
OF_getencprop(parent, "#address-cells", &addr_cells,
|
||||
sizeof(addr_cells));
|
||||
size_cells = 2;
|
||||
OF_getencprop(parent, "#size-cells", &size_cells,
|
||||
sizeof(size_cells));
|
||||
rv = gic_v3_ofw_fill_ranges(parent, sc, &addr_cells,
|
||||
&size_cells);
|
||||
if (rv != 0)
|
||||
return (rv);
|
||||
|
||||
/* Iterate through all GIC subordinates */
|
||||
for (node = OF_child(parent); node > 0; node = OF_peer(node)) {
|
||||
/*
|
||||
|
@ -84,6 +84,9 @@ struct gic_v3_softc {
|
||||
device_t *gic_children;
|
||||
struct intr_pic *gic_pic;
|
||||
struct gic_v3_irqsrc *gic_irqs;
|
||||
|
||||
int nranges;
|
||||
struct arm_gic_range * ranges;
|
||||
};
|
||||
|
||||
struct gic_v3_devinfo {
|
||||
|
Loading…
Reference in New Issue
Block a user