mirror of
https://github.com/openbsd/src.git
synced 2024-12-21 23:18:00 -08:00
Implement two-level (indirect) Device Table support to increase the
range of DeviceIDs we can set up for translation. Peripherals capable of doing DMA/MSIs are supposed to show up with unique DeviceIDs. The Device Table maps the DeviceID to a Interrupt Translation Table. So far we only used a single contiguous block for the Device Table, but on some machines this does not cover the whole range of physical devices. Using the GIC's indirect mode allows to move to a two-level setup to increase the range. ok kettenis@
This commit is contained in:
parent
18d3f3c5c3
commit
7afc621a64
@ -1,4 +1,4 @@
|
||||
/* $OpenBSD: agintc.c,v 1.60 2024/12/07 20:48:32 kettenis Exp $ */
|
||||
/* $OpenBSD: agintc.c,v 1.61 2024/12/07 21:12:22 patrick Exp $ */
|
||||
/*
|
||||
* Copyright (c) 2007, 2009, 2011, 2017 Dale Rahn <drahn@dalerahn.com>
|
||||
* Copyright (c) 2018 Mark Kettenis <kettenis@openbsd.org>
|
||||
@ -1577,6 +1577,7 @@ struct agintc_msi_softc {
|
||||
struct agintc_dmamem *sc_dtt;
|
||||
size_t sc_dtt_pgsz;
|
||||
uint8_t sc_dte_sz;
|
||||
int sc_dtt_indirect;
|
||||
int sc_cidbits;
|
||||
struct agintc_dmamem *sc_ctt;
|
||||
size_t sc_ctt_pgsz;
|
||||
@ -1721,11 +1722,19 @@ agintc_msi_attach(struct device *parent, struct device *self, void *aux)
|
||||
size = (1ULL << sc->sc_devbits) * sc->sc_dte_sz;
|
||||
size = roundup(size, sc->sc_dtt_pgsz);
|
||||
|
||||
/* FIXME: For now, skip registering MSI controller */
|
||||
if (size / sc->sc_dtt_pgsz > GITS_BASER_SZ_MASK + 1) {
|
||||
printf(": cannot support %u devbits on %lu pgsz\n",
|
||||
sc->sc_devbits, sc->sc_dtt_pgsz);
|
||||
return;
|
||||
/* Might make sense to go indirect */
|
||||
if (size > 2 * sc->sc_dtt_pgsz) {
|
||||
bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
|
||||
baser | GITS_BASER_INDIRECT);
|
||||
if (bus_space_read_8(sc->sc_iot, sc->sc_ioh,
|
||||
GITS_BASER(i)) & GITS_BASER_INDIRECT)
|
||||
sc->sc_dtt_indirect = 1;
|
||||
}
|
||||
if (sc->sc_dtt_indirect) {
|
||||
size = (1ULL << sc->sc_devbits);
|
||||
size /= (sc->sc_dtt_pgsz / sc->sc_dte_sz);
|
||||
size *= sizeof(uint64_t);
|
||||
size = roundup(size, sc->sc_dtt_pgsz);
|
||||
}
|
||||
|
||||
/* Clamp down to maximum configurable num pages */
|
||||
@ -1734,6 +1743,9 @@ agintc_msi_attach(struct device *parent, struct device *self, void *aux)
|
||||
|
||||
/* Calculate max deviceid based off configured size */
|
||||
sc->sc_deviceid_max = (size / sc->sc_dte_sz) - 1;
|
||||
if (sc->sc_dtt_indirect)
|
||||
sc->sc_deviceid_max = ((size / sizeof(uint64_t)) *
|
||||
(sc->sc_dtt_pgsz / sc->sc_dte_sz)) - 1;
|
||||
|
||||
/* Allocate table. */
|
||||
sc->sc_dtt = agintc_dmamem_alloc(sc->sc_dmat,
|
||||
@ -1748,7 +1760,9 @@ agintc_msi_attach(struct device *parent, struct device *self, void *aux)
|
||||
KASSERT((dtt_pa & GITS_BASER_PA_MASK) == dtt_pa);
|
||||
bus_space_write_8(sc->sc_iot, sc->sc_ioh, GITS_BASER(i),
|
||||
GITS_BASER_IC_NORM_NC | baser & GITS_BASER_PGSZ_MASK |
|
||||
dtt_pa | (size / sc->sc_dtt_pgsz) - 1 | GITS_BASER_VALID);
|
||||
dtt_pa | (size / sc->sc_dtt_pgsz) - 1 |
|
||||
(sc->sc_dtt_indirect ? GITS_BASER_INDIRECT : 0) |
|
||||
GITS_BASER_VALID);
|
||||
}
|
||||
|
||||
/* Set up collection translation table. */
|
||||
@ -1884,6 +1898,40 @@ agintc_msi_wait_cmd(struct agintc_msi_softc *sc)
|
||||
printf("%s: command queue timeout\n", sc->sc_dev.dv_xname);
|
||||
}
|
||||
|
||||
int
|
||||
agintc_msi_create_device_table(struct agintc_msi_softc *sc, uint32_t deviceid)
|
||||
{
|
||||
uint64_t *table = AGINTC_DMA_KVA(sc->sc_dtt);
|
||||
uint32_t idx = deviceid / (sc->sc_dtt_pgsz / sc->sc_dte_sz);
|
||||
struct agintc_dmamem *dtt;
|
||||
paddr_t dtt_pa;
|
||||
|
||||
/* Out of bounds */
|
||||
if (deviceid > sc->sc_deviceid_max)
|
||||
return ENXIO;
|
||||
|
||||
/* No need to adjust */
|
||||
if (!sc->sc_dtt_indirect)
|
||||
return 0;
|
||||
|
||||
/* Table already allocated */
|
||||
if (table[idx])
|
||||
return 0;
|
||||
|
||||
/* FIXME: leaks */
|
||||
dtt = agintc_dmamem_alloc(sc->sc_dmat,
|
||||
sc->sc_dtt_pgsz, sc->sc_dtt_pgsz);
|
||||
if (dtt == NULL)
|
||||
return ENOMEM;
|
||||
|
||||
dtt_pa = AGINTC_DMA_DVA(dtt);
|
||||
KASSERT((dtt_pa & GITS_BASER_PA_MASK) == dtt_pa);
|
||||
table[idx] = dtt_pa | GITS_BASER_VALID;
|
||||
cpu_dcache_wb_range((vaddr_t)&table[idx], sizeof(table[idx]));
|
||||
__asm volatile("dsb sy");
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct agintc_msi_device *
|
||||
agintc_msi_create_device(struct agintc_msi_softc *sc, uint32_t deviceid)
|
||||
{
|
||||
@ -1893,6 +1941,9 @@ agintc_msi_create_device(struct agintc_msi_softc *sc, uint32_t deviceid)
|
||||
if (deviceid > sc->sc_deviceid_max)
|
||||
return NULL;
|
||||
|
||||
if (agintc_msi_create_device_table(sc, deviceid) != 0)
|
||||
return NULL;
|
||||
|
||||
md = malloc(sizeof(*md), M_DEVBUF, M_ZERO | M_WAITOK);
|
||||
md->md_deviceid = deviceid;
|
||||
md->md_itt = agintc_dmamem_alloc(sc->sc_dmat,
|
||||
|
Loading…
Reference in New Issue
Block a user