diff --git a/sys/dev/dpaa2/dpaa2_mc.c b/sys/dev/dpaa2/dpaa2_mc.c index 02470a3efd6e..a6b56be10432 100644 --- a/sys/dev/dpaa2/dpaa2_mc.c +++ b/sys/dev/dpaa2/dpaa2_mc.c @@ -1,906 +1,890 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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 /* * 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 "opt_acpi.h" #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEV_ACPI #include #include #endif #ifdef FDT #include #include #include #include #endif #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 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)); + rm = dpaa2_mc_rman(mcdev, type, flags); + if (rm == NULL) + return (bus_generic_alloc_resource(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); + res = bus_generic_rman_alloc_resource(mcdev, child, type, rid, start, + end, count, flags); + if (res == NULL) 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); + rm = dpaa2_mc_rman(mcdev, type, rman_get_flags(r)); if (rm) - return (rman_adjust_resource(r, start, end)); + return (bus_generic_rman_adjust_resource(mcdev, child, type, 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); - } - + rm = dpaa2_mc_rman(mcdev, type, rman_get_flags(r)); + if (rm) + return (bus_generic_rman_release_resource(mcdev, child, type, + rid, 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); + struct rman *rm; - return (BUS_ACTIVATE_RESOURCE(device_get_parent(mcdev), child, type, - rid, r)); + rm = dpaa2_mc_rman(mcdev, type, rman_get_flags(r)); + if (rm) + return (bus_generic_rman_activate_resource(mcdev, child, type, + rid, r)); + return (bus_generic_activate_resource(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); + struct rman *rm; - return (BUS_DEACTIVATE_RESOURCE(device_get_parent(mcdev), child, type, - rid, r)); + rm = dpaa2_mc_rman(mcdev, type, rman_get_flags(r)); + if (rm) + return (bus_generic_rman_deactivate_resource(mcdev, child, type, + rid, r)); + return (bus_generic_deactivate_resource(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); 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); + rm = dpaa2_mc_rman(mcdev, dinfo->dtype, 0); 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); + rm = dpaa2_mc_rman(mcdev, devtype, 0); 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); } /** * @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); #ifdef DEV_ACPI u_int xref, devid; #endif #ifdef FDT phandle_t msi_parent; #endif int error; if (sc && dinfo) { #ifdef DEV_ACPI 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); } #endif #ifdef FDT if (!sc->acpi_based) { /* FDT-based driver. */ error = ofw_bus_msimap(sc->ofw_node, dinfo->icid, &msi_parent, NULL); if (error) return (0); return ((u_int) msi_parent); } #endif } return (0); } /** * @internal */ static u_int dpaa2_mc_map_id(device_t mcdev, device_t child, uintptr_t *id) { struct dpaa2_devinfo *dinfo; #ifdef DEV_ACPI u_int xref, devid; int error; #endif dinfo = device_get_ivars(child); if (dinfo) { /* * The first named components from IORT table with the given * name (as a substring) will be used. */ #ifdef DEV_ACPI error = acpi_iort_map_named_msi(IORT_DEVICE_NAME, dinfo->icid, &xref, &devid); if (error == 0) *id = devid; else #endif *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 rman * +dpaa2_mc_rman(device_t mcdev, int type, u_int flags) { 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)); diff --git a/sys/dev/dpaa2/dpaa2_mc.h b/sys/dev/dpaa2/dpaa2_mc.h index 9a21c9724b82..40b318c4c9e7 100644 --- a/sys/dev/dpaa2/dpaa2_mc.h +++ b/sys/dev/dpaa2/dpaa2_mc.h @@ -1,218 +1,219 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * 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. */ #ifndef _DPAA2_MC_H #define _DPAA2_MC_H #include #include #include #include #include #include "pci_if.h" #include "dpaa2_types.h" #include "dpaa2_mcp.h" #include "dpaa2_swp.h" #include "dpaa2_ni.h" #include "dpaa2_io.h" #include "dpaa2_mac.h" #include "dpaa2_con.h" #include "dpaa2_bp.h" /* * Maximum number of MSIs supported by the MC for its children without IOMMU. * * TODO: Should be much more with IOMMU translation. */ #define DPAA2_MC_MSI_COUNT 32 /* Flags for DPAA2 devices as resources. */ #define DPAA2_MC_DEV_ALLOCATABLE 0x01u /* to be managed by DPAA2-specific rman */ #define DPAA2_MC_DEV_ASSOCIATED 0x02u /* to obtain info about DPAA2 device */ #define DPAA2_MC_DEV_SHAREABLE 0x04u /* to be shared among DPAA2 devices */ struct dpaa2_mc_devinfo; /* about managed DPAA2 devices */ /** * @brief Software context for the DPAA2 Management Complex (MC) driver. * * dev: Device associated with this software context. * rcdev: Child device associated with the root resource container. * acpi_based: Attached using ACPI (true) or FDT (false). * ofw_node: FDT node of the Management Complex (acpi_based == false). * * res: Unmapped MC command portal and control registers resources. * map: Mapped MC command portal and control registers resources. * * dpio_rman: I/O objects resource manager. * dpbp_rman: Buffer Pools resource manager. * dpcon_rman: Concentrators resource manager. * dpmcp_rman: MC portals resource manager. */ struct dpaa2_mc_softc { device_t dev; device_t rcdev; bool acpi_based; phandle_t ofw_node; struct resource *res[2]; struct resource_map map[2]; /* For allocatable managed DPAA2 objects. */ struct rman dpio_rman; struct rman dpbp_rman; struct rman dpcon_rman; struct rman dpmcp_rman; /* For managed DPAA2 objects. */ struct mtx mdev_lock; STAILQ_HEAD(, dpaa2_mc_devinfo) mdev_list; /* NOTE: Workaround in case of no IOMMU available. */ #ifndef IOMMU device_t msi_owner; bool msi_allocated; struct mtx msi_lock; struct { device_t child; int irq; } msi[DPAA2_MC_MSI_COUNT]; #endif }; /** * @brief Software context for the DPAA2 Resource Container (RC) driver. * * dev: Device associated with this software context. * portal: Helper object to send commands to the MC portal. * unit: Helps to distinguish between root (0) and child DRPCs. * cont_id: Container ID. */ struct dpaa2_rc_softc { device_t dev; int unit; uint32_t cont_id; }; /** * @brief Information about MSI messages supported by the DPAA2 object. * * msi_msgnum: Number of MSI messages supported by the DPAA2 object. * msi_alloc: Number of MSI messages allocated for the DPAA2 object. * msi_handlers: Number of MSI message handlers configured. */ struct dpaa2_msinfo { uint8_t msi_msgnum; uint8_t msi_alloc; uint32_t msi_handlers; }; /** * @brief Information about DPAA2 device. * * pdev: Parent device. * dev: Device this devinfo is associated with. * * id: ID of a logical DPAA2 object resource. * portal_id: ID of the MC portal which belongs to the object's container. * icid: Isolation context ID of the DPAA2 object. It is shared * between a resource container and all of its children. * * dtype: Type of the DPAA2 object. * resources: Resources available for this DPAA2 device. * msi: Information about MSI messages supported by the DPAA2 object. */ struct dpaa2_devinfo { device_t pdev; device_t dev; uint32_t id; uint32_t portal_id; uint32_t icid; enum dpaa2_dev_type dtype; struct resource_list resources; struct dpaa2_msinfo msi; /* * DPAA2 object might or might not have its own portal allocated to * execute MC commands. If the portal has been allocated, it takes * precedence over the portal owned by the resource container. */ struct dpaa2_mcp *portal; }; DECLARE_CLASS(dpaa2_mc_driver); /* For device interface. */ int dpaa2_mc_attach(device_t dev); int dpaa2_mc_detach(device_t dev); /* For bus interface. */ +struct rman *dpaa2_mc_rman(device_t mcdev, int type, u_int flags); 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); int dpaa2_mc_adjust_resource(device_t mcdev, device_t child, int type, struct resource *r, rman_res_t start, rman_res_t end); int dpaa2_mc_release_resource(device_t mcdev, device_t child, int type, int rid, struct resource *r); int dpaa2_mc_activate_resource(device_t mcdev, device_t child, int type, int rid, struct resource *r); int dpaa2_mc_deactivate_resource(device_t mcdev, device_t child, int type, int rid, struct resource *r); /* For pseudo-pcib interface. */ int dpaa2_mc_alloc_msi(device_t mcdev, device_t child, int count, int maxcount, int *irqs); int dpaa2_mc_release_msi(device_t mcdev, device_t child, int count, int *irqs); int dpaa2_mc_map_msi(device_t mcdev, device_t child, int irq, uint64_t *addr, uint32_t *data); int dpaa2_mc_get_id(device_t mcdev, device_t child, enum pci_id_type type, uintptr_t *id); /* For DPAA2 MC bus interface. */ int dpaa2_mc_manage_dev(device_t mcdev, device_t dpaa2_dev, uint32_t flags); int dpaa2_mc_get_free_dev(device_t mcdev, device_t *dpaa2_dev, enum dpaa2_dev_type devtype); int dpaa2_mc_get_dev(device_t mcdev, device_t *dpaa2_dev, enum dpaa2_dev_type devtype, uint32_t obj_id); int dpaa2_mc_get_shared_dev(device_t mcdev, device_t *dpaa2_dev, enum dpaa2_dev_type devtype); int dpaa2_mc_reserve_dev(device_t mcdev, device_t dpaa2_dev, enum dpaa2_dev_type devtype); int dpaa2_mc_release_dev(device_t mcdev, device_t dpaa2_dev, enum dpaa2_dev_type devtype); #endif /* _DPAA2_MC_H */ diff --git a/sys/dev/dpaa2/dpaa2_mc_acpi.c b/sys/dev/dpaa2/dpaa2_mc_acpi.c index 75cbfcb0791f..1042ea56d8cf 100644 --- a/sys/dev/dpaa2/dpaa2_mc_acpi.c +++ b/sys/dev/dpaa2/dpaa2_mc_acpi.c @@ -1,395 +1,396 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright © 2021-2022 Dmitry Salychev * Copyright © 2021 Bjoern A. Zeeb * * 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 /* * The DPAA2 Management Complex (MC) Bus Driver (ACPI-based). * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "acpi_bus_if.h" #include "pcib_if.h" #include "pci_if.h" #include "dpaa2_mcp.h" #include "dpaa2_mc.h" #include "dpaa2_mc_if.h" #define _COMPONENT ACPI_BUS ACPI_MODULE_NAME("DPAA2_MC") struct dpaa2_mac_dev_softc { int uid; uint64_t reg; char managed[64]; char phy_conn_type[64]; char phy_mode[64]; ACPI_HANDLE phy_channel; }; static int dpaa2_mac_dev_probe(device_t dev) { uint64_t reg; ssize_t s; s = device_get_property(dev, "reg", ®, sizeof(reg), DEVICE_PROP_UINT64); if (s == -1) return (ENXIO); device_set_desc(dev, "DPAA2 MAC DEV"); return (BUS_PROBE_DEFAULT); } static int dpaa2_mac_dev_attach(device_t dev) { struct dpaa2_mac_dev_softc *sc; ACPI_HANDLE h; ssize_t s; sc = device_get_softc(dev); h = acpi_get_handle(dev); if (h == NULL) return (ENXIO); s = acpi_GetInteger(h, "_UID", &sc->uid); if (ACPI_FAILURE(s)) { device_printf(dev, "Cannot find '_UID' property: %zd\n", s); return (ENXIO); } s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg), DEVICE_PROP_UINT64); if (s == -1) { device_printf(dev, "Cannot find 'reg' property: %zd\n", s); return (ENXIO); } s = device_get_property(dev, "managed", sc->managed, sizeof(sc->managed), DEVICE_PROP_ANY); s = device_get_property(dev, "phy-connection-type", sc->phy_conn_type, sizeof(sc->phy_conn_type), DEVICE_PROP_ANY); s = device_get_property(dev, "phy-mode", sc->phy_mode, sizeof(sc->phy_mode), DEVICE_PROP_ANY); s = device_get_property(dev, "phy-handle", &sc->phy_channel, sizeof(sc->phy_channel), DEVICE_PROP_HANDLE); if (bootverbose) device_printf(dev, "UID %#04x reg %#04jx managed '%s' " "phy-connection-type '%s' phy-mode '%s' phy-handle '%s'\n", sc->uid, sc->reg, sc->managed[0] != '\0' ? sc->managed : "", sc->phy_conn_type[0] != '\0' ? sc->phy_conn_type : "", sc->phy_mode[0] != '\0' ? sc->phy_mode : "", sc->phy_channel != NULL ? acpi_name(sc->phy_channel) : ""); return (0); } static bool dpaa2_mac_dev_match_id(device_t dev, uint32_t id) { struct dpaa2_mac_dev_softc *sc; if (dev == NULL) return (false); sc = device_get_softc(dev); if (sc->uid == id) return (true); return (false); } static device_t dpaa2_mac_dev_get_phy_dev(device_t dev) { struct dpaa2_mac_dev_softc *sc; if (dev == NULL) return (NULL); sc = device_get_softc(dev); if (sc->phy_channel == NULL) return (NULL); return (acpi_get_device(sc->phy_channel)); } static device_method_t dpaa2_mac_dev_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dpaa2_mac_dev_probe), DEVMETHOD(device_attach, dpaa2_mac_dev_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD_END }; DEFINE_CLASS_0(dpaa2_mac_dev, dpaa2_mac_dev_driver, dpaa2_mac_dev_methods, sizeof(struct dpaa2_mac_dev_softc)); DRIVER_MODULE(dpaa2_mac_dev, dpaa2_mc, dpaa2_mac_dev_driver, 0, 0); MODULE_DEPEND(dpaa2_mac_dev, memac_mdio_acpi, 1, 1, 1); /* * Device interface. */ static int dpaa2_mc_acpi_probe(device_t dev) { static char *dpaa2_mc_ids[] = { "NXP0008", NULL }; int rc; ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); rc = ACPI_ID_PROBE(device_get_parent(dev), dev, dpaa2_mc_ids, NULL); if (rc <= 0) device_set_desc(dev, "DPAA2 Management Complex"); return (rc); } /* Context for walking PRxx child devices. */ struct dpaa2_mc_acpi_prxx_walk_ctx { device_t dev; int count; int countok; }; static ACPI_STATUS dpaa2_mc_acpi_probe_child(ACPI_HANDLE h, device_t *dev, int level, void *arg) { struct dpaa2_mc_acpi_prxx_walk_ctx *ctx; struct acpi_device *ad; device_t child; uint32_t uid; ctx = (struct dpaa2_mc_acpi_prxx_walk_ctx *)arg; ctx->count++; #if 0 device_printf(ctx->dev, "%s: %s level %d count %d\n", __func__, acpi_name(h), level, ctx->count); #endif if (ACPI_FAILURE(acpi_GetInteger(h, "_UID", &uid))) return (AE_OK); #if 0 if (bootverbose) device_printf(ctx->dev, "%s: Found child Ports _UID %u\n", __func__, uid); #endif /* Technically M_ACPIDEV */ if ((ad = malloc(sizeof(*ad), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) return (AE_OK); child = device_add_child(ctx->dev, "dpaa2_mac_dev", -1); if (child == NULL) { free(ad, M_DEVBUF); return (AE_OK); } ad->ad_handle = h; ad->ad_cls_class = 0xffffff; resource_list_init(&ad->ad_rl); device_set_ivars(child, ad); *dev = child; ctx->countok++; return (AE_OK); } static int dpaa2_mc_acpi_attach(device_t dev) { struct dpaa2_mc_softc *sc; sc = device_get_softc(dev); sc->acpi_based = true; struct dpaa2_mc_acpi_prxx_walk_ctx ctx; ctx.dev = dev; ctx.count = 0; ctx.countok = 0; ACPI_SCAN_CHILDREN(device_get_parent(dev), dev, 2, dpaa2_mc_acpi_probe_child, &ctx); #if 0 device_printf(dev, "Found %d child Ports in ASL, %d ok\n", ctx.count, ctx.countok); #endif return (dpaa2_mc_attach(dev)); } /* * ACPI compat layer. */ static device_t dpaa2_mc_acpi_find_dpaa2_mac_dev(device_t dev, uint32_t id) { int devcount, error, i, len; device_t *devlist, mdev; const char *mdevname; error = device_get_children(dev, &devlist, &devcount); if (error != 0) return (NULL); for (i = 0; i < devcount; i++) { mdev = devlist[i]; mdevname = device_get_name(mdev); if (mdevname != NULL) { len = strlen(mdevname); if (strncmp("dpaa2_mac_dev", mdevname, len) != 0) continue; } else { continue; } if (!device_is_attached(mdev)) continue; if (dpaa2_mac_dev_match_id(mdev, id)) return (mdev); } return (NULL); } static int dpaa2_mc_acpi_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id) { device_t mdev, pdev; mdev = dpaa2_mc_acpi_find_dpaa2_mac_dev(dev, id); if (mdev == NULL) { device_printf(dev, "%s: error finding dpmac device with id=%u\n", __func__, id); return (ENXIO); } pdev = dpaa2_mac_dev_get_phy_dev(mdev); if (pdev == NULL) { device_printf(dev, "%s: error getting MDIO device for dpamc %s " "(id=%u)\n", __func__, device_get_nameunit(mdev), id); return (ENXIO); } if (phy_dev != NULL) *phy_dev = pdev; return (0); } static ssize_t dpaa2_mc_acpi_get_property(device_t dev, device_t child, const char *propname, void *propvalue, size_t size, device_property_type_t type) { return (bus_generic_get_property(dev, child, propname, propvalue, size, type)); } static int dpaa2_mc_acpi_read_ivar(device_t dev, device_t child, int index, uintptr_t *result) { /* * This is special in that it passes "child" as second argument rather * than "dev". acpi_get_handle() in dpaa2_mac_dev_attach() calls the * read on parent(dev), dev and gets us here not to ACPI. Hence we * need to keep child as-is and pass it to our parent which is ACPI. * Only that gives the desired result. */ return (BUS_READ_IVAR(device_get_parent(dev), child, index, result)); } static device_method_t dpaa2_mc_acpi_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dpaa2_mc_acpi_probe), DEVMETHOD(device_attach, dpaa2_mc_acpi_attach), DEVMETHOD(device_detach, dpaa2_mc_detach), /* Bus interface */ + DEVMETHOD(bus_get_rman, dpaa2_mc_rman), DEVMETHOD(bus_alloc_resource, dpaa2_mc_alloc_resource), DEVMETHOD(bus_adjust_resource, dpaa2_mc_adjust_resource), DEVMETHOD(bus_release_resource, dpaa2_mc_release_resource), DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource), DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* Pseudo-PCIB interface */ DEVMETHOD(pcib_alloc_msi, dpaa2_mc_alloc_msi), DEVMETHOD(pcib_release_msi, dpaa2_mc_release_msi), DEVMETHOD(pcib_map_msi, dpaa2_mc_map_msi), DEVMETHOD(pcib_get_id, dpaa2_mc_get_id), /* DPAA2 MC bus interface */ DEVMETHOD(dpaa2_mc_manage_dev, dpaa2_mc_manage_dev), DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev), DEVMETHOD(dpaa2_mc_get_dev, dpaa2_mc_get_dev), DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev), DEVMETHOD(dpaa2_mc_reserve_dev, dpaa2_mc_reserve_dev), DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev), DEVMETHOD(dpaa2_mc_get_phy_dev, dpaa2_mc_acpi_get_phy_dev), /* ACPI compar layer. */ DEVMETHOD(bus_read_ivar, dpaa2_mc_acpi_read_ivar), DEVMETHOD(bus_get_property, dpaa2_mc_acpi_get_property), DEVMETHOD_END }; DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_acpi_driver, dpaa2_mc_acpi_methods, sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver); /* Make sure miibus gets procesed first. */ DRIVER_MODULE_ORDERED(dpaa2_mc, acpi, dpaa2_mc_acpi_driver, NULL, NULL, SI_ORDER_ANY); MODULE_DEPEND(dpaa2_mc, memac_mdio_acpi, 1, 1, 1); diff --git a/sys/dev/dpaa2/dpaa2_mc_fdt.c b/sys/dev/dpaa2/dpaa2_mc_fdt.c index 8e9ab10dec9d..a6babfc89ca9 100644 --- a/sys/dev/dpaa2/dpaa2_mc_fdt.c +++ b/sys/dev/dpaa2/dpaa2_mc_fdt.c @@ -1,395 +1,396 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright © 2021-2022 Dmitry Salychev * Copyright © 2022 Bjoern A. Zeeb * * 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 /* * The DPAA2 Management Complex (MC) Bus Driver (FDT-based). * * 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 #include #include #include #include #include #include #include #include #include #include #include #include "pcib_if.h" #include "pci_if.h" #include "ofw_bus_if.h" #include "dpaa2_mcp.h" #include "dpaa2_mc.h" #include "dpaa2_mc_if.h" struct dpaa2_mac_fdt_softc { uint32_t reg; phandle_t sfp; phandle_t pcs_handle; phandle_t phy_handle; char managed[64]; char phy_conn_type[64]; }; #if 0 ethernet@1 { compatible = "fsl,qoriq-mc-dpmac"; reg = <0x1>; sfp = <0x14>; pcs-handle = <0x15>; phy-connection-type = "10gbase-r"; managed = "in-band-status"; }; ethernet@3 { compatible = "fsl,qoriq-mc-dpmac"; reg = <0x3>; phy-handle = <0x18>; phy-connection-type = "qsgmii"; managed = "in-band-status"; pcs-handle = <0x19>; }; #endif static int dpaa2_mac_dev_probe(device_t dev) { phandle_t node; uint64_t reg; ssize_t s; node = ofw_bus_get_node(dev); if (!ofw_bus_node_is_compatible(node, "fsl,qoriq-mc-dpmac")) { device_printf(dev, "'%s' not fsl,qoriq-mc-dpmac compatible\n", ofw_bus_get_name(dev)); return (ENXIO); } s = device_get_property(dev, "reg", ®, sizeof(reg), DEVICE_PROP_UINT32); if (s == -1) { device_printf(dev, "%s: '%s' has no 'reg' property, s %zd\n", __func__, ofw_bus_get_name(dev), s); return (ENXIO); } device_set_desc(dev, "DPAA2 MAC DEV"); return (BUS_PROBE_DEFAULT); } static int dpaa2_mac_fdt_attach(device_t dev) { struct dpaa2_mac_fdt_softc *sc; phandle_t node; ssize_t s; sc = device_get_softc(dev); node = ofw_bus_get_node(dev); s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg), DEVICE_PROP_UINT32); if (s == -1) { device_printf(dev, "Cannot find 'reg' property: %zd\n", s); return (ENXIO); } s = device_get_property(dev, "managed", sc->managed, sizeof(sc->managed), DEVICE_PROP_ANY); s = device_get_property(dev, "phy-connection-type", sc->phy_conn_type, sizeof(sc->phy_conn_type), DEVICE_PROP_ANY); s = device_get_property(dev, "pcs-handle", &sc->pcs_handle, sizeof(sc->pcs_handle), DEVICE_PROP_HANDLE); /* 'sfp' and 'phy-handle' are optional but we need one or the other. */ s = device_get_property(dev, "sfp", &sc->sfp, sizeof(sc->sfp), DEVICE_PROP_HANDLE); s = device_get_property(dev, "phy-handle", &sc->phy_handle, sizeof(sc->phy_handle), DEVICE_PROP_HANDLE); if (bootverbose) device_printf(dev, "node %#x '%s': reg %#x sfp %#x pcs-handle " "%#x phy-handle %#x managed '%s' phy-conn-type '%s'\n", node, ofw_bus_get_name(dev), sc->reg, sc->sfp, sc->pcs_handle, sc->phy_handle, sc->managed, sc->phy_conn_type); return (0); } static bool dpaa2_mac_fdt_match_id(device_t dev, uint32_t id) { struct dpaa2_mac_fdt_softc *sc; if (dev == NULL) return (false); sc = device_get_softc(dev); if (sc->reg == id) return (true); return (false); } static device_t dpaa2_mac_fdt_get_phy_dev(device_t dev) { struct dpaa2_mac_fdt_softc *sc; if (dev == NULL) return (NULL); sc = device_get_softc(dev); if (sc->phy_handle == 0 && sc->sfp == 0) return (NULL); #ifdef __not_yet__ /* No sff,sfp support yet. */ if (sc->sfp != 0) { device_t xdev; xdev = OF_device_from_xref(OF_xref_from_node(sc->sfp)); if (xdev != NULL) return (xdev); } #endif return (OF_device_from_xref(OF_xref_from_node(sc->phy_handle))); } static device_method_t dpaa2_mac_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dpaa2_mac_dev_probe), DEVMETHOD(device_attach, dpaa2_mac_fdt_attach), DEVMETHOD(device_detach, bus_generic_detach), DEVMETHOD_END }; DEFINE_CLASS_0(dpaa2_mac_fdt, dpaa2_mac_fdt_driver, dpaa2_mac_fdt_methods, sizeof(struct dpaa2_mac_fdt_softc)); DRIVER_MODULE(dpaa2_mac_fdt, dpaa2_mc, dpaa2_mac_fdt_driver, 0, 0); MODULE_DEPEND(dpaa2_mac_fdt, memac_mdio_fdt, 1, 1, 1); /* * Device interface. */ static int dpaa2_mc_fdt_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,qoriq-mc")) return (ENXIO); device_set_desc(dev, "DPAA2 Management Complex"); return (BUS_PROBE_DEFAULT); } static int dpaa2_mc_fdt_probe_child(device_t bus, phandle_t child) { device_t childdev; /* make sure we do not aliready have a device. */ childdev = ofw_bus_find_child_device_by_phandle(bus, child); if (childdev != NULL) return (0); childdev = simplebus_add_device(bus, child, 0, "dpaa2_mac_fdt", -1, NULL); if (childdev == NULL) return (ENXIO); return (device_probe_and_attach(childdev)); } static int dpaa2_mc_fdt_attach(device_t dev) { struct dpaa2_mc_softc *sc; phandle_t node; phandle_t child; sc = device_get_softc(dev); sc->acpi_based = false; sc->ofw_node = ofw_bus_get_node(dev); bus_generic_probe(dev); bus_enumerate_hinted_children(dev); bus_generic_probe(dev); bus_enumerate_hinted_children(dev); /* * Attach the children represented in the device tree. */ /* fsl-mc -> dpamcs */ node = OF_child(sc->ofw_node); simplebus_init(dev, node); /* Attach the dpmac children represented in the device tree. */ child = ofw_bus_find_compatible(node, "fsl,qoriq-mc-dpmac"); for (; child > 0; child = OF_peer(child)) { if (!ofw_bus_node_is_compatible(child, "fsl,qoriq-mc-dpmac")) continue; if (!OF_hasprop(child, "reg")) continue; if (dpaa2_mc_fdt_probe_child(dev, child) != 0) continue; } return (dpaa2_mc_attach(dev)); } /* * FDT compat layer. */ static device_t dpaa2_mc_fdt_find_dpaa2_mac_dev(device_t dev, uint32_t id) { int devcount, error, i, len; device_t *devlist, mdev; const char *mdevname; error = device_get_children(dev, &devlist, &devcount); if (error != 0) return (NULL); for (i = 0; i < devcount; i++) { mdev = devlist[i]; mdevname = device_get_name(mdev); if (mdevname == NULL) continue; len = strlen(mdevname); if (strncmp("dpaa2_mac_fdt", mdevname, len) != 0) continue; if (!device_is_attached(mdev)) continue; if (dpaa2_mac_fdt_match_id(mdev, id)) return (mdev); } return (NULL); } static int dpaa2_mc_fdt_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id) { device_t mdev, pdev; mdev = dpaa2_mc_fdt_find_dpaa2_mac_dev(dev, id); if (mdev == NULL) { device_printf(dev, "%s: error finding dpmac device with id=%u\n", __func__, id); return (ENXIO); } pdev = dpaa2_mac_fdt_get_phy_dev(mdev); if (pdev == NULL) { device_printf(dev, "%s: error getting MDIO device for dpamc %s " "(id=%u)\n", __func__, device_get_nameunit(mdev), id); return (ENXIO); } if (phy_dev != NULL) *phy_dev = pdev; if (bootverbose) device_printf(dev, "dpmac_id %u mdev %p (%s) pdev %p (%s)\n", id, mdev, device_get_nameunit(mdev), pdev, device_get_nameunit(pdev)); return (0); } static const struct ofw_bus_devinfo * dpaa2_mc_simplebus_get_devinfo(device_t bus, device_t child) { return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child)); } static device_method_t dpaa2_mc_fdt_methods[] = { /* Device interface */ DEVMETHOD(device_probe, dpaa2_mc_fdt_probe), DEVMETHOD(device_attach, dpaa2_mc_fdt_attach), DEVMETHOD(device_detach, dpaa2_mc_detach), /* Bus interface */ + DEVMETHOD(bus_get_rman, dpaa2_mc_rman), DEVMETHOD(bus_alloc_resource, dpaa2_mc_alloc_resource), DEVMETHOD(bus_adjust_resource, dpaa2_mc_adjust_resource), DEVMETHOD(bus_release_resource, dpaa2_mc_release_resource), DEVMETHOD(bus_activate_resource, dpaa2_mc_activate_resource), DEVMETHOD(bus_deactivate_resource, dpaa2_mc_deactivate_resource), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), /* Pseudo-PCIB interface */ DEVMETHOD(pcib_alloc_msi, dpaa2_mc_alloc_msi), DEVMETHOD(pcib_release_msi, dpaa2_mc_release_msi), DEVMETHOD(pcib_map_msi, dpaa2_mc_map_msi), DEVMETHOD(pcib_get_id, dpaa2_mc_get_id), /* DPAA2 MC bus interface */ DEVMETHOD(dpaa2_mc_manage_dev, dpaa2_mc_manage_dev), DEVMETHOD(dpaa2_mc_get_free_dev,dpaa2_mc_get_free_dev), DEVMETHOD(dpaa2_mc_get_dev, dpaa2_mc_get_dev), DEVMETHOD(dpaa2_mc_get_shared_dev, dpaa2_mc_get_shared_dev), DEVMETHOD(dpaa2_mc_reserve_dev, dpaa2_mc_reserve_dev), DEVMETHOD(dpaa2_mc_release_dev, dpaa2_mc_release_dev), DEVMETHOD(dpaa2_mc_get_phy_dev, dpaa2_mc_fdt_get_phy_dev), /* OFW/simplebus */ DEVMETHOD(ofw_bus_get_devinfo, dpaa2_mc_simplebus_get_devinfo), DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat), DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model), DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name), DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node), DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type), DEVMETHOD_END }; DEFINE_CLASS_1(dpaa2_mc, dpaa2_mc_fdt_driver, dpaa2_mc_fdt_methods, sizeof(struct dpaa2_mc_softc), dpaa2_mc_driver); DRIVER_MODULE(dpaa2_mc, simplebus, dpaa2_mc_fdt_driver, 0, 0);