Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/dpaa2/dpaa2_mc.c
- This file was added.
/*- | |||||
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||||
* | |||||
* Copyright © 2021-2022 Dmitry Salychev | |||||
* | |||||
* 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$"); | |||||
/* | |||||
* The DPAA2 Management Complex (MC) bus driver. | |||||
* | |||||
* MC is a hardware resource manager which can be found in several NXP | |||||
* SoCs (LX2160A, for example) and provides an access to the specialized | |||||
* hardware objects used in network-oriented packet processing applications. | |||||
*/ | |||||
#include <sys/param.h> | |||||
#include <sys/kernel.h> | |||||
#include <sys/bus.h> | |||||
#include <sys/rman.h> | |||||
#include <sys/module.h> | |||||
#include <sys/malloc.h> | |||||
#include <sys/mutex.h> | |||||
#include <sys/queue.h> | |||||
#include <vm/vm.h> | |||||
#include <machine/bus.h> | |||||
#include <machine/resource.h> | |||||
#include <contrib/dev/acpica/include/acpi.h> | |||||
#include <dev/acpica/acpivar.h> | |||||
#include <dev/ofw/openfirm.h> | |||||
#include <dev/ofw/ofw_bus.h> | |||||
#include <dev/ofw/ofw_bus_subr.h> | |||||
#include <dev/ofw/ofw_pci.h> | |||||
#include "pcib_if.h" | |||||
#include "pci_if.h" | |||||
#include "dpaa2_mc.h" | |||||
/* Macros to read/write MC registers */ | |||||
#define mcreg_read_4(_sc, _r) bus_read_4(&(_sc)->map[1], (_r)) | |||||
#define mcreg_write_4(_sc, _r, _v) bus_write_4(&(_sc)->map[1], (_r), (_v)) | |||||
#define COMPARE_TYPE(t, v) (strncmp((v), (t), strlen((v))) == 0) | |||||
#define IORT_DEVICE_NAME "MCE" | |||||
/* MC Registers */ | |||||
#define MC_REG_GCR1 0x0000u | |||||
#define MC_REG_GCR2 0x0004u /* TODO: Does it exist? */ | |||||
#define MC_REG_GSR 0x0008u | |||||
#define MC_REG_FAPR 0x0028u | |||||
/* General Control Register 1 (GCR1) */ | |||||
#define GCR1_P1_STOP 0x80000000u | |||||
#define GCR1_P2_STOP 0x40000000u | |||||
/* General Status Register (GSR) */ | |||||
#define GSR_HW_ERR(v) (((v) & 0x80000000u) >> 31) | |||||
#define GSR_CAT_ERR(v) (((v) & 0x40000000u) >> 30) | |||||
#define GSR_DPL_OFFSET(v) (((v) & 0x3FFFFF00u) >> 8) | |||||
#define GSR_MCS(v) (((v) & 0xFFu) >> 0) | |||||
/* Timeouts to wait for the MC status. */ | |||||
#define MC_STAT_TIMEOUT 1000u /* us */ | |||||
#define MC_STAT_ATTEMPTS 100u | |||||
/** | |||||
* @brief Structure to describe a DPAA2 device as a managed resource. | |||||
*/ | |||||
struct dpaa2_mc_devinfo { | |||||
STAILQ_ENTRY(dpaa2_mc_devinfo) link; | |||||
device_t dpaa2_dev; | |||||
uint32_t flags; | |||||
uint32_t owners; | |||||
}; | |||||
MALLOC_DEFINE(M_DPAA2_MC, "dpaa2_mc", "DPAA2 Management Complex"); | |||||
static struct resource_spec dpaa2_mc_spec[] = { | |||||
{ SYS_RES_MEMORY, 0, RF_ACTIVE | RF_UNMAPPED }, | |||||
{ SYS_RES_MEMORY, 1, RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL }, | |||||
RESOURCE_SPEC_END | |||||
}; | |||||
static u_int dpaa2_mc_get_xref(device_t, device_t); | |||||
static u_int dpaa2_mc_map_id(device_t, device_t, uintptr_t *); | |||||
static struct rman *dpaa2_mc_rman(device_t, int); | |||||
static int dpaa2_mc_alloc_msi_impl(device_t, device_t, int, int, int *); | |||||
static int dpaa2_mc_release_msi_impl(device_t, device_t, int, int *); | |||||
static int dpaa2_mc_map_msi_impl(device_t, device_t, int, uint64_t *, | |||||
uint32_t *); | |||||
/* | |||||
* For device interface. | |||||
*/ | |||||
int | |||||
dpaa2_mc_attach(device_t dev) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct resource_map_request req; | |||||
uint32_t val; | |||||
int error; | |||||
sc = device_get_softc(dev); | |||||
sc->dev = dev; | |||||
sc->msi_allocated = false; | |||||
sc->msi_owner = NULL; | |||||
error = bus_alloc_resources(sc->dev, dpaa2_mc_spec, sc->res); | |||||
if (error) { | |||||
device_printf(dev, "%s: failed to allocate resources\n", | |||||
__func__); | |||||
return (ENXIO); | |||||
} | |||||
if (sc->res[1]) { | |||||
resource_init_map_request(&req); | |||||
req.memattr = VM_MEMATTR_DEVICE; | |||||
error = bus_map_resource(sc->dev, SYS_RES_MEMORY, sc->res[1], | |||||
&req, &sc->map[1]); | |||||
if (error) { | |||||
device_printf(dev, "%s: failed to map control " | |||||
"registers\n", __func__); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
if (bootverbose) | |||||
device_printf(dev, | |||||
"GCR1=0x%x, GCR2=0x%x, GSR=0x%x, FAPR=0x%x\n", | |||||
mcreg_read_4(sc, MC_REG_GCR1), | |||||
mcreg_read_4(sc, MC_REG_GCR2), | |||||
mcreg_read_4(sc, MC_REG_GSR), | |||||
mcreg_read_4(sc, MC_REG_FAPR)); | |||||
/* Reset P1_STOP and P2_STOP bits to resume MC processor. */ | |||||
val = mcreg_read_4(sc, MC_REG_GCR1) & | |||||
~(GCR1_P1_STOP | GCR1_P2_STOP); | |||||
mcreg_write_4(sc, MC_REG_GCR1, val); | |||||
/* Poll MC status. */ | |||||
if (bootverbose) | |||||
device_printf(dev, "polling MC status...\n"); | |||||
for (int i = 0; i < MC_STAT_ATTEMPTS; i++) { | |||||
val = mcreg_read_4(sc, MC_REG_GSR); | |||||
if (GSR_MCS(val) != 0u) | |||||
break; | |||||
DELAY(MC_STAT_TIMEOUT); | |||||
} | |||||
if (bootverbose) | |||||
device_printf(dev, | |||||
"GCR1=0x%x, GCR2=0x%x, GSR=0x%x, FAPR=0x%x\n", | |||||
mcreg_read_4(sc, MC_REG_GCR1), | |||||
mcreg_read_4(sc, MC_REG_GCR2), | |||||
mcreg_read_4(sc, MC_REG_GSR), | |||||
mcreg_read_4(sc, MC_REG_FAPR)); | |||||
} | |||||
/* At least 64 bytes of the command portal should be available. */ | |||||
if (rman_get_size(sc->res[0]) < DPAA2_MCP_MEM_WIDTH) { | |||||
device_printf(dev, "%s: MC portal memory region too small: " | |||||
"%jd\n", __func__, rman_get_size(sc->res[0])); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
/* Map MC portal memory resource. */ | |||||
resource_init_map_request(&req); | |||||
req.memattr = VM_MEMATTR_DEVICE; | |||||
error = bus_map_resource(sc->dev, SYS_RES_MEMORY, sc->res[0], | |||||
&req, &sc->map[0]); | |||||
if (error) { | |||||
device_printf(dev, "Failed to map MC portal memory\n"); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
/* Initialize a resource manager for the DPAA2 I/O objects. */ | |||||
sc->dpio_rman.rm_type = RMAN_ARRAY; | |||||
sc->dpio_rman.rm_descr = "DPAA2 DPIO objects"; | |||||
error = rman_init(&sc->dpio_rman); | |||||
if (error) { | |||||
device_printf(dev, "Failed to initialize a resource manager for " | |||||
"the DPAA2 I/O objects: error=%d\n", error); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
/* Initialize a resource manager for the DPAA2 buffer pools. */ | |||||
sc->dpbp_rman.rm_type = RMAN_ARRAY; | |||||
sc->dpbp_rman.rm_descr = "DPAA2 DPBP objects"; | |||||
error = rman_init(&sc->dpbp_rman); | |||||
if (error) { | |||||
device_printf(dev, "Failed to initialize a resource manager for " | |||||
"the DPAA2 buffer pools: error=%d\n", error); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
/* Initialize a resource manager for the DPAA2 concentrators. */ | |||||
sc->dpcon_rman.rm_type = RMAN_ARRAY; | |||||
sc->dpcon_rman.rm_descr = "DPAA2 DPCON objects"; | |||||
error = rman_init(&sc->dpcon_rman); | |||||
if (error) { | |||||
device_printf(dev, "Failed to initialize a resource manager for " | |||||
"the DPAA2 concentrators: error=%d\n", error); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
/* Initialize a resource manager for the DPAA2 MC portals. */ | |||||
sc->dpmcp_rman.rm_type = RMAN_ARRAY; | |||||
sc->dpmcp_rman.rm_descr = "DPAA2 DPMCP objects"; | |||||
error = rman_init(&sc->dpmcp_rman); | |||||
if (error) { | |||||
device_printf(dev, "Failed to initialize a resource manager for " | |||||
"the DPAA2 MC portals: error=%d\n", error); | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
/* Initialize a list of non-allocatable DPAA2 devices. */ | |||||
mtx_init(&sc->mdev_lock, "MC portal mdev lock", NULL, MTX_DEF); | |||||
STAILQ_INIT(&sc->mdev_list); | |||||
mtx_init(&sc->msi_lock, "MC MSI lock", NULL, MTX_DEF); | |||||
/* | |||||
* Add a root resource container as the only child of the bus. All of | |||||
* the direct descendant containers will be attached to the root one | |||||
* instead of the MC device. | |||||
*/ | |||||
sc->rcdev = device_add_child(dev, "dpaa2_rc", 0); | |||||
if (sc->rcdev == NULL) { | |||||
dpaa2_mc_detach(dev); | |||||
return (ENXIO); | |||||
} | |||||
bus_generic_probe(dev); | |||||
bus_generic_attach(dev); | |||||
return (0); | |||||
} | |||||
int | |||||
dpaa2_mc_detach(device_t dev) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct dpaa2_devinfo *dinfo = NULL; | |||||
int error; | |||||
bus_generic_detach(dev); | |||||
sc = device_get_softc(dev); | |||||
if (sc->rcdev) | |||||
device_delete_child(dev, sc->rcdev); | |||||
bus_release_resources(dev, dpaa2_mc_spec, sc->res); | |||||
dinfo = device_get_ivars(dev); | |||||
if (dinfo) | |||||
free(dinfo, M_DPAA2_MC); | |||||
error = bus_generic_detach(dev); | |||||
if (error != 0) | |||||
return (error); | |||||
return (device_delete_children(dev)); | |||||
} | |||||
/* | |||||
* For bus interface. | |||||
*/ | |||||
struct resource * | |||||
dpaa2_mc_alloc_resource(device_t mcdev, device_t child, int type, int *rid, | |||||
rman_res_t start, rman_res_t end, rman_res_t count, u_int flags) | |||||
{ | |||||
struct resource *res; | |||||
struct rman *rm; | |||||
int error; | |||||
rm = dpaa2_mc_rman(mcdev, type); | |||||
if (!rm) | |||||
return (BUS_ALLOC_RESOURCE(device_get_parent(mcdev), child, | |||||
type, rid, start, end, count, flags)); | |||||
/* | |||||
* Skip managing DPAA2-specific resource. It must be provided to MC by | |||||
* calling DPAA2_MC_MANAGE_DEV() beforehand. | |||||
*/ | |||||
if (type <= DPAA2_DEV_MC) { | |||||
error = rman_manage_region(rm, start, end); | |||||
if (error) { | |||||
device_printf(mcdev, "rman_manage_region() failed: " | |||||
"start=%#jx, end=%#jx, error=%d\n", start, end, | |||||
error); | |||||
goto fail; | |||||
} | |||||
} | |||||
res = rman_reserve_resource(rm, start, end, count, flags, child); | |||||
if (!res) { | |||||
device_printf(mcdev, "rman_reserve_resource() failed: " | |||||
"start=%#jx, end=%#jx, count=%#jx\n", start, end, count); | |||||
goto fail; | |||||
} | |||||
rman_set_rid(res, *rid); | |||||
if (flags & RF_ACTIVE) { | |||||
if (bus_activate_resource(child, type, *rid, res)) { | |||||
device_printf(mcdev, "bus_activate_resource() failed: " | |||||
"rid=%d, res=%#jx\n", *rid, (uintmax_t) res); | |||||
rman_release_resource(res); | |||||
goto fail; | |||||
} | |||||
} | |||||
return (res); | |||||
fail: | |||||
device_printf(mcdev, "%s() failed: type=%d, rid=%d, start=%#jx, " | |||||
"end=%#jx, count=%#jx, flags=%x\n", __func__, type, *rid, start, end, | |||||
count, flags); | |||||
return (NULL); | |||||
} | |||||
int | |||||
dpaa2_mc_adjust_resource(device_t mcdev, device_t child, int type, | |||||
struct resource *r, rman_res_t start, rman_res_t end) | |||||
{ | |||||
struct rman *rm; | |||||
rm = dpaa2_mc_rman(mcdev, type); | |||||
if (rm) | |||||
return (rman_adjust_resource(r, start, end)); | |||||
return (bus_generic_adjust_resource(mcdev, child, type, r, start, end)); | |||||
} | |||||
int | |||||
dpaa2_mc_release_resource(device_t mcdev, device_t child, int type, int rid, | |||||
struct resource *r) | |||||
{ | |||||
struct rman *rm; | |||||
rm = dpaa2_mc_rman(mcdev, type); | |||||
if (rm) { | |||||
KASSERT(rman_is_region_manager(r, rm), ("rman mismatch")); | |||||
rman_release_resource(r); | |||||
} | |||||
return (bus_generic_release_resource(mcdev, child, type, rid, r)); | |||||
} | |||||
int | |||||
dpaa2_mc_activate_resource(device_t mcdev, device_t child, int type, int rid, | |||||
struct resource *r) | |||||
{ | |||||
int rc; | |||||
if ((rc = rman_activate_resource(r)) != 0) | |||||
return (rc); | |||||
return (BUS_ACTIVATE_RESOURCE(device_get_parent(mcdev), child, type, | |||||
rid, r)); | |||||
} | |||||
int | |||||
dpaa2_mc_deactivate_resource(device_t mcdev, device_t child, int type, int rid, | |||||
struct resource *r) | |||||
{ | |||||
int rc; | |||||
if ((rc = rman_deactivate_resource(r)) != 0) | |||||
return (rc); | |||||
return (BUS_DEACTIVATE_RESOURCE(device_get_parent(mcdev), child, type, | |||||
rid, r)); | |||||
} | |||||
/* | |||||
* For pseudo-pcib interface. | |||||
*/ | |||||
int | |||||
dpaa2_mc_alloc_msi(device_t mcdev, device_t child, int count, int maxcount, | |||||
int *irqs) | |||||
{ | |||||
#if defined(INTRNG) | |||||
return (dpaa2_mc_alloc_msi_impl(mcdev, child, count, maxcount, irqs)); | |||||
#else | |||||
return (ENXIO); | |||||
#endif | |||||
} | |||||
int | |||||
dpaa2_mc_release_msi(device_t mcdev, device_t child, int count, int *irqs) | |||||
{ | |||||
#if defined(INTRNG) | |||||
return (dpaa2_mc_release_msi_impl(mcdev, child, count, irqs)); | |||||
#else | |||||
return (ENXIO); | |||||
#endif | |||||
} | |||||
int | |||||
dpaa2_mc_map_msi(device_t mcdev, device_t child, int irq, uint64_t *addr, | |||||
uint32_t *data) | |||||
{ | |||||
#if defined(INTRNG) | |||||
return (dpaa2_mc_map_msi_impl(mcdev, child, irq, addr, data)); | |||||
#else | |||||
return (ENXIO); | |||||
#endif | |||||
} | |||||
int | |||||
dpaa2_mc_get_id(device_t mcdev, device_t child, enum pci_id_type type, | |||||
uintptr_t *id) | |||||
{ | |||||
struct dpaa2_devinfo *dinfo; | |||||
dinfo = device_get_ivars(child); | |||||
if (strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (ENXIO); | |||||
if (type == PCI_ID_MSI) | |||||
return (dpaa2_mc_map_id(mcdev, child, id)); | |||||
*id = dinfo->icid; | |||||
return (0); | |||||
} | |||||
/* | |||||
* For DPAA2 Management Complex bus driver interface. | |||||
*/ | |||||
int | |||||
dpaa2_mc_manage_dev(device_t mcdev, device_t dpaa2_dev, uint32_t flags) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct dpaa2_devinfo *dinfo; | |||||
struct dpaa2_mc_devinfo *di; | |||||
struct rman *rm; | |||||
int error; | |||||
sc = device_get_softc(mcdev); | |||||
dinfo = device_get_ivars(dpaa2_dev); | |||||
if (!sc || !dinfo || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (EINVAL); | |||||
di = malloc(sizeof(*di), M_DPAA2_MC, M_WAITOK | M_ZERO); | |||||
if (!di) | |||||
return (ENOMEM); | |||||
di->dpaa2_dev = dpaa2_dev; | |||||
di->flags = flags; | |||||
di->owners = 0; | |||||
/* Append a new managed DPAA2 device to the queue. */ | |||||
mtx_assert(&sc->mdev_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->mdev_lock); | |||||
STAILQ_INSERT_TAIL(&sc->mdev_list, di, link); | |||||
mtx_unlock(&sc->mdev_lock); | |||||
if (flags & DPAA2_MC_DEV_ALLOCATABLE) { | |||||
/* Select rman based on a type of the DPAA2 device. */ | |||||
rm = dpaa2_mc_rman(mcdev, dinfo->dtype); | |||||
if (!rm) | |||||
return (ENOENT); | |||||
/* Manage DPAA2 device as an allocatable resource. */ | |||||
error = rman_manage_region(rm, (rman_res_t) dpaa2_dev, | |||||
(rman_res_t) dpaa2_dev); | |||||
if (error) | |||||
return (error); | |||||
} | |||||
return (0); | |||||
} | |||||
int | |||||
dpaa2_mc_get_free_dev(device_t mcdev, device_t *dpaa2_dev, | |||||
enum dpaa2_dev_type devtype) | |||||
{ | |||||
struct rman *rm; | |||||
rman_res_t start, end; | |||||
int error; | |||||
if (strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (EINVAL); | |||||
/* Select resource manager based on a type of the DPAA2 device. */ | |||||
rm = dpaa2_mc_rman(mcdev, devtype); | |||||
if (!rm) | |||||
return (ENOENT); | |||||
/* Find first free DPAA2 device of the given type. */ | |||||
error = rman_first_free_region(rm, &start, &end); | |||||
if (error) | |||||
return (error); | |||||
KASSERT(start == end, ("start != end, but should be the same pointer " | |||||
"to the DPAA2 device: start=%jx, end=%jx", start, end)); | |||||
*dpaa2_dev = (device_t) start; | |||||
return (0); | |||||
} | |||||
int | |||||
dpaa2_mc_get_dev(device_t mcdev, device_t *dpaa2_dev, | |||||
enum dpaa2_dev_type devtype, uint32_t obj_id) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct dpaa2_devinfo *dinfo; | |||||
struct dpaa2_mc_devinfo *di; | |||||
int error = ENOENT; | |||||
sc = device_get_softc(mcdev); | |||||
if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (EINVAL); | |||||
mtx_assert(&sc->mdev_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->mdev_lock); | |||||
STAILQ_FOREACH(di, &sc->mdev_list, link) { | |||||
dinfo = device_get_ivars(di->dpaa2_dev); | |||||
if (dinfo->dtype == devtype && dinfo->id == obj_id) { | |||||
*dpaa2_dev = di->dpaa2_dev; | |||||
error = 0; | |||||
break; | |||||
} | |||||
} | |||||
mtx_unlock(&sc->mdev_lock); | |||||
return (error); | |||||
} | |||||
int | |||||
dpaa2_mc_get_shared_dev(device_t mcdev, device_t *dpaa2_dev, | |||||
enum dpaa2_dev_type devtype) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct dpaa2_devinfo *dinfo; | |||||
struct dpaa2_mc_devinfo *di; | |||||
device_t dev = NULL; | |||||
uint32_t owners = UINT32_MAX; | |||||
int error = ENOENT; | |||||
sc = device_get_softc(mcdev); | |||||
if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (EINVAL); | |||||
mtx_assert(&sc->mdev_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->mdev_lock); | |||||
STAILQ_FOREACH(di, &sc->mdev_list, link) { | |||||
dinfo = device_get_ivars(di->dpaa2_dev); | |||||
if ((dinfo->dtype == devtype) && | |||||
(di->flags & DPAA2_MC_DEV_SHAREABLE) && | |||||
(di->owners < owners)) { | |||||
dev = di->dpaa2_dev; | |||||
owners = di->owners; | |||||
} | |||||
} | |||||
if (dev) { | |||||
*dpaa2_dev = dev; | |||||
error = 0; | |||||
} | |||||
mtx_unlock(&sc->mdev_lock); | |||||
return (error); | |||||
} | |||||
int | |||||
dpaa2_mc_reserve_dev(device_t mcdev, device_t dpaa2_dev, | |||||
enum dpaa2_dev_type devtype) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct dpaa2_mc_devinfo *di; | |||||
int error = ENOENT; | |||||
sc = device_get_softc(mcdev); | |||||
if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (EINVAL); | |||||
mtx_assert(&sc->mdev_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->mdev_lock); | |||||
STAILQ_FOREACH(di, &sc->mdev_list, link) { | |||||
if (di->dpaa2_dev == dpaa2_dev && | |||||
(di->flags & DPAA2_MC_DEV_SHAREABLE)) { | |||||
di->owners++; | |||||
error = 0; | |||||
break; | |||||
} | |||||
} | |||||
mtx_unlock(&sc->mdev_lock); | |||||
return (error); | |||||
} | |||||
int | |||||
dpaa2_mc_release_dev(device_t mcdev, device_t dpaa2_dev, | |||||
enum dpaa2_dev_type devtype) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
struct dpaa2_mc_devinfo *di; | |||||
int error = ENOENT; | |||||
sc = device_get_softc(mcdev); | |||||
if (!sc || strcmp(device_get_name(mcdev), "dpaa2_mc") != 0) | |||||
return (EINVAL); | |||||
mtx_assert(&sc->mdev_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->mdev_lock); | |||||
STAILQ_FOREACH(di, &sc->mdev_list, link) { | |||||
if (di->dpaa2_dev == dpaa2_dev && | |||||
(di->flags & DPAA2_MC_DEV_SHAREABLE)) { | |||||
di->owners -= di->owners > 0 ? 1 : 0; | |||||
error = 0; | |||||
break; | |||||
} | |||||
} | |||||
mtx_unlock(&sc->mdev_lock); | |||||
return (error); | |||||
} | |||||
/** | |||||
* @brief Convert DPAA2 device type to string. | |||||
*/ | |||||
const char * | |||||
dpaa2_ttos(enum dpaa2_dev_type type) | |||||
{ | |||||
switch (type) { | |||||
case DPAA2_DEV_MC: | |||||
return ("mc"); /* NOTE: to print as information only. */ | |||||
case DPAA2_DEV_RC: | |||||
return ("dprc"); | |||||
case DPAA2_DEV_IO: | |||||
return ("dpio"); | |||||
case DPAA2_DEV_NI: | |||||
return ("dpni"); | |||||
case DPAA2_DEV_MCP: | |||||
return ("dpmcp"); | |||||
case DPAA2_DEV_BP: | |||||
return ("dpbp"); | |||||
case DPAA2_DEV_CON: | |||||
return ("dpcon"); | |||||
case DPAA2_DEV_MAC: | |||||
return ("dpmac"); | |||||
case DPAA2_DEV_MUX: | |||||
return ("dpdmux"); | |||||
case DPAA2_DEV_SW: | |||||
return ("dpsw"); | |||||
default: | |||||
break; | |||||
} | |||||
return ("notype"); | |||||
} | |||||
/** | |||||
* @brief Convert string to DPAA2 device type. | |||||
*/ | |||||
enum dpaa2_dev_type | |||||
dpaa2_stot(const char *str) | |||||
{ | |||||
if (COMPARE_TYPE(str, "dprc")) { | |||||
return (DPAA2_DEV_RC); | |||||
} else if (COMPARE_TYPE(str, "dpio")) { | |||||
return (DPAA2_DEV_IO); | |||||
} else if (COMPARE_TYPE(str, "dpni")) { | |||||
return (DPAA2_DEV_NI); | |||||
} else if (COMPARE_TYPE(str, "dpmcp")) { | |||||
return (DPAA2_DEV_MCP); | |||||
} else if (COMPARE_TYPE(str, "dpbp")) { | |||||
return (DPAA2_DEV_BP); | |||||
} else if (COMPARE_TYPE(str, "dpcon")) { | |||||
return (DPAA2_DEV_CON); | |||||
} else if (COMPARE_TYPE(str, "dpmac")) { | |||||
return (DPAA2_DEV_MAC); | |||||
} else if (COMPARE_TYPE(str, "dpdmux")) { | |||||
return (DPAA2_DEV_MUX); | |||||
} else if (COMPARE_TYPE(str, "dpsw")) { | |||||
return (DPAA2_DEV_SW); | |||||
} | |||||
return (DPAA2_DEV_NOTYPE); | |||||
} | |||||
/** | |||||
* @internal | |||||
*/ | |||||
static u_int | |||||
dpaa2_mc_get_xref(device_t mcdev, device_t child) | |||||
{ | |||||
struct dpaa2_mc_softc *sc = device_get_softc(mcdev); | |||||
struct dpaa2_devinfo *dinfo = device_get_ivars(child); | |||||
phandle_t msi_parent; | |||||
u_int xref, devid; | |||||
int error; | |||||
if (sc && dinfo) { | |||||
if (sc->acpi_based) { | |||||
/* | |||||
* NOTE: The first named component from the IORT table | |||||
* with the given name (as a substring) will be used. | |||||
*/ | |||||
error = acpi_iort_map_named_msi(IORT_DEVICE_NAME, | |||||
dinfo->icid, &xref, &devid); | |||||
if (error) | |||||
return (0); | |||||
return (xref); | |||||
andrew: When are thee NULL? | |||||
} else { | |||||
/* FDT-based driver. */ | |||||
Not Done Inline ActionsThe normal way to handle acpi & fdt differences is to either create an interface (_if.m file) and implement as needed in each attachment or as an ivar. andrew: The normal way to handle acpi & fdt differences is to either create an interface (`_if.m` file)… | |||||
error = ofw_bus_msimap(sc->ofw_node, dinfo->icid, | |||||
&msi_parent, NULL); | |||||
if (error) | |||||
return (0); | |||||
return ((u_int) msi_parent); | |||||
} | |||||
} | |||||
return (0); | |||||
} | |||||
/** | |||||
* @internal | |||||
*/ | |||||
static u_int | |||||
dpaa2_mc_map_id(device_t mcdev, device_t child, uintptr_t *id) | |||||
{ | |||||
struct dpaa2_devinfo *dinfo; | |||||
u_int xref, devid; | |||||
int error; | |||||
dinfo = device_get_ivars(child); | |||||
if (dinfo) { | |||||
/* | |||||
* The first named components from IORT table with the given | |||||
* name (as a substring) will be used. | |||||
*/ | |||||
error = acpi_iort_map_named_msi(IORT_DEVICE_NAME, dinfo->icid, | |||||
&xref, &devid); | |||||
if (error == 0) | |||||
*id = devid; | |||||
else | |||||
*id = dinfo->icid; /* RID not in IORT, likely FW bug */ | |||||
return (0); | |||||
} | |||||
return (ENXIO); | |||||
} | |||||
/** | |||||
* @internal | |||||
* @brief Obtain a resource manager based on the given type of the resource. | |||||
*/ | |||||
static struct rman * | |||||
dpaa2_mc_rman(device_t mcdev, int type) | |||||
{ | |||||
struct dpaa2_mc_softc *sc; | |||||
sc = device_get_softc(mcdev); | |||||
switch (type) { | |||||
case DPAA2_DEV_IO: | |||||
return (&sc->dpio_rman); | |||||
case DPAA2_DEV_BP: | |||||
return (&sc->dpbp_rman); | |||||
case DPAA2_DEV_CON: | |||||
return (&sc->dpcon_rman); | |||||
case DPAA2_DEV_MCP: | |||||
return (&sc->dpmcp_rman); | |||||
default: | |||||
break; | |||||
} | |||||
return (NULL); | |||||
} | |||||
#if defined(INTRNG) && !defined(IOMMU) | |||||
/** | |||||
* @internal | |||||
* @brief Allocates requested number of MSIs. | |||||
* | |||||
* NOTE: This function is a part of fallback solution when IOMMU isn't available. | |||||
* Total number of IRQs is limited to 32. | |||||
*/ | |||||
static int | |||||
dpaa2_mc_alloc_msi_impl(device_t mcdev, device_t child, int count, int maxcount, | |||||
int *irqs) | |||||
{ | |||||
struct dpaa2_mc_softc *sc = device_get_softc(mcdev); | |||||
int msi_irqs[DPAA2_MC_MSI_COUNT]; | |||||
int error; | |||||
/* Pre-allocate a bunch of MSIs for MC to be used by its children. */ | |||||
if (!sc->msi_allocated) { | |||||
error = intr_alloc_msi(mcdev, child, dpaa2_mc_get_xref(mcdev, | |||||
child), DPAA2_MC_MSI_COUNT, DPAA2_MC_MSI_COUNT, msi_irqs); | |||||
if (error) { | |||||
device_printf(mcdev, "failed to pre-allocate %d MSIs: " | |||||
"error=%d\n", DPAA2_MC_MSI_COUNT, error); | |||||
return (error); | |||||
} | |||||
mtx_assert(&sc->msi_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->msi_lock); | |||||
for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { | |||||
sc->msi[i].child = NULL; | |||||
sc->msi[i].irq = msi_irqs[i]; | |||||
} | |||||
sc->msi_owner = child; | |||||
sc->msi_allocated = true; | |||||
mtx_unlock(&sc->msi_lock); | |||||
} | |||||
error = ENOENT; | |||||
/* Find the first free MSIs from the pre-allocated pool. */ | |||||
mtx_assert(&sc->msi_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->msi_lock); | |||||
for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { | |||||
if (sc->msi[i].child != NULL) | |||||
continue; | |||||
error = 0; | |||||
for (int j = 0; j < count; j++) { | |||||
if (i + j >= DPAA2_MC_MSI_COUNT) { | |||||
device_printf(mcdev, "requested %d MSIs exceed " | |||||
"limit of %d available\n", count, | |||||
DPAA2_MC_MSI_COUNT); | |||||
error = E2BIG; | |||||
break; | |||||
} | |||||
sc->msi[i + j].child = child; | |||||
irqs[j] = sc->msi[i + j].irq; | |||||
} | |||||
break; | |||||
} | |||||
mtx_unlock(&sc->msi_lock); | |||||
return (error); | |||||
} | |||||
/** | |||||
* @internal | |||||
* @brief Marks IRQs as free in the pre-allocated pool of MSIs. | |||||
* | |||||
* NOTE: This function is a part of fallback solution when IOMMU isn't available. | |||||
* Total number of IRQs is limited to 32. | |||||
* NOTE: MSIs are kept allocated in the kernel as a part of the pool. | |||||
*/ | |||||
static int | |||||
dpaa2_mc_release_msi_impl(device_t mcdev, device_t child, int count, int *irqs) | |||||
{ | |||||
struct dpaa2_mc_softc *sc = device_get_softc(mcdev); | |||||
mtx_assert(&sc->msi_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->msi_lock); | |||||
for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { | |||||
if (sc->msi[i].child != child) | |||||
continue; | |||||
for (int j = 0; j < count; j++) { | |||||
if (sc->msi[i].irq == irqs[j]) { | |||||
sc->msi[i].child = NULL; | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
mtx_unlock(&sc->msi_lock); | |||||
return (0); | |||||
} | |||||
/** | |||||
* @internal | |||||
* @brief Provides address to write to and data according to the given MSI from | |||||
* the pre-allocated pool. | |||||
* | |||||
* NOTE: This function is a part of fallback solution when IOMMU isn't available. | |||||
* Total number of IRQs is limited to 32. | |||||
*/ | |||||
static int | |||||
dpaa2_mc_map_msi_impl(device_t mcdev, device_t child, int irq, uint64_t *addr, | |||||
uint32_t *data) | |||||
{ | |||||
struct dpaa2_mc_softc *sc = device_get_softc(mcdev); | |||||
int error = EINVAL; | |||||
mtx_assert(&sc->msi_lock, MA_NOTOWNED); | |||||
mtx_lock(&sc->msi_lock); | |||||
for (int i = 0; i < DPAA2_MC_MSI_COUNT; i++) { | |||||
if (sc->msi[i].child == child && sc->msi[i].irq == irq) { | |||||
error = 0; | |||||
break; | |||||
} | |||||
} | |||||
mtx_unlock(&sc->msi_lock); | |||||
if (error) | |||||
return (error); | |||||
return (intr_map_msi(mcdev, sc->msi_owner, dpaa2_mc_get_xref(mcdev, | |||||
sc->msi_owner), irq, addr, data)); | |||||
} | |||||
#endif /* defined(INTRNG) && !defined(IOMMU) */ | |||||
static device_method_t dpaa2_mc_methods[] = { | |||||
DEVMETHOD_END | |||||
}; | |||||
DEFINE_CLASS_0(dpaa2_mc, dpaa2_mc_driver, dpaa2_mc_methods, | |||||
sizeof(struct dpaa2_mc_softc)); |
When are thee NULL?