Add initial DPAA2 support

DPAA2 is a hardware-level networking architecture found in some NXP
SoCs which contain hardware blocks including Management Complex
(MC, a command interface to manipulate DPAA2 objects), Wire Rate I/O
processor (WRIOP, packets distribution, queuing, drop decisions),
Queues and Buffers Manager (QBMan, Rx/Tx queues control, Rx buffer
pools) and the others.

The Management Complex runs NXP-supplied firmware which provides DPAA2
objects as an abstraction layer over those blocks to simplify an
access to the underlying hardware. Each DPAA2 object has its own
driver (to perform an initialization at least) and will be visible
as a separate device in the device tree.

Two new drivers (dpaa2_mc and dpaa2_rc) act like firmware buses in
order to form a hierarchy of the DPAA2 devices:

	acpiX (or simplebusX)
	  dpaa2_mcX
	    dpaa2_rcX
	      dpaa2_mcp0
	      ...
	      dpaa2_mcpN
	      dpaa2_bpX
	      dpaa2_macX
	      dpaa2_io0
	      ...
	      dpaa2_ioM
	      dpaa2_niX

dpaa2_mc is suppossed to be a root of the hierarchy, comes in ACPI
and FDT flavours and implements helper interfaces to allocate and
assign bus resources, MSI and "managed" DPAA2 devices (NXP treats some
of the objects as resources for the other DPAA2 objects to let them
function properly). Almost all of the DPAA2 objects are assigned to
the resource containers (dpaa2_rc) to implement isolation.

The initial implementation focuses on the DPAA2 network interface
to be operational. It is the most complex object in terms of
dependencies which uses I/O objects to transmit/receive packets.

Approved by:		bz (mentor)
Tested by:		manu, bz
MFC after:		3 months
Differential Revision:	https://reviews.freebsd.org/D36638
This commit is contained in:
Dmitry Salychev 2022-09-20 11:47:41 +02:00
parent 73a78b5efa
commit ba7319e909
No known key found for this signature in database
GPG Key ID: CEDA4464564B041D
33 changed files with 17601 additions and 0 deletions

View File

@ -22,6 +22,7 @@ device dwc3 # Synopsys DWC controller
device sdhci
# Ethernet NICs
device dpaa2 # Data Path Acceleration Architecture (2nd Gen)
device enetc # QorIQ LS1028A NIC
options FDT

View File

@ -182,6 +182,25 @@ dev/axgbe/xgbe-phy-v1.c optional axa
dev/cpufreq/cpufreq_dt.c optional cpufreq fdt
dev/dpaa2/dpaa2_bp.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_cmd_if.m optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_con.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_io.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_mac.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_mc.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_mc_acpi.c optional soc_nxp_ls dpaa2 acpi
dev/dpaa2/dpaa2_mc_fdt.c optional soc_nxp_ls dpaa2 fdt
dev/dpaa2/dpaa2_mc_if.m optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_mcp.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_ni.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_rc.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_swp.c optional soc_nxp_ls dpaa2
dev/dpaa2/dpaa2_swp_if.m optional soc_nxp_ls dpaa2
dev/dpaa2/memac_mdio_acpi.c optional soc_nxp_ls dpaa2 acpi
dev/dpaa2/memac_mdio_common.c optional soc_nxp_ls dpaa2 acpi | soc_nxp_ls dpaa2 fdt
dev/dpaa2/memac_mdio_fdt.c optional soc_nxp_ls dpaa2 fdt
dev/dpaa2/memac_mdio_if.m optional soc_nxp_ls dpaa2 acpi | soc_nxp_ls dpaa2 fdt
dev/dwc/if_dwc.c optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 | fdt dwc_socfpga soc_intel_stratix10
dev/dwc/if_dwc_if.m optional fdt dwc_rk soc_rockchip_rk3328 | fdt dwc_rk soc_rockchip_rk3399 | fdt dwc_socfpga soc_intel_stratix10

205
sys/dev/dpaa2/dpaa2_bp.c Normal file
View File

@ -0,0 +1,205 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* The DPAA2 Buffer Pool (DPBP) driver.
*
* The DPBP configures a buffer pool that can be associated with DPAA2 network
* and accelerator interfaces.
*/
#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 <vm/vm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#include "pcib_if.h"
#include "pci_if.h"
#include "dpaa2_mc.h"
#include "dpaa2_mcp.h"
#include "dpaa2_swp.h"
#include "dpaa2_swp_if.h"
#include "dpaa2_cmd_if.h"
/* DPAA2 Buffer Pool resource specification. */
struct resource_spec dpaa2_bp_spec[] = {
/*
* DPMCP resources.
*
* NOTE: MC command portals (MCPs) are used to send commands to, and
* receive responses from, the MC firmware. One portal per DPBP.
*/
#define MCP_RES_NUM (1u)
#define MCP_RID_OFF (0u)
#define MCP_RID(rid) ((rid) + MCP_RID_OFF)
/* --- */
{ DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL },
/* --- */
RESOURCE_SPEC_END
};
static int
dpaa2_bp_probe(device_t dev)
{
/* DPBP device will be added by the parent resource container. */
device_set_desc(dev, "DPAA2 Buffer Pool");
return (BUS_PROBE_DEFAULT);
}
static int
dpaa2_bp_detach(device_t dev)
{
device_t child = dev;
struct dpaa2_bp_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
if (sc->cmd != NULL) {
(void)DPAA2_CMD_BP_DISABLE(dev, child, sc->cmd);
(void)DPAA2_CMD_BP_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd,
sc->bp_token));
(void)DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd,
sc->rc_token));
dpaa2_mcp_free_command(sc->cmd);
sc->cmd = NULL;
}
dinfo->portal = NULL;
bus_release_resources(sc->dev, dpaa2_bp_spec, sc->res);
return (0);
}
static int
dpaa2_bp_attach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
device_t mcp_dev;
struct dpaa2_bp_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_devinfo *mcp_dinfo;
int error;
sc->dev = dev;
sc->cmd = NULL;
error = bus_alloc_resources(sc->dev, dpaa2_bp_spec, sc->res);
if (error) {
device_printf(dev, "%s: failed to allocate resources: "
"error=%d\n", __func__, error);
return (ENXIO);
}
/* Send commands to MC via allocated portal. */
mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]);
mcp_dinfo = device_get_ivars(mcp_dev);
dinfo->portal = mcp_dinfo->portal;
/* Allocate a command to send to MC hardware. */
error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF);
if (error) {
device_printf(dev, "%s: failed to allocate dpaa2_cmd: "
"error=%d\n", __func__, error);
dpaa2_bp_detach(dev);
return (ENXIO);
}
/* Open resource container and DPBP object. */
error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id,
&sc->rc_token);
if (error) {
device_printf(dev, "%s: failed to open DPRC: error=%d\n",
__func__, error);
dpaa2_bp_detach(dev);
return (ENXIO);
}
error = DPAA2_CMD_BP_OPEN(dev, child, sc->cmd, dinfo->id, &sc->bp_token);
if (error) {
device_printf(dev, "%s: failed to open DPBP: id=%d, error=%d\n",
__func__, dinfo->id, error);
dpaa2_bp_detach(dev);
return (ENXIO);
}
/* Prepare DPBP object. */
error = DPAA2_CMD_BP_RESET(dev, child, sc->cmd);
if (error) {
device_printf(dev, "%s: failed to reset DPBP: id=%d, error=%d\n",
__func__, dinfo->id, error);
dpaa2_bp_detach(dev);
return (ENXIO);
}
error = DPAA2_CMD_BP_ENABLE(dev, child, sc->cmd);
if (error) {
device_printf(dev, "%s: failed to enable DPBP: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
dpaa2_bp_detach(dev);
return (ENXIO);
}
error = DPAA2_CMD_BP_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr);
if (error) {
device_printf(dev, "%s: failed to get DPBP attributes: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
dpaa2_bp_detach(dev);
return (ENXIO);
}
return (0);
}
static device_method_t dpaa2_bp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, dpaa2_bp_probe),
DEVMETHOD(device_attach, dpaa2_bp_attach),
DEVMETHOD(device_detach, dpaa2_bp_detach),
DEVMETHOD_END
};
static driver_t dpaa2_bp_driver = {
"dpaa2_bp",
dpaa2_bp_methods,
sizeof(struct dpaa2_bp_softc),
};
DRIVER_MODULE(dpaa2_bp, dpaa2_rc, dpaa2_bp_driver, 0, 0);

74
sys/dev/dpaa2/dpaa2_bp.h Normal file
View File

@ -0,0 +1,74 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright © 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_BP_H
#define _DPAA2_BP_H
#include <sys/bus.h>
/* Maximum resources per DPBP: 1 DPMCP. */
#define DPAA2_BP_MAX_RESOURCES 1
/**
* @brief Attributes of the DPBP object.
*
* id: DPBP object ID.
* bpid: Hardware buffer pool ID; should be used as an argument in
* acquire/release operations on buffers.
*/
struct dpaa2_bp_attr {
uint32_t id;
uint16_t bpid;
};
/**
* @brief Configuration/state of the buffer pool.
*/
struct dpaa2_bp_conf {
uint8_t bdi;
uint8_t state; /* bitmask */
uint32_t free_bufn;
};
/**
* @brief Software context for the DPAA2 Buffer Pool driver.
*/
struct dpaa2_bp_softc {
device_t dev;
struct dpaa2_bp_attr attr;
/* Help to send commands to MC. */
struct dpaa2_cmd *cmd;
uint16_t rc_token;
uint16_t bp_token;
struct resource *res[DPAA2_BP_MAX_RESOURCES];
};
extern struct resource_spec dpaa2_bp_spec[];
#endif /* _DPAA2_BP_H */

1583
sys/dev/dpaa2/dpaa2_cmd_if.m Normal file

File diff suppressed because it is too large Load Diff

213
sys/dev/dpaa2/dpaa2_con.c Normal file
View File

@ -0,0 +1,213 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* The DPAA2 Concentrator (DPCON) driver.
*
* Supports configuration of QBMan channels for advanced scheduling of ingress
* packets from one or more network interfaces.
*
* DPCONs are used to distribute Rx or Tx Confirmation traffic to different
* cores, via affine DPIO objects. The implication is that one DPCON must be
* available for each core where Rx or Tx Confirmation traffic should be
* distributed to.
*
* QBMan channel contains several work queues. The WQs within a channel have a
* priority relative to each other. Each channel consists of either eight or two
* WQs, and thus, there are either eight or two possible priorities in a channel.
*/
#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 <vm/vm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#include "pcib_if.h"
#include "pci_if.h"
#include "dpaa2_mcp.h"
#include "dpaa2_swp.h"
#include "dpaa2_mc.h"
#include "dpaa2_cmd_if.h"
/* DPAA2 Concentrator resource specification. */
struct resource_spec dpaa2_con_spec[] = {
/*
* DPMCP resources.
*
* NOTE: MC command portals (MCPs) are used to send commands to, and
* receive responses from, the MC firmware. One portal per DPCON.
*/
#define MCP_RES_NUM (1u)
#define MCP_RID_OFF (0u)
#define MCP_RID(rid) ((rid) + MCP_RID_OFF)
/* --- */
{ DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL },
/* --- */
RESOURCE_SPEC_END
};
static int dpaa2_con_detach(device_t dev);
/*
* Device interface.
*/
static int
dpaa2_con_probe(device_t dev)
{
/* DPCON device will be added by a parent resource container itself. */
device_set_desc(dev, "DPAA2 Concentrator");
return (BUS_PROBE_DEFAULT);
}
static int
dpaa2_con_detach(device_t dev)
{
device_t child = dev;
struct dpaa2_con_softc *sc = device_get_softc(dev);
DPAA2_CMD_CON_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->con_token));
DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
dpaa2_mcp_free_command(sc->cmd);
sc->cmd = NULL;
sc->con_token = 0;
sc->rc_token = 0;
return (0);
}
static int
dpaa2_con_attach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
device_t mcp_dev;
struct dpaa2_con_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_devinfo *mcp_dinfo;
int error;
sc->dev = dev;
error = bus_alloc_resources(sc->dev, dpaa2_con_spec, sc->res);
if (error) {
device_printf(dev, "%s: failed to allocate resources: "
"error=%d\n", __func__, error);
return (ENXIO);
}
/* Obtain MC portal. */
mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]);
mcp_dinfo = device_get_ivars(mcp_dev);
dinfo->portal = mcp_dinfo->portal;
/* Allocate a command to send to MC hardware. */
error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF);
if (error) {
device_printf(dev, "Failed to allocate dpaa2_cmd: error=%d\n",
error);
goto err_exit;
}
/* Open resource container and DPCON object. */
error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id,
&sc->rc_token);
if (error) {
device_printf(dev, "Failed to open DPRC: error=%d\n", error);
goto err_free_cmd;
}
error = DPAA2_CMD_CON_OPEN(dev, child, sc->cmd, dinfo->id,
&sc->con_token);
if (error) {
device_printf(dev, "Failed to open DPCON: id=%d, error=%d\n",
dinfo->id, error);
goto err_close_rc;
}
/* Prepare DPCON object. */
error = DPAA2_CMD_CON_RESET(dev, child, sc->cmd);
if (error) {
device_printf(dev, "Failed to reset DPCON: id=%d, error=%d\n",
dinfo->id, error);
goto err_close_con;
}
error = DPAA2_CMD_CON_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr);
if (error) {
device_printf(dev, "Failed to get DPCON attributes: id=%d, "
"error=%d\n", dinfo->id, error);
goto err_close_con;
}
/* TODO: Enable debug output via sysctl (to reduce output). */
if (bootverbose)
device_printf(dev, "chan_id=%d, priorities=%d\n",
sc->attr.chan_id, sc->attr.prior_num);
return (0);
err_close_con:
DPAA2_CMD_CON_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->con_token));
err_close_rc:
DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
err_free_cmd:
dpaa2_mcp_free_command(sc->cmd);
err_exit:
return (ENXIO);
}
static device_method_t dpaa2_con_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, dpaa2_con_probe),
DEVMETHOD(device_attach, dpaa2_con_attach),
DEVMETHOD(device_detach, dpaa2_con_detach),
DEVMETHOD_END
};
static driver_t dpaa2_con_driver = {
"dpaa2_con",
dpaa2_con_methods,
sizeof(struct dpaa2_con_softc),
};
DRIVER_MODULE(dpaa2_con, dpaa2_rc, dpaa2_con_driver, 0, 0);

70
sys/dev/dpaa2/dpaa2_con.h Normal file
View File

@ -0,0 +1,70 @@
/*-
* 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_CON_H
#define _DPAA2_CON_H
#include <sys/rman.h>
#include <sys/bus.h>
#include <sys/queue.h>
#include "dpaa2_types.h"
#include "dpaa2_mcp.h"
/* Maximum resources per DPCON: 1 DPMCP. */
#define DPAA2_CON_MAX_RESOURCES 1
/**
* @brief Attributes of the DPCON object.
*
* id: DPCON object ID.
* chan_id: QBMan channel ID to be used for dequeue operations.
* prior_num: Number of priorities for the DPCON channel (1-8).
*/
struct dpaa2_con_attr {
uint32_t id;
uint16_t chan_id;
uint8_t prior_num;
};
/**
* @brief Software context for the DPAA2 Concentrator driver.
*/
struct dpaa2_con_softc {
device_t dev;
struct resource *res[DPAA2_CON_MAX_RESOURCES];
struct dpaa2_con_attr attr;
/* Help to send commands to MC. */
struct dpaa2_cmd *cmd;
uint16_t rc_token;
uint16_t con_token;
};
extern struct resource_spec dpaa2_con_spec[];
#endif /* _DPAA2_CON_H */

569
sys/dev/dpaa2/dpaa2_io.c Normal file
View File

@ -0,0 +1,569 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* QBMan command interface and the DPAA2 I/O (DPIO) driver.
*
* The DPIO object allows configuration of the QBMan software portal with
* optional notification capabilities.
*
* Software portals are used by the driver to communicate with the QBMan. The
* DPIO objects main purpose is to enable the driver to perform I/O enqueue
* and dequeue operations, as well as buffer release and acquire operations
* using QBMan.
*/
#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/_cpuset.h>
#include <sys/cpuset.h>
#include <sys/taskqueue.h>
#include <sys/smp.h>
#include <vm/vm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#include "pcib_if.h"
#include "pci_if.h"
#include "dpaa2_mc.h"
#include "dpaa2_mcp.h"
#include "dpaa2_swp.h"
#include "dpaa2_swp_if.h"
#include "dpaa2_cmd_if.h"
#include "dpaa2_io.h"
#include "dpaa2_ni.h"
#define DPIO_IRQ_INDEX 0 /* index of the only DPIO IRQ */
#define DPIO_POLL_MAX 32
/*
* Memory:
* 0: cache-enabled part of the QBMan software portal.
* 1: cache-inhibited part of the QBMan software portal.
* 2: control registers of the QBMan software portal?
*
* Note that MSI should be allocated separately using pseudo-PCI interface.
*/
struct resource_spec dpaa2_io_spec[] = {
/*
* System Memory resources.
*/
#define MEM_RES_NUM (3u)
#define MEM_RID_OFF (0u)
#define MEM_RID(rid) ((rid) + MEM_RID_OFF)
{ SYS_RES_MEMORY, MEM_RID(0), RF_ACTIVE | RF_UNMAPPED },
{ SYS_RES_MEMORY, MEM_RID(1), RF_ACTIVE | RF_UNMAPPED },
{ SYS_RES_MEMORY, MEM_RID(2), RF_ACTIVE | RF_UNMAPPED | RF_OPTIONAL },
/*
* DPMCP resources.
*
* NOTE: MC command portals (MCPs) are used to send commands to, and
* receive responses from, the MC firmware. One portal per DPIO.
*/
#define MCP_RES_NUM (1u)
#define MCP_RID_OFF (MEM_RID_OFF + MEM_RES_NUM)
#define MCP_RID(rid) ((rid) + MCP_RID_OFF)
/* --- */
{ DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL },
/* --- */
RESOURCE_SPEC_END
};
/* Configuration routines. */
static int dpaa2_io_setup_irqs(device_t dev);
static int dpaa2_io_release_irqs(device_t dev);
static int dpaa2_io_setup_msi(struct dpaa2_io_softc *sc);
static int dpaa2_io_release_msi(struct dpaa2_io_softc *sc);
/* Interrupt handlers */
static void dpaa2_io_intr(void *arg);
static int
dpaa2_io_probe(device_t dev)
{
/* DPIO device will be added by a parent resource container itself. */
device_set_desc(dev, "DPAA2 I/O");
return (BUS_PROBE_DEFAULT);
}
static int
dpaa2_io_detach(device_t dev)
{
device_t child = dev;
struct dpaa2_io_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
int error;
/* Tear down interrupt handler and release IRQ resources. */
dpaa2_io_release_irqs(dev);
/* Free software portal helper object. */
dpaa2_swp_free_portal(sc->swp);
/* Disable DPIO object. */
error = DPAA2_CMD_IO_DISABLE(dev, child, dpaa2_mcp_tk(sc->cmd,
sc->io_token));
if (error && bootverbose)
device_printf(dev, "%s: failed to disable DPIO: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
/* Close control sessions with the DPAA2 objects. */
DPAA2_CMD_IO_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->io_token));
DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
/* Free pre-allocated MC command. */
dpaa2_mcp_free_command(sc->cmd);
sc->cmd = NULL;
sc->io_token = 0;
sc->rc_token = 0;
/* Unmap memory resources of the portal. */
for (int i = 0; i < MEM_RES_NUM; i++) {
if (sc->res[MEM_RID(i)] == NULL)
continue;
error = bus_unmap_resource(sc->dev, SYS_RES_MEMORY,
sc->res[MEM_RID(i)], &sc->map[MEM_RID(i)]);
if (error && bootverbose)
device_printf(dev, "%s: failed to unmap memory "
"resource: rid=%d, error=%d\n", __func__, MEM_RID(i),
error);
}
/* Release allocated resources. */
bus_release_resources(dev, dpaa2_io_spec, sc->res);
return (0);
}
static int
dpaa2_io_attach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
device_t mcp_dev;
struct dpaa2_io_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_devinfo *mcp_dinfo;
struct resource_map_request req;
struct {
vm_memattr_t memattr;
char *label;
} map_args[MEM_RES_NUM] = {
{ VM_MEMATTR_WRITE_BACK, "cache-enabled part" },
{ VM_MEMATTR_DEVICE, "cache-inhibited part" },
{ VM_MEMATTR_DEVICE, "control registers" }
};
int error;
sc->dev = dev;
sc->swp = NULL;
sc->cmd = NULL;
sc->intr = NULL;
sc->irq_resource = NULL;
/* Allocate resources. */
error = bus_alloc_resources(sc->dev, dpaa2_io_spec, sc->res);
if (error) {
device_printf(dev, "%s: failed to allocate resources: "
"error=%d\n", __func__, error);
return (ENXIO);
}
/* Set allocated MC portal up. */
mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]);
mcp_dinfo = device_get_ivars(mcp_dev);
dinfo->portal = mcp_dinfo->portal;
/* Map memory resources of the portal. */
for (int i = 0; i < MEM_RES_NUM; i++) {
if (sc->res[MEM_RID(i)] == NULL)
continue;
resource_init_map_request(&req);
req.memattr = map_args[i].memattr;
error = bus_map_resource(sc->dev, SYS_RES_MEMORY,
sc->res[MEM_RID(i)], &req, &sc->map[MEM_RID(i)]);
if (error) {
device_printf(dev, "%s: failed to map %s: error=%d\n",
__func__, map_args[i].label, error);
goto err_exit;
}
}
/* Allocate a command to send to the MC hardware. */
error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF);
if (error) {
device_printf(dev, "%s: failed to allocate dpaa2_cmd: "
"error=%d\n", __func__, error);
goto err_exit;
}
/* Prepare DPIO object. */
error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id,
&sc->rc_token);
if (error) {
device_printf(dev, "%s: failed to open DPRC: error=%d\n",
__func__, error);
goto err_exit;
}
error = DPAA2_CMD_IO_OPEN(dev, child, sc->cmd, dinfo->id, &sc->io_token);
if (error) {
device_printf(dev, "%s: failed to open DPIO: id=%d, error=%d\n",
__func__, dinfo->id, error);
goto err_exit;
}
error = DPAA2_CMD_IO_RESET(dev, child, sc->cmd);
if (error) {
device_printf(dev, "%s: failed to reset DPIO: id=%d, error=%d\n",
__func__, dinfo->id, error);
goto err_exit;
}
error = DPAA2_CMD_IO_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr);
if (error) {
device_printf(dev, "%s: failed to get DPIO attributes: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
goto err_exit;
}
error = DPAA2_CMD_IO_ENABLE(dev, child, sc->cmd);
if (error) {
device_printf(dev, "%s: failed to enable DPIO: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
goto err_exit;
}
/* Prepare descriptor of the QBMan software portal. */
sc->swp_desc.dpio_dev = dev;
sc->swp_desc.swp_version = sc->attr.swp_version;
sc->swp_desc.swp_clk = sc->attr.swp_clk;
sc->swp_desc.swp_id = sc->attr.swp_id;
sc->swp_desc.has_notif = sc->attr.priors_num ? true : false;
sc->swp_desc.has_8prio = sc->attr.priors_num == 8u ? true : false;
sc->swp_desc.cena_res = sc->res[0];
sc->swp_desc.cena_map = &sc->map[0];
sc->swp_desc.cinh_res = sc->res[1];
sc->swp_desc.cinh_map = &sc->map[1];
/*
* Compute how many 256 QBMAN cycles fit into one ns. This is because
* the interrupt timeout period register needs to be specified in QBMAN
* clock cycles in increments of 256.
*/
sc->swp_desc.swp_cycles_ratio = 256000 /
(sc->swp_desc.swp_clk / 1000000);
/* Initialize QBMan software portal. */
error = dpaa2_swp_init_portal(&sc->swp, &sc->swp_desc, DPAA2_SWP_DEF);
if (error) {
device_printf(dev, "%s: failed to initialize dpaa2_swp: "
"error=%d\n", __func__, error);
goto err_exit;
}
error = dpaa2_io_setup_irqs(dev);
if (error) {
device_printf(dev, "%s: failed to setup IRQs: error=%d\n",
__func__, error);
goto err_exit;
}
#if 0
/* TODO: Enable debug output via sysctl (to reduce output). */
if (bootverbose)
device_printf(dev, "dpio_id=%d, swp_id=%d, chan_mode=%s, "
"notif_priors=%d, swp_version=0x%x\n",
sc->attr.id, sc->attr.swp_id,
sc->attr.chan_mode == DPAA2_IO_LOCAL_CHANNEL
? "local_channel" : "no_channel", sc->attr.priors_num,
sc->attr.swp_version);
#endif
return (0);
err_exit:
dpaa2_io_detach(dev);
return (ENXIO);
}
/**
* @brief Enqueue multiple frames to a frame queue using one FQID.
*/
static int
dpaa2_io_enq_multiple_fq(device_t iodev, uint32_t fqid,
struct dpaa2_fd *fd, int frames_n)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
struct dpaa2_swp *swp = sc->swp;
struct dpaa2_eq_desc ed;
uint32_t flags = 0;
memset(&ed, 0, sizeof(ed));
/* Setup enqueue descriptor. */
dpaa2_swp_set_ed_norp(&ed, false);
dpaa2_swp_set_ed_fq(&ed, fqid);
return (dpaa2_swp_enq_mult(swp, &ed, fd, &flags, frames_n));
}
/**
* @brief Configure the channel data availability notification (CDAN)
* in a particular WQ channel paired with DPIO.
*/
static int
dpaa2_io_conf_wq_channel(device_t iodev, struct dpaa2_io_notif_ctx *ctx)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
/* Enable generation of the CDAN notifications. */
if (ctx->cdan_en)
return (dpaa2_swp_conf_wq_channel(sc->swp, ctx->fq_chan_id,
DPAA2_WQCHAN_WE_EN | DPAA2_WQCHAN_WE_CTX, ctx->cdan_en,
ctx->qman_ctx));
return (0);
}
/**
* @brief Query current configuration/state of the buffer pool.
*/
static int
dpaa2_io_query_bp(device_t iodev, uint16_t bpid, struct dpaa2_bp_conf *conf)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
return (dpaa2_swp_query_bp(sc->swp, bpid, conf));
}
/**
* @brief Release one or more buffer pointers to the QBMan buffer pool.
*/
static int
dpaa2_io_release_bufs(device_t iodev, uint16_t bpid, bus_addr_t *buf,
uint32_t buf_num)
{
struct dpaa2_io_softc *sc = device_get_softc(iodev);
return (dpaa2_swp_release_bufs(sc->swp, bpid, buf, buf_num));
}
/**
* @brief Configure DPNI object to generate interrupts.
*/
static int
dpaa2_io_setup_irqs(device_t dev)
{
struct dpaa2_io_softc *sc = device_get_softc(dev);
int error;
/*
* Setup interrupts generated by the software portal.
*/
dpaa2_swp_set_intr_trigger(sc->swp, DPAA2_SWP_INTR_DQRI);
dpaa2_swp_clear_intr_status(sc->swp, 0xFFFFFFFFu);
/* Configure IRQs. */
error = dpaa2_io_setup_msi(sc);
if (error) {
device_printf(dev, "%s: failed to allocate MSI: error=%d\n",
__func__, error);
return (error);
}
if ((sc->irq_resource = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) {
device_printf(dev, "%s: failed to allocate IRQ resource\n",
__func__);
return (ENXIO);
}
if (bus_setup_intr(dev, sc->irq_resource, INTR_TYPE_NET | INTR_MPSAFE |
INTR_ENTROPY, NULL, dpaa2_io_intr, sc, &sc->intr)) {
device_printf(dev, "%s: failed to setup IRQ resource\n",
__func__);
return (ENXIO);
}
/* Wrap DPIO ID around number of CPUs. */
bus_bind_intr(dev, sc->irq_resource, sc->attr.id % mp_ncpus);
/*
* Setup and enable Static Dequeue Command to receive CDANs from
* channel 0.
*/
if (sc->swp_desc.has_notif)
dpaa2_swp_set_push_dequeue(sc->swp, 0, true);
return (0);
}
static int
dpaa2_io_release_irqs(device_t dev)
{
struct dpaa2_io_softc *sc = device_get_softc(dev);
/* Disable receiving CDANs from channel 0. */
if (sc->swp_desc.has_notif)
dpaa2_swp_set_push_dequeue(sc->swp, 0, false);
/* Release IRQ resources. */
if (sc->intr != NULL)
bus_teardown_intr(dev, sc->irq_resource, &sc->intr);
if (sc->irq_resource != NULL)
bus_release_resource(dev, SYS_RES_IRQ, sc->irq_rid[0],
sc->irq_resource);
(void)dpaa2_io_release_msi(device_get_softc(dev));
/* Configure software portal to stop generating interrupts. */
dpaa2_swp_set_intr_trigger(sc->swp, 0);
dpaa2_swp_clear_intr_status(sc->swp, 0xFFFFFFFFu);
return (0);
}
/**
* @brief Allocate MSI interrupts for this DPAA2 I/O object.
*/
static int
dpaa2_io_setup_msi(struct dpaa2_io_softc *sc)
{
int val;
val = pci_msi_count(sc->dev);
if (val < DPAA2_IO_MSI_COUNT)
device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val,
DPAA2_IO_MSI_COUNT);
val = MIN(val, DPAA2_IO_MSI_COUNT);
if (pci_alloc_msi(sc->dev, &val) != 0)
return (EINVAL);
for (int i = 0; i < val; i++)
sc->irq_rid[i] = i + 1;
return (0);
}
static int
dpaa2_io_release_msi(struct dpaa2_io_softc *sc)
{
int error;
error = pci_release_msi(sc->dev);
if (error) {
device_printf(sc->dev, "%s: failed to release MSI: error=%d/n",
__func__, error);
return (error);
}
return (0);
}
/**
* @brief DPAA2 I/O interrupt handler.
*/
static void
dpaa2_io_intr(void *arg)
{
struct dpaa2_io_softc *sc = (struct dpaa2_io_softc *) arg;
struct dpaa2_io_notif_ctx *ctx[DPIO_POLL_MAX];
struct dpaa2_dq dq;
uint32_t idx, status;
uint16_t flags;
int rc, cdan_n = 0;
status = dpaa2_swp_read_intr_status(sc->swp);
if (status == 0) {
return;
}
DPAA2_SWP_LOCK(sc->swp, &flags);
if (flags & DPAA2_SWP_DESTROYED) {
/* Terminate operation if portal is destroyed. */
DPAA2_SWP_UNLOCK(sc->swp);
return;
}
for (int i = 0; i < DPIO_POLL_MAX; i++) {
rc = dpaa2_swp_dqrr_next_locked(sc->swp, &dq, &idx);
if (rc) {
break;
}
if ((dq.common.verb & DPAA2_DQRR_RESULT_MASK) ==
DPAA2_DQRR_RESULT_CDAN) {
ctx[cdan_n++] = (struct dpaa2_io_notif_ctx *) dq.scn.ctx;
} else {
/* TODO: Report unknown DQRR entry. */
}
dpaa2_swp_write_reg(sc->swp, DPAA2_SWP_CINH_DCAP, idx);
}
DPAA2_SWP_UNLOCK(sc->swp);
for (int i = 0; i < cdan_n; i++) {
ctx[i]->poll(ctx[i]->channel);
}
/* Enable software portal interrupts back */
dpaa2_swp_clear_intr_status(sc->swp, status);
dpaa2_swp_write_reg(sc->swp, DPAA2_SWP_CINH_IIR, 0);
}
static device_method_t dpaa2_io_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, dpaa2_io_probe),
DEVMETHOD(device_attach, dpaa2_io_attach),
DEVMETHOD(device_detach, dpaa2_io_detach),
/* QBMan software portal interface */
DEVMETHOD(dpaa2_swp_enq_multiple_fq, dpaa2_io_enq_multiple_fq),
DEVMETHOD(dpaa2_swp_conf_wq_channel, dpaa2_io_conf_wq_channel),
DEVMETHOD(dpaa2_swp_query_bp, dpaa2_io_query_bp),
DEVMETHOD(dpaa2_swp_release_bufs, dpaa2_io_release_bufs),
DEVMETHOD_END
};
static driver_t dpaa2_io_driver = {
"dpaa2_io",
dpaa2_io_methods,
sizeof(struct dpaa2_io_softc),
};
DRIVER_MODULE(dpaa2_io, dpaa2_rc, dpaa2_io_driver, 0, 0);

110
sys/dev/dpaa2/dpaa2_io.h Normal file
View File

@ -0,0 +1,110 @@
/*-
* 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_IO_H
#define _DPAA2_IO_H
#include <sys/rman.h>
#include <sys/bus.h>
#include <sys/queue.h>
#include "dpaa2_types.h"
#include "dpaa2_mcp.h"
/* Maximum resources per DPIO: 3 SYS_MEM + 1 DPMCP. */
#define DPAA2_IO_MAX_RESOURCES 4
/* Maximum number of MSIs supported by the DPIO objects. */
#define DPAA2_IO_MSI_COUNT 1
enum dpaa2_io_chan_mode {
DPAA2_IO_NO_CHANNEL,
DPAA2_IO_LOCAL_CHANNEL
};
/**
* @brief Attributes of the DPIO object.
*
* swp_ce_paddr: Physical address of the cache-enabled area.
* swp_ci_paddr: Physical address of the cache-inhibited area.
* swp_version: Hardware IP version of the software portal.
* swp_clk: QBMAN clock frequency value in Hz.
* id: DPIO object ID.
* swp_id: Software portal ID.
* priors_num: Number of priorities for the notification channel (1-8);
* relevant only if channel mode is "local channel".
* chan_mode: Notification channel mode.
*/
struct dpaa2_io_attr {
uint64_t swp_ce_paddr;
uint64_t swp_ci_paddr;
uint32_t swp_version;
uint32_t swp_clk;
uint32_t id;
uint16_t swp_id;
uint8_t priors_num;
enum dpaa2_io_chan_mode chan_mode;
};
/**
* @brief Context used by DPIO to configure data availability notifications
* (CDAN) on a particular WQ channel.
*/
struct dpaa2_io_notif_ctx {
void (*poll)(void *);
device_t io_dev;
void *channel;
uint64_t qman_ctx;
uint16_t fq_chan_id;
bool cdan_en;
};
/**
* @brief Software context for the DPAA2 I/O driver.
*/
struct dpaa2_io_softc {
device_t dev;
struct dpaa2_swp_desc swp_desc;
struct dpaa2_swp *swp;
struct dpaa2_io_attr attr;
/* Help to send commands to MC. */
struct dpaa2_cmd *cmd;
uint16_t rc_token;
uint16_t io_token;
struct resource *res[DPAA2_IO_MAX_RESOURCES];
struct resource_map map[DPAA2_IO_MAX_RESOURCES];
int irq_rid[DPAA2_IO_MSI_COUNT];
struct resource *irq_resource;
void *intr; /* interrupt handle */
};
extern struct resource_spec dpaa2_io_spec[];
#endif /* _DPAA2_IO_H */

376
sys/dev/dpaa2/dpaa2_mac.c Normal file
View File

@ -0,0 +1,376 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* The DPAA2 MAC driver.
*
* For every DPAA2 MAC, there is an MC object named DPMAC, for MDIO and link
* state updates. The DPMAC virtualizes the MDIO interface, so each PHY driver
* may see a private interface (removing the need for synchronization in GPP on
* the multiplexed MDIO hardware).
*/
#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 <vm/vm.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/pci/pcivar.h>
#include "pcib_if.h"
#include "pci_if.h"
#include "dpaa2_mc.h"
#include "dpaa2_ni.h"
#include "dpaa2_mcp.h"
#include "dpaa2_swp.h"
#include "dpaa2_swp_if.h"
#include "dpaa2_cmd_if.h"
/* Index of the only DPMAC IRQ. */
#define DPMAC_IRQ_INDEX 0
/* DPMAC IRQ statuses. */
#define DPMAC_IRQ_LINK_CFG_REQ 0x00000001 /* change in requested link config. */
#define DPMAC_IRQ_LINK_CHANGED 0x00000002 /* link state changed */
#define DPMAC_IRQ_LINK_UP_REQ 0x00000004 /* link up request */
#define DPMAC_IRQ_LINK_DOWN_REQ 0x00000008 /* link down request */
#define DPMAC_IRQ_EP_CHANGED 0x00000010 /* DPAA2 endpoint dis/connected */
/* DPAA2 MAC resource specification. */
struct resource_spec dpaa2_mac_spec[] = {
/*
* DPMCP resources.
*
* NOTE: MC command portals (MCPs) are used to send commands to, and
* receive responses from, the MC firmware. One portal per DPMAC.
*/
#define MCP_RES_NUM (1u)
#define MCP_RID_OFF (0u)
#define MCP_RID(rid) ((rid) + MCP_RID_OFF)
/* --- */
{ DPAA2_DEV_MCP, MCP_RID(0), RF_ACTIVE | RF_SHAREABLE | RF_OPTIONAL },
/* --- */
RESOURCE_SPEC_END
};
/* Interrupt configuration routines. */
static int dpaa2_mac_setup_irq(device_t);
static int dpaa2_mac_setup_msi(struct dpaa2_mac_softc *);
/* Subroutines to get text representation. */
static const char *dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if);
static const char *dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type);
/* Interrupt handlers */
static void dpaa2_mac_intr(void *arg);
static int
dpaa2_mac_probe(device_t dev)
{
/* DPIO device will be added by a parent resource container itself. */
device_set_desc(dev, "DPAA2 MAC");
return (BUS_PROBE_DEFAULT);
}
static int
dpaa2_mac_attach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
device_t mcp_dev;
struct dpaa2_mac_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_devinfo *mcp_dinfo;
int error;
sc->dev = dev;
memset(sc->addr, 0, ETHER_ADDR_LEN);
error = bus_alloc_resources(sc->dev, dpaa2_mac_spec, sc->res);
if (error) {
device_printf(dev, "%s: failed to allocate resources: "
"error=%d\n", __func__, error);
return (ENXIO);
}
/* Obtain MC portal. */
mcp_dev = (device_t) rman_get_start(sc->res[MCP_RID(0)]);
mcp_dinfo = device_get_ivars(mcp_dev);
dinfo->portal = mcp_dinfo->portal;
/* Allocate a command to send to MC hardware. */
error = dpaa2_mcp_init_command(&sc->cmd, DPAA2_CMD_DEF);
if (error) {
device_printf(dev, "Failed to allocate dpaa2_cmd: error=%d\n",
error);
goto err_exit;
}
/* Open resource container and DPMAC object. */
error = DPAA2_CMD_RC_OPEN(dev, child, sc->cmd, rcinfo->id,
&sc->rc_token);
if (error) {
device_printf(dev, "Failed to open DPRC: error=%d\n", error);
goto err_free_cmd;
}
error = DPAA2_CMD_MAC_OPEN(dev, child, sc->cmd, dinfo->id,
&sc->mac_token);
if (error) {
device_printf(dev, "Failed to open DPMAC: id=%d, error=%d\n",
dinfo->id, error);
goto err_close_rc;
}
error = DPAA2_CMD_MAC_GET_ATTRIBUTES(dev, child, sc->cmd, &sc->attr);
if (error) {
device_printf(dev, "Failed to get DPMAC attributes: id=%d, "
"error=%d\n", dinfo->id, error);
goto err_close_mac;
}
error = DPAA2_CMD_MAC_GET_ADDR(dev, child, sc->cmd, sc->addr);
if (error)
device_printf(dev, "Failed to get physical address: error=%d\n",
error);
/*
* TODO: Enable debug output via sysctl.
*/
if (bootverbose) {
device_printf(dev, "ether %6D\n", sc->addr, ":");
device_printf(dev, "max_rate=%d, eth_if=%s, link_type=%s\n",
sc->attr.max_rate,
dpaa2_mac_ethif_to_str(sc->attr.eth_if),
dpaa2_mac_link_type_to_str(sc->attr.link_type));
}
error = dpaa2_mac_setup_irq(dev);
if (error) {
device_printf(dev, "Failed to setup IRQs: error=%d\n", error);
goto err_close_mac;
}
return (0);
err_close_mac:
DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->mac_token));
err_close_rc:
DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
err_free_cmd:
dpaa2_mcp_free_command(sc->cmd);
err_exit:
return (ENXIO);
}
static int
dpaa2_mac_detach(device_t dev)
{
device_t child = dev;
struct dpaa2_mac_softc *sc = device_get_softc(dev);
DPAA2_CMD_MAC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->mac_token));
DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(sc->cmd, sc->rc_token));
dpaa2_mcp_free_command(sc->cmd);
sc->cmd = NULL;
sc->rc_token = 0;
sc->mac_token = 0;
return (0);
}
/**
* @brief Configure DPMAC object to generate interrupts.
*/
static int
dpaa2_mac_setup_irq(device_t dev)
{
device_t child = dev;
struct dpaa2_mac_softc *sc = device_get_softc(dev);
struct dpaa2_cmd *cmd = sc->cmd;
uint16_t mac_token = sc->mac_token;
uint32_t irq_mask;
int error;
/* Configure IRQs. */
error = dpaa2_mac_setup_msi(sc);
if (error) {
device_printf(dev, "Failed to allocate MSI\n");
return (error);
}
if ((sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&sc->irq_rid[0], RF_ACTIVE | RF_SHAREABLE)) == NULL) {
device_printf(dev, "Failed to allocate IRQ resource\n");
return (ENXIO);
}
if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE,
NULL, dpaa2_mac_intr, sc, &sc->intr)) {
device_printf(dev, "Failed to setup IRQ resource\n");
return (ENXIO);
}
/* Configure DPNI to generate interrupts. */
irq_mask =
DPMAC_IRQ_LINK_CFG_REQ |
DPMAC_IRQ_LINK_CHANGED |
DPMAC_IRQ_LINK_UP_REQ |
DPMAC_IRQ_LINK_DOWN_REQ |
DPMAC_IRQ_EP_CHANGED;
error = DPAA2_CMD_MAC_SET_IRQ_MASK(dev, child, dpaa2_mcp_tk(cmd,
mac_token), DPMAC_IRQ_INDEX, irq_mask);
if (error) {
device_printf(dev, "Failed to set IRQ mask\n");
return (error);
}
/* Enable IRQ. */
error = DPAA2_CMD_MAC_SET_IRQ_ENABLE(dev, child, cmd, DPMAC_IRQ_INDEX,
true);
if (error) {
device_printf(dev, "Failed to enable IRQ\n");
return (error);
}
return (0);
}
/**
* @brief Allocate MSI interrupts for DPMAC.
*/
static int
dpaa2_mac_setup_msi(struct dpaa2_mac_softc *sc)
{
int val;
val = pci_msi_count(sc->dev);
if (val < DPAA2_MAC_MSI_COUNT)
device_printf(sc->dev, "MSI: actual=%d, expected=%d\n", val,
DPAA2_MAC_MSI_COUNT);
val = MIN(val, DPAA2_MAC_MSI_COUNT);
if (pci_alloc_msi(sc->dev, &val) != 0)
return (EINVAL);
for (int i = 0; i < val; i++)
sc->irq_rid[i] = i + 1;
return (0);
}
static void
dpaa2_mac_intr(void *arg)
{
struct dpaa2_mac_softc *sc = (struct dpaa2_mac_softc *) arg;
device_t child = sc->dev;
uint32_t status = ~0u; /* clear all IRQ status bits */
int error;
error = DPAA2_CMD_MAC_GET_IRQ_STATUS(sc->dev, child,
dpaa2_mcp_tk(sc->cmd, sc->mac_token), DPMAC_IRQ_INDEX, &status);
if (error)
device_printf(sc->dev, "%s: failed to obtain IRQ status: "
"error=%d\n", __func__, error);
}
static const char *
dpaa2_mac_ethif_to_str(enum dpaa2_mac_eth_if eth_if)
{
switch (eth_if) {
case DPAA2_MAC_ETH_IF_MII:
return ("MII");
case DPAA2_MAC_ETH_IF_RMII:
return ("RMII");
case DPAA2_MAC_ETH_IF_SMII:
return ("SMII");
case DPAA2_MAC_ETH_IF_GMII:
return ("GMII");
case DPAA2_MAC_ETH_IF_RGMII:
return ("RGMII");
case DPAA2_MAC_ETH_IF_SGMII:
return ("SGMII");
case DPAA2_MAC_ETH_IF_QSGMII:
return ("QSGMII");
case DPAA2_MAC_ETH_IF_XAUI:
return ("XAUI");
case DPAA2_MAC_ETH_IF_XFI:
return ("XFI");
case DPAA2_MAC_ETH_IF_CAUI:
return ("CAUI");
case DPAA2_MAC_ETH_IF_1000BASEX:
return ("1000BASE-X");
case DPAA2_MAC_ETH_IF_USXGMII:
return ("USXGMII");
default:
return ("unknown");
}
}
static const char *
dpaa2_mac_link_type_to_str(enum dpaa2_mac_link_type link_type)
{
switch (link_type) {
case DPAA2_MAC_LINK_TYPE_NONE:
return ("NONE");
case DPAA2_MAC_LINK_TYPE_FIXED:
return ("FIXED");
case DPAA2_MAC_LINK_TYPE_PHY:
return ("PHY");
case DPAA2_MAC_LINK_TYPE_BACKPLANE:
return ("BACKPLANE");
default:
return ("unknown");
}
}
static device_method_t dpaa2_mac_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, dpaa2_mac_probe),
DEVMETHOD(device_attach, dpaa2_mac_attach),
DEVMETHOD(device_detach, dpaa2_mac_detach),
DEVMETHOD_END
};
static driver_t dpaa2_mac_driver = {
"dpaa2_mac",
dpaa2_mac_methods,
sizeof(struct dpaa2_mac_softc),
};
DRIVER_MODULE(dpaa2_mac, dpaa2_rc, dpaa2_mac_driver, 0, 0);

124
sys/dev/dpaa2/dpaa2_mac.h Normal file
View File

@ -0,0 +1,124 @@
/*-
* 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_MAC_H
#define _DPAA2_MAC_H
#include <sys/rman.h>
#include <sys/bus.h>
#include <sys/queue.h>
#include <net/ethernet.h>
#include "dpaa2_types.h"
#include "dpaa2_mcp.h"
#define DPAA2_MAC_MAX_RESOURCES 1 /* Maximum resources per DPMAC: 1 DPMCP. */
#define DPAA2_MAC_MSI_COUNT 1 /* MSIs per DPMAC */
/* DPMAC link configuration options. */
#define DPAA2_MAC_LINK_OPT_AUTONEG ((uint64_t) 0x01u)
#define DPAA2_MAC_LINK_OPT_HALF_DUPLEX ((uint64_t) 0x02u)
#define DPAA2_MAC_LINK_OPT_PAUSE ((uint64_t) 0x04u)
#define DPAA2_MAC_LINK_OPT_ASYM_PAUSE ((uint64_t) 0x08u)
enum dpaa2_mac_eth_if {
DPAA2_MAC_ETH_IF_MII,
DPAA2_MAC_ETH_IF_RMII,
DPAA2_MAC_ETH_IF_SMII,
DPAA2_MAC_ETH_IF_GMII,
DPAA2_MAC_ETH_IF_RGMII,
DPAA2_MAC_ETH_IF_SGMII,
DPAA2_MAC_ETH_IF_QSGMII,
DPAA2_MAC_ETH_IF_XAUI,
DPAA2_MAC_ETH_IF_XFI,
DPAA2_MAC_ETH_IF_CAUI,
DPAA2_MAC_ETH_IF_1000BASEX,
DPAA2_MAC_ETH_IF_USXGMII
};
enum dpaa2_mac_link_type {
DPAA2_MAC_LINK_TYPE_NONE,
DPAA2_MAC_LINK_TYPE_FIXED,
DPAA2_MAC_LINK_TYPE_PHY,
DPAA2_MAC_LINK_TYPE_BACKPLANE
};
/**
* @brief Attributes of the DPMAC object.
*
* id: DPMAC object ID.
* max_rate: Maximum supported rate (in Mbps).
* eth_if: Type of the Ethernet interface.
* link_type: Type of the link.
*/
struct dpaa2_mac_attr {
uint32_t id;
uint32_t max_rate;
enum dpaa2_mac_eth_if eth_if;
enum dpaa2_mac_link_type link_type;
};
/**
* @brief Link state of the DPMAC object.
*/
struct dpaa2_mac_link_state {
uint64_t options;
uint64_t supported;
uint64_t advert;
uint32_t rate;
bool up;
bool state_valid;
};
/**
* @brief Software context for the DPAA2 MAC driver.
*
* dev: Device associated with this software context.
* addr: Physical address assigned to the DPMAC object.
* attr: Attributes of the DPMAC object.
*/
struct dpaa2_mac_softc {
device_t dev;
uint8_t addr[ETHER_ADDR_LEN];
struct resource *res[DPAA2_MAC_MAX_RESOURCES];
struct dpaa2_mac_attr attr;
/* Help to send commands to MC. */
struct dpaa2_cmd *cmd;
uint16_t rc_token;
uint16_t mac_token;
/* Interrupts. */
int irq_rid[DPAA2_MAC_MSI_COUNT];
struct resource *irq_res;
void *intr; /* interrupt handle */
};
extern struct resource_spec dpaa2_mac_spec[];
#endif /* _DPAA2_MAC_H */

971
sys/dev/dpaa2/dpaa2_mc.c Normal file
View File

@ -0,0 +1,971 @@
/*-
* 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 <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 "opt_acpi.h"
#include "opt_platform.h"
#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>
#ifdef DEV_ACPI
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#endif
#ifdef FDT
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/ofw_pci.h>
#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 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;
#ifdef DEV_ACPI
u_int xref, devid;
#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 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));

218
sys/dev/dpaa2/dpaa2_mc.h Normal file
View File

@ -0,0 +1,218 @@
/*-
* 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 <sys/rman.h>
#include <sys/bus.h>
#include <sys/queue.h>
#include <net/ethernet.h>
#include <dev/ofw/openfirm.h>
#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 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 */

View File

@ -0,0 +1,393 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* 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 <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 <machine/bus.h>
#include <machine/resource.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#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"
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", &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_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);

View File

@ -0,0 +1,399 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* 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 <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 <machine/bus.h>
#include <machine/resource.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/simplebus.h>
#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", &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 (!OF_hasprop(child, "pcs-handle"))
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_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);

152
sys/dev/dpaa2/dpaa2_mc_if.m Normal file
View File

@ -0,0 +1,152 @@
#-
# 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 <machine/bus.h>
#include <dev/dpaa2/dpaa2_mc.h>
/**
* @brief Interface of the DPAA2 Management Complex (MC) bus driver.
*
* It helps to manipulate DPAA2-specific resources (DPIOs, DPBPs, etc.)
*/
INTERFACE dpaa2_mc;
#
# Default implementation of the commands.
#
CODE {
static int
bypass_manage_dev(device_t dev, device_t dpaa2_dev, uint32_t flags)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_MANAGE_DEV(device_get_parent(dev),
dpaa2_dev, flags));
return (ENXIO);
}
static int
bypass_get_free_dev(device_t dev, device_t *dpaa2_dev,
enum dpaa2_dev_type devtype)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_GET_FREE_DEV(device_get_parent(dev),
dpaa2_dev, devtype));
return (ENXIO);
}
static int
bypass_get_dev(device_t dev, device_t *dpaa2_dev,
enum dpaa2_dev_type devtype, uint32_t obj_id)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_GET_DEV(device_get_parent(dev),
dpaa2_dev, devtype, obj_id));
return (ENXIO);
}
static int
bypass_get_shared_dev(device_t dev, device_t *dpaa2_dev,
enum dpaa2_dev_type devtype)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_GET_SHARED_DEV(device_get_parent(dev),
dpaa2_dev, devtype));
return (ENXIO);
}
static int
bypass_reserve_dev(device_t dev, device_t dpaa2_dev,
enum dpaa2_dev_type devtype)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_RESERVE_DEV(device_get_parent(dev),
dpaa2_dev, devtype));
return (ENXIO);
}
static int
bypass_release_dev(device_t dev, device_t dpaa2_dev,
enum dpaa2_dev_type devtype)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_RELEASE_DEV(device_get_parent(dev),
dpaa2_dev, devtype));
return (ENXIO);
}
static int
bypass_get_phy_dev(device_t dev, device_t *phy_dev, uint32_t id)
{
if (device_get_parent(dev) != NULL)
return (DPAA2_MC_GET_PHY_DEV(device_get_parent(dev),
phy_dev, id));
return (ENXIO);
}
}
METHOD int manage_dev {
device_t dev;
device_t dpaa2_dev;
uint32_t flags;
} DEFAULT bypass_manage_dev;
METHOD int get_free_dev {
device_t dev;
device_t *dpaa2_dev;
enum dpaa2_dev_type devtype;
} DEFAULT bypass_get_free_dev;
METHOD int get_dev {
device_t dev;
device_t *dpaa2_dev;
enum dpaa2_dev_type devtype;
uint32_t obj_id;
} DEFAULT bypass_get_dev;
METHOD int get_shared_dev {
device_t dev;
device_t *dpaa2_dev;
enum dpaa2_dev_type devtype;
} DEFAULT bypass_get_shared_dev;
METHOD int reserve_dev {
device_t dev;
device_t dpaa2_dev;
enum dpaa2_dev_type devtype;
} DEFAULT bypass_reserve_dev;
METHOD int release_dev {
device_t dev;
device_t dpaa2_dev;
enum dpaa2_dev_type devtype;
} DEFAULT bypass_release_dev;
METHOD int get_phy_dev {
device_t dev;
device_t *phy_dev;
uint32_t id;
} DEFAULT bypass_get_phy_dev;

318
sys/dev/dpaa2/dpaa2_mcp.c Normal file
View File

@ -0,0 +1,318 @@
/*-
* 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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* DPAA2 MC command portal and helper routines.
*/
#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/time.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/condvar.h>
#include <sys/lock.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include "pcib_if.h"
#include "pci_if.h"
#include "dpaa2_mcp.h"
#include "dpaa2_mc.h"
#include "dpaa2_cmd_if.h"
MALLOC_DEFINE(M_DPAA2_MCP, "dpaa2_mcp", "DPAA2 Management Complex Portal");
static struct resource_spec dpaa2_mcp_spec[] = {
{ SYS_RES_MEMORY, 0, RF_ACTIVE | RF_UNMAPPED },
RESOURCE_SPEC_END
};
int
dpaa2_mcp_init_portal(struct dpaa2_mcp **mcp, struct resource *res,
struct resource_map *map, uint16_t flags)
{
const int mflags = flags & DPAA2_PORTAL_NOWAIT_ALLOC
? (M_NOWAIT | M_ZERO) : (M_WAITOK | M_ZERO);
struct dpaa2_mcp *p;
if (!mcp || !res || !map)
return (DPAA2_CMD_STAT_EINVAL);
p = malloc(sizeof(struct dpaa2_mcp), M_DPAA2_MCP, mflags);
if (p == NULL)
return (DPAA2_CMD_STAT_NO_MEMORY);
mtx_init(&p->lock, "mcp_sleep_lock", NULL, MTX_DEF);
p->res = res;
p->map = map;
p->flags = flags;
p->rc_api_major = 0; /* DPRC API version to be cached later. */
p->rc_api_minor = 0;
*mcp = p;
return (0);
}
void
dpaa2_mcp_free_portal(struct dpaa2_mcp *mcp)
{
uint16_t flags;
KASSERT(mcp != NULL, ("%s: mcp is NULL", __func__));
DPAA2_MCP_LOCK(mcp, &flags);
mcp->flags |= DPAA2_PORTAL_DESTROYED;
DPAA2_MCP_UNLOCK(mcp);
/* Let threads stop using this portal. */
DELAY(DPAA2_PORTAL_TIMEOUT);
mtx_destroy(&mcp->lock);
free(mcp, M_DPAA2_MCP);
}
int
dpaa2_mcp_init_command(struct dpaa2_cmd **cmd, uint16_t flags)
{
const int mflags = flags & DPAA2_CMD_NOWAIT_ALLOC
? (M_NOWAIT | M_ZERO) : (M_WAITOK | M_ZERO);
struct dpaa2_cmd *c;
struct dpaa2_cmd_header *hdr;
if (!cmd)
return (DPAA2_CMD_STAT_EINVAL);
c = malloc(sizeof(struct dpaa2_cmd), M_DPAA2_MCP, mflags);
if (!c)
return (DPAA2_CMD_STAT_NO_MEMORY);
hdr = (struct dpaa2_cmd_header *) &c->header;
hdr->srcid = 0;
hdr->status = DPAA2_CMD_STAT_OK;
hdr->token = 0;
hdr->cmdid = 0;
hdr->flags_hw = DPAA2_CMD_DEF;
hdr->flags_sw = DPAA2_CMD_DEF;
if (flags & DPAA2_CMD_HIGH_PRIO)
hdr->flags_hw |= DPAA2_HW_FLAG_HIGH_PRIO;
if (flags & DPAA2_CMD_INTR_DIS)
hdr->flags_sw |= DPAA2_SW_FLAG_INTR_DIS;
for (uint32_t i = 0; i < DPAA2_CMD_PARAMS_N; i++)
c->params[i] = 0;
*cmd = c;
return (0);
}
void
dpaa2_mcp_free_command(struct dpaa2_cmd *cmd)
{
if (cmd != NULL)
free(cmd, M_DPAA2_MCP);
}
struct dpaa2_cmd *
dpaa2_mcp_tk(struct dpaa2_cmd *cmd, uint16_t token)
{
struct dpaa2_cmd_header *hdr;
if (cmd != NULL) {
hdr = (struct dpaa2_cmd_header *) &cmd->header;
hdr->token = token;
}
return (cmd);
}
struct dpaa2_cmd *
dpaa2_mcp_f(struct dpaa2_cmd *cmd, uint16_t flags)
{
struct dpaa2_cmd_header *hdr;
if (cmd) {
hdr = (struct dpaa2_cmd_header *) &cmd->header;
hdr->flags_hw = DPAA2_CMD_DEF;
hdr->flags_sw = DPAA2_CMD_DEF;
if (flags & DPAA2_CMD_HIGH_PRIO)
hdr->flags_hw |= DPAA2_HW_FLAG_HIGH_PRIO;
if (flags & DPAA2_CMD_INTR_DIS)
hdr->flags_sw |= DPAA2_SW_FLAG_INTR_DIS;
}
return (cmd);
}
static int
dpaa2_mcp_probe(device_t dev)
{
/* DPMCP device will be added by the parent resource container. */
device_set_desc(dev, "DPAA2 MC portal");
return (BUS_PROBE_DEFAULT);
}
static int
dpaa2_mcp_detach(device_t dev)
{
return (0);
}
static int
dpaa2_mcp_attach(device_t dev)
{
device_t pdev = device_get_parent(dev);
device_t child = dev;
struct dpaa2_mcp_softc *sc = device_get_softc(dev);
struct dpaa2_devinfo *rcinfo = device_get_ivars(pdev);
struct dpaa2_devinfo *dinfo = device_get_ivars(dev);
struct dpaa2_cmd *cmd;
struct dpaa2_mcp *portal;
struct resource_map_request req;
uint16_t rc_token, mcp_token;
int error;
sc->dev = dev;
error = bus_alloc_resources(sc->dev, dpaa2_mcp_spec, sc->res);
if (error) {
device_printf(dev, "%s: failed to allocate resources\n",
__func__);
goto err_exit;
}
/* 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]));
goto err_exit;
}
/* 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, "%s: failed to map MC portal memory\n",
__func__);
goto err_exit;
}
/* Initialize portal to send commands to MC. */
error = dpaa2_mcp_init_portal(&portal, sc->res[0], &sc->map[0],
DPAA2_PORTAL_DEF);
if (error) {
device_printf(dev, "%s: failed to initialize dpaa2_mcp: "
"error=%d\n", __func__, error);
goto err_exit;
}
/* Allocate a command to send to MC hardware. */
error = dpaa2_mcp_init_command(&cmd, DPAA2_CMD_DEF);
if (error) {
device_printf(dev, "%s: failed to allocate dpaa2_cmd: "
"error=%d\n", __func__, error);
goto err_exit;
}
/* Open resource container and DPMCP object. */
error = DPAA2_CMD_RC_OPEN(dev, child, cmd, rcinfo->id, &rc_token);
if (error) {
device_printf(dev, "%s: failed to open DPRC: error=%d\n",
__func__, error);
goto err_free_cmd;
}
error = DPAA2_CMD_MCP_OPEN(dev, child, cmd, dinfo->id, &mcp_token);
if (error) {
device_printf(dev, "%s: failed to open DPMCP: id=%d, error=%d\n",
__func__, dinfo->id, error);
goto err_close_rc;
}
/* Prepare DPMCP object. */
error = DPAA2_CMD_MCP_RESET(dev, child, cmd);
if (error) {
device_printf(dev, "%s: failed to reset DPMCP: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
goto err_close_mcp;
}
/* Close the DPMCP object and the resource container. */
error = DPAA2_CMD_MCP_CLOSE(dev, child, cmd);
if (error) {
device_printf(dev, "%s: failed to close DPMCP: id=%d, "
"error=%d\n", __func__, dinfo->id, error);
goto err_close_rc;
}
error = DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(cmd, rc_token));
if (error) {
device_printf(dev, "%s: failed to close DPRC: error=%d\n",
__func__, error);
goto err_free_cmd;
}
dpaa2_mcp_free_command(cmd);
dinfo->portal = portal;
return (0);
err_close_mcp:
DPAA2_CMD_MCP_CLOSE(dev, child, dpaa2_mcp_tk(cmd, mcp_token));
err_close_rc:
DPAA2_CMD_RC_CLOSE(dev, child, dpaa2_mcp_tk(cmd, rc_token));
err_free_cmd:
dpaa2_mcp_free_command(cmd);
err_exit:
dpaa2_mcp_detach(dev);
return (ENXIO);
}
static device_method_t dpaa2_mcp_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, dpaa2_mcp_probe),
DEVMETHOD(device_attach, dpaa2_mcp_attach),
DEVMETHOD(device_detach, dpaa2_mcp_detach),
DEVMETHOD_END
};
static driver_t dpaa2_mcp_driver = {
"dpaa2_mcp",
dpaa2_mcp_methods,
sizeof(struct dpaa2_mcp_softc),
};
DRIVER_MODULE(dpaa2_mcp, dpaa2_rc, dpaa2_mcp_driver, 0, 0);

449
sys/dev/dpaa2/dpaa2_mcp.h Normal file
View File

@ -0,0 +1,449 @@
/*-
* 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_MCP_H
#define _DPAA2_MCP_H
#include <sys/rman.h>
#include <sys/condvar.h>
#include <sys/mutex.h>
#include "dpaa2_types.h"
/*
* DPAA2 MC command interface helper routines.
*/
#define DPAA2_PORTAL_TIMEOUT 100000 /* us */
#define DPAA2_MCP_MEM_WIDTH 0x40 /* Minimal size of the MC portal. */
#define DPAA2_MCP_MAX_RESOURCES 1 /* resources per DPMCP: 1 SYS_MEM */
/*
* Portal flags.
*
* TODO: Use the same flags for both MC and software portals.
*/
#define DPAA2_PORTAL_DEF 0x0u
#define DPAA2_PORTAL_NOWAIT_ALLOC 0x2u /* Do not sleep during init */
#define DPAA2_PORTAL_LOCKED 0x4000u /* Wait till portal's unlocked */
#define DPAA2_PORTAL_DESTROYED 0x8000u /* Terminate any operations */
/* Command flags. */
#define DPAA2_CMD_DEF 0x0u
#define DPAA2_CMD_HIGH_PRIO 0x80u /* High priority command */
#define DPAA2_CMD_INTR_DIS 0x100u /* Disable cmd finished intr */
#define DPAA2_CMD_NOWAIT_ALLOC 0x8000u /* Do not sleep during init */
/* DPAA2 command return codes. */
#define DPAA2_CMD_STAT_OK 0x0 /* Set by MC on success */
#define DPAA2_CMD_STAT_READY 0x1 /* Ready to be processed */
#define DPAA2_CMD_STAT_AUTH_ERR 0x3 /* Illegal object-portal-icid */
#define DPAA2_CMD_STAT_NO_PRIVILEGE 0x4 /* No privilege */
#define DPAA2_CMD_STAT_DMA_ERR 0x5 /* DMA or I/O error */
#define DPAA2_CMD_STAT_CONFIG_ERR 0x6 /* Invalid/conflicting params */
#define DPAA2_CMD_STAT_TIMEOUT 0x7 /* Command timed out */
#define DPAA2_CMD_STAT_NO_RESOURCE 0x8 /* No DPAA2 resources */
#define DPAA2_CMD_STAT_NO_MEMORY 0x9 /* No memory available */
#define DPAA2_CMD_STAT_BUSY 0xA /* Device is busy */
#define DPAA2_CMD_STAT_UNSUPPORTED_OP 0xB /* Unsupported operation */
#define DPAA2_CMD_STAT_INVALID_STATE 0xC /* Invalid state */
/* Driver-specific return codes. */
#define DPAA2_CMD_STAT_UNKNOWN_OBJ 0xFD /* Unknown DPAA2 object. */
#define DPAA2_CMD_STAT_EINVAL 0xFE /* Invalid argument */
#define DPAA2_CMD_STAT_ERR 0xFF /* General error */
/* Object's memory region flags. */
#define DPAA2_RC_REG_CACHEABLE 0x1 /* Cacheable memory mapping */
#define DPAA2_HW_FLAG_HIGH_PRIO 0x80u
#define DPAA2_SW_FLAG_INTR_DIS 0x01u
#define DPAA2_CMD_PARAMS_N 7u
#define DPAA2_LABEL_SZ 16
/* ------------------------- MNG command IDs -------------------------------- */
#define CMD_MNG_BASE_VERSION 1
#define CMD_MNG_ID_OFFSET 4
#define CMD_MNG(id) (((id) << CMD_MNG_ID_OFFSET) | CMD_MNG_BASE_VERSION)
#define CMDID_MNG_GET_VER CMD_MNG(0x831)
#define CMDID_MNG_GET_SOC_VER CMD_MNG(0x832)
#define CMDID_MNG_GET_CONT_ID CMD_MNG(0x830)
/* ------------------------- DPRC command IDs ------------------------------- */
#define CMD_RC_BASE_VERSION 1
#define CMD_RC_2ND_VERSION 2
#define CMD_RC_3RD_VERSION 3
#define CMD_RC_ID_OFFSET 4
#define CMD_RC(id) (((id) << CMD_RC_ID_OFFSET) | CMD_RC_BASE_VERSION)
#define CMD_RC_V2(id) (((id) << CMD_RC_ID_OFFSET) | CMD_RC_2ND_VERSION)
#define CMD_RC_V3(id) (((id) << CMD_RC_ID_OFFSET) | CMD_RC_3RD_VERSION)
#define CMDID_RC_OPEN CMD_RC(0x805)
#define CMDID_RC_CLOSE CMD_RC(0x800)
#define CMDID_RC_GET_API_VERSION CMD_RC(0xA05)
#define CMDID_RC_GET_ATTR CMD_RC(0x004)
#define CMDID_RC_RESET_CONT CMD_RC(0x005)
#define CMDID_RC_RESET_CONT_V2 CMD_RC_V2(0x005)
#define CMDID_RC_SET_IRQ CMD_RC(0x010)
#define CMDID_RC_SET_IRQ_ENABLE CMD_RC(0x012)
#define CMDID_RC_SET_IRQ_MASK CMD_RC(0x014)
#define CMDID_RC_GET_IRQ_STATUS CMD_RC(0x016)
#define CMDID_RC_CLEAR_IRQ_STATUS CMD_RC(0x017)
#define CMDID_RC_GET_CONT_ID CMD_RC(0x830)
#define CMDID_RC_GET_OBJ_COUNT CMD_RC(0x159)
#define CMDID_RC_GET_OBJ CMD_RC(0x15A)
#define CMDID_RC_GET_OBJ_DESC CMD_RC(0x162)
#define CMDID_RC_GET_OBJ_REG CMD_RC(0x15E)
#define CMDID_RC_GET_OBJ_REG_V2 CMD_RC_V2(0x15E)
#define CMDID_RC_GET_OBJ_REG_V3 CMD_RC_V3(0x15E)
#define CMDID_RC_SET_OBJ_IRQ CMD_RC(0x15F)
#define CMDID_RC_GET_CONN CMD_RC(0x16C)
/* ------------------------- DPIO command IDs ------------------------------- */
#define CMD_IO_BASE_VERSION 1
#define CMD_IO_ID_OFFSET 4
#define CMD_IO(id) (((id) << CMD_IO_ID_OFFSET) | CMD_IO_BASE_VERSION)
#define CMDID_IO_OPEN CMD_IO(0x803)
#define CMDID_IO_CLOSE CMD_IO(0x800)
#define CMDID_IO_ENABLE CMD_IO(0x002)
#define CMDID_IO_DISABLE CMD_IO(0x003)
#define CMDID_IO_GET_ATTR CMD_IO(0x004)
#define CMDID_IO_RESET CMD_IO(0x005)
#define CMDID_IO_SET_IRQ_ENABLE CMD_IO(0x012)
#define CMDID_IO_SET_IRQ_MASK CMD_IO(0x014)
#define CMDID_IO_GET_IRQ_STATUS CMD_IO(0x016)
#define CMDID_IO_ADD_STATIC_DQ_CHAN CMD_IO(0x122)
/* ------------------------- DPNI command IDs ------------------------------- */
#define CMD_NI_BASE_VERSION 1
#define CMD_NI_2ND_VERSION 2
#define CMD_NI_4TH_VERSION 4
#define CMD_NI_ID_OFFSET 4
#define CMD_NI(id) (((id) << CMD_NI_ID_OFFSET) | CMD_NI_BASE_VERSION)
#define CMD_NI_V2(id) (((id) << CMD_NI_ID_OFFSET) | CMD_NI_2ND_VERSION)
#define CMD_NI_V4(id) (((id) << CMD_NI_ID_OFFSET) | CMD_NI_4TH_VERSION)
#define CMDID_NI_OPEN CMD_NI(0x801)
#define CMDID_NI_CLOSE CMD_NI(0x800)
#define CMDID_NI_ENABLE CMD_NI(0x002)
#define CMDID_NI_DISABLE CMD_NI(0x003)
#define CMDID_NI_GET_API_VER CMD_NI(0xA01)
#define CMDID_NI_RESET CMD_NI(0x005)
#define CMDID_NI_GET_ATTR CMD_NI(0x004)
#define CMDID_NI_SET_BUF_LAYOUT CMD_NI(0x265)
#define CMDID_NI_GET_TX_DATA_OFF CMD_NI(0x212)
#define CMDID_NI_GET_PORT_MAC_ADDR CMD_NI(0x263)
#define CMDID_NI_SET_PRIM_MAC_ADDR CMD_NI(0x224)
#define CMDID_NI_GET_PRIM_MAC_ADDR CMD_NI(0x225)
#define CMDID_NI_SET_LINK_CFG CMD_NI(0x21A)
#define CMDID_NI_GET_LINK_CFG CMD_NI(0x278)
#define CMDID_NI_GET_LINK_STATE CMD_NI(0x215)
#define CMDID_NI_SET_QOS_TABLE CMD_NI(0x240)
#define CMDID_NI_CLEAR_QOS_TABLE CMD_NI(0x243)
#define CMDID_NI_SET_POOLS CMD_NI(0x200)
#define CMDID_NI_SET_ERR_BEHAVIOR CMD_NI(0x20B)
#define CMDID_NI_GET_QUEUE CMD_NI(0x25F)
#define CMDID_NI_SET_QUEUE CMD_NI(0x260)
#define CMDID_NI_GET_QDID CMD_NI(0x210)
#define CMDID_NI_ADD_MAC_ADDR CMD_NI(0x226)
#define CMDID_NI_REMOVE_MAC_ADDR CMD_NI(0x227)
#define CMDID_NI_CLEAR_MAC_FILTERS CMD_NI(0x228)
#define CMDID_NI_SET_MFL CMD_NI(0x216)
#define CMDID_NI_SET_OFFLOAD CMD_NI(0x26C)
#define CMDID_NI_SET_IRQ_MASK CMD_NI(0x014)
#define CMDID_NI_SET_IRQ_ENABLE CMD_NI(0x012)
#define CMDID_NI_GET_IRQ_STATUS CMD_NI(0x016)
#define CMDID_NI_SET_UNI_PROMISC CMD_NI(0x222)
#define CMDID_NI_SET_MULTI_PROMISC CMD_NI(0x220)
#define CMDID_NI_GET_STATISTICS CMD_NI(0x25D)
#define CMDID_NI_SET_RX_TC_DIST CMD_NI(0x235)
/* ------------------------- DPBP command IDs ------------------------------- */
#define CMD_BP_BASE_VERSION 1
#define CMD_BP_ID_OFFSET 4
#define CMD_BP(id) (((id) << CMD_BP_ID_OFFSET) | CMD_BP_BASE_VERSION)
#define CMDID_BP_OPEN CMD_BP(0x804)
#define CMDID_BP_CLOSE CMD_BP(0x800)
#define CMDID_BP_ENABLE CMD_BP(0x002)
#define CMDID_BP_DISABLE CMD_BP(0x003)
#define CMDID_BP_GET_ATTR CMD_BP(0x004)
#define CMDID_BP_RESET CMD_BP(0x005)
/* ------------------------- DPMAC command IDs ------------------------------ */
#define CMD_MAC_BASE_VERSION 1
#define CMD_MAC_2ND_VERSION 2
#define CMD_MAC_ID_OFFSET 4
#define CMD_MAC(id) (((id) << CMD_MAC_ID_OFFSET) | CMD_MAC_BASE_VERSION)
#define CMD_MAC_V2(id) (((id) << CMD_MAC_ID_OFFSET) | CMD_MAC_2ND_VERSION)
#define CMDID_MAC_OPEN CMD_MAC(0x80C)
#define CMDID_MAC_CLOSE CMD_MAC(0x800)
#define CMDID_MAC_RESET CMD_MAC(0x005)
#define CMDID_MAC_MDIO_READ CMD_MAC(0x0C0)
#define CMDID_MAC_MDIO_WRITE CMD_MAC(0x0C1)
#define CMDID_MAC_GET_ADDR CMD_MAC(0x0C5)
#define CMDID_MAC_GET_ATTR CMD_MAC(0x004)
#define CMDID_MAC_SET_LINK_STATE CMD_MAC_V2(0x0C3)
#define CMDID_MAC_SET_IRQ_MASK CMD_MAC(0x014)
#define CMDID_MAC_SET_IRQ_ENABLE CMD_MAC(0x012)
#define CMDID_MAC_GET_IRQ_STATUS CMD_MAC(0x016)
/* ------------------------- DPCON command IDs ------------------------------ */
#define CMD_CON_BASE_VERSION 1
#define CMD_CON_ID_OFFSET 4
#define CMD_CON(id) (((id) << CMD_CON_ID_OFFSET) | CMD_CON_BASE_VERSION)
#define CMDID_CON_OPEN CMD_CON(0x808)
#define CMDID_CON_CLOSE CMD_CON(0x800)
#define CMDID_CON_ENABLE CMD_CON(0x002)
#define CMDID_CON_DISABLE CMD_CON(0x003)
#define CMDID_CON_GET_ATTR CMD_CON(0x004)
#define CMDID_CON_RESET CMD_CON(0x005)
#define CMDID_CON_SET_NOTIF CMD_CON(0x100)
/* ------------------------- DPMCP command IDs ------------------------------ */
#define CMD_MCP_BASE_VERSION 1
#define CMD_MCP_2ND_VERSION 2
#define CMD_MCP_ID_OFFSET 4
#define CMD_MCP(id) (((id) << CMD_MCP_ID_OFFSET) | CMD_MCP_BASE_VERSION)
#define CMD_MCP_V2(id) (((id) << CMD_MCP_ID_OFFSET) | CMD_MCP_2ND_VERSION)
#define CMDID_MCP_CREATE CMD_MCP_V2(0x90B)
#define CMDID_MCP_DESTROY CMD_MCP(0x98B)
#define CMDID_MCP_OPEN CMD_MCP(0x80B)
#define CMDID_MCP_CLOSE CMD_MCP(0x800)
#define CMDID_MCP_RESET CMD_MCP(0x005)
#define DPAA2_MCP_LOCK(__mcp, __flags) do { \
mtx_assert(&(__mcp)->lock, MA_NOTOWNED); \
mtx_lock(&(__mcp)->lock); \
*(__flags) = (__mcp)->flags; \
(__mcp)->flags |= DPAA2_PORTAL_LOCKED; \
} while (0)
#define DPAA2_MCP_UNLOCK(__mcp) do { \
mtx_assert(&(__mcp)->lock, MA_OWNED); \
(__mcp)->flags &= ~DPAA2_PORTAL_LOCKED; \
mtx_unlock(&(__mcp)->lock); \
} while (0)
enum dpaa2_rc_region_type {
DPAA2_RC_REG_MC_PORTAL,
DPAA2_RC_REG_QBMAN_PORTAL
};
/**
* @brief Helper object to interact with the MC portal.
*
* res: Unmapped portal's I/O memory.
* map: Mapped portal's I/O memory.
* lock: Lock to send a command to the portal and wait for the
* result.
* flags: Current state of the object.
* rc_api_major: Major version of the DPRC API.
* rc_api_minor: Minor version of the DPRC API.
*/
struct dpaa2_mcp {
struct resource *res;
struct resource_map *map;
struct mtx lock;
uint16_t flags;
uint16_t rc_api_major;
uint16_t rc_api_minor;
};
/**
* @brief Command object holds data to be written to the MC portal.
*
* header: 8 least significant bytes of the MC portal.
* params: Parameters to pass together with the command to MC. Might keep
* command execution results.
*
* NOTE: 64 bytes.
*/
struct dpaa2_cmd {
uint64_t header;
uint64_t params[DPAA2_CMD_PARAMS_N];
};
/**
* @brief Helper object to access fields of the MC command header.
*
* srcid: The SoC architected source ID of the submitter. This field is
* reserved and cannot be written by the driver.
* flags_hw: Bits from 8 to 15 of the command header. Most of them are
* reserved at the moment.
* status: Command ready/status. This field is used as the handshake field
* between MC and the driver. MC reports command completion with
* success/error codes in this field.
* flags_sw: ...
* token: ...
* cmdid: ...
*
* NOTE: 8 bytes.
*/
struct dpaa2_cmd_header {
uint8_t srcid;
uint8_t flags_hw;
uint8_t status;
uint8_t flags_sw;
uint16_t token;
uint16_t cmdid;
} __packed;
/**
* @brief Information about DPAA2 object.
*
* id: ID of a logical object resource.
* vendor: Object vendor identifier.
* irq_count: Number of interrupts supported by the object.
* reg_count: Number of mappable regions supported by the object.
* state: Object state (combination of states).
* ver_major: Major version of the object.
* ver_minor: Minor version of the object.
* flags: Object attributes flags.
* type: ...
* label: ...
*/
struct dpaa2_obj {
uint32_t id;
uint16_t vendor;
uint8_t irq_count;
uint8_t reg_count;
uint32_t state;
uint16_t ver_major;
uint16_t ver_minor;
uint16_t flags;
uint8_t label[DPAA2_LABEL_SZ];
enum dpaa2_dev_type type;
};
/**
* @brief Attributes of the DPRC object.
*
* cont_id: Container ID.
* portal_id: Container's portal ID.
* options: Container's options as set at container's creation.
* icid: Container's isolation context ID.
*/
struct dpaa2_rc_attr {
uint32_t cont_id;
uint32_t portal_id;
uint32_t options;
uint32_t icid;
};
/**
* @brief Description of the object's memory region.
*
* base_paddr: Region base physical address.
* base_offset: Region base offset.
* size: Region size (in bytes).
* flags: Region flags (cacheable, etc.)
* type: Type of a software portal this region belongs to.
*/
struct dpaa2_rc_obj_region {
uint64_t base_paddr;
uint64_t base_offset;
uint32_t size;
uint32_t flags;
enum dpaa2_rc_region_type type;
};
/**
* @brief DPAA2 endpoint descriptor.
*
* obj_id: Endpoint object ID.
* if_id: Interface ID; for endpoints with multiple interfaces
* (DPSW, DPDMUX), 0 - otherwise.
* type: Endpoint object type, null-terminated string.
*/
struct dpaa2_ep_desc {
uint32_t obj_id;
uint32_t if_id;
enum dpaa2_dev_type type;
};
/**
* @brief Configuration of the channel data availability notification (CDAN).
*
* qman_ctx: Context value provided with each CDAN message.
* dpio_id: DPIO object ID configured with a notification channel.
* prior: Priority selection within the DPIO channel; valid values
* are 0-7, depending on the number of priorities in that channel.
*/
struct dpaa2_con_notif_cfg {
uint64_t qman_ctx;
uint32_t dpio_id;
uint8_t prior;
};
/**
* @brief Attributes of the DPMCP object.
*
* id: DPMCP object ID.
* options: Options of the MC portal (disabled high-prio commands, etc.).
*/
struct dpaa2_mcp_attr {
uint32_t id;
uint32_t options;
};
/**
* @brief Software context for the DPAA2 MC portal.
*/
struct dpaa2_mcp_softc {
device_t dev;
struct dpaa2_mcp_attr attr;
struct resource *res[DPAA2_MCP_MAX_RESOURCES];
struct resource_map map[DPAA2_MCP_MAX_RESOURCES];
};
int dpaa2_mcp_init_portal(struct dpaa2_mcp **mcp, struct resource *res,
struct resource_map *map, uint16_t flags);
int dpaa2_mcp_init_command(struct dpaa2_cmd **cmd, uint16_t flags);
void dpaa2_mcp_free_portal(struct dpaa2_mcp *mcp);
void dpaa2_mcp_free_command(struct dpaa2_cmd *cmd);
/* to quickly update command token */
struct dpaa2_cmd *dpaa2_mcp_tk(struct dpaa2_cmd *cmd, uint16_t token);
/* to quickly update command flags */
struct dpaa2_cmd *dpaa2_mcp_f(struct dpaa2_cmd *cmd, uint16_t flags);
#endif /* _DPAA2_MCP_H */

3671
sys/dev/dpaa2/dpaa2_ni.c Normal file

File diff suppressed because it is too large Load Diff

607
sys/dev/dpaa2/dpaa2_ni.h Normal file
View File

@ -0,0 +1,607 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright © 2021-2022 Dmitry Salychev
* Copyright © 2022 Mathew McBride
*
* 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_NI_H
#define _DPAA2_NI_H
#include <sys/rman.h>
#include <sys/bus.h>
#include <sys/queue.h>
#include <sys/taskqueue.h>
#include <sys/mbuf.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/buf_ring.h>
#include <sys/proc.h>
#include <sys/mutex.h>
#include <net/if.h>
#include <net/ethernet.h>
#include <net/if_media.h>
#include "dpaa2_types.h"
#include "dpaa2_mcp.h"
#include "dpaa2_swp.h"
#include "dpaa2_io.h"
#include "dpaa2_mac.h"
#include "dpaa2_ni_dpkg.h"
/* Name of the DPAA2 network interface. */
#define DPAA2_NI_IFNAME "dpni"
/* Maximum resources per DPNI: 16 DPIOs + 16 DPCONs + 1 DPBP + 1 DPMCP. */
#define DPAA2_NI_MAX_RESOURCES 34
#define DPAA2_NI_MSI_COUNT 1 /* MSIs per DPNI */
#define DPAA2_NI_MAX_CHANNELS 16 /* to distribute ingress traffic to cores */
#define DPAA2_NI_MAX_TCS 8 /* traffic classes per DPNI */
#define DPAA2_NI_MAX_POOLS 8 /* buffer pools per DPNI */
/* Maximum number of Rx buffers. */
#define DPAA2_NI_BUFS_INIT (50u * DPAA2_SWP_BUFS_PER_CMD)
#define DPAA2_NI_BUFS_MAX (1 << 15) /* 15 bits for buffer index max. */
/* Maximum number of buffers allocated per Tx ring. */
#define DPAA2_NI_BUFS_PER_TX (1 << 7)
#define DPAA2_NI_MAX_BPTX (1 << 8) /* 8 bits for buffer index max. */
/* Number of the DPNI statistics counters. */
#define DPAA2_NI_STAT_COUNTERS 7u
#define DPAA2_NI_STAT_SYSCTLS 9u
/* Error and status bits in the frame annotation status word. */
#define DPAA2_NI_FAS_DISC 0x80000000 /* debug frame */
#define DPAA2_NI_FAS_MS 0x40000000 /* MACSEC frame */
#define DPAA2_NI_FAS_PTP 0x08000000
#define DPAA2_NI_FAS_MC 0x04000000 /* Ethernet multicast frame */
#define DPAA2_NI_FAS_BC 0x02000000 /* Ethernet broadcast frame */
#define DPAA2_NI_FAS_KSE 0x00040000
#define DPAA2_NI_FAS_EOFHE 0x00020000
#define DPAA2_NI_FAS_MNLE 0x00010000
#define DPAA2_NI_FAS_TIDE 0x00008000
#define DPAA2_NI_FAS_PIEE 0x00004000
#define DPAA2_NI_FAS_FLE 0x00002000 /* Frame length error */
#define DPAA2_NI_FAS_FPE 0x00001000 /* Frame physical error */
#define DPAA2_NI_FAS_PTE 0x00000080
#define DPAA2_NI_FAS_ISP 0x00000040
#define DPAA2_NI_FAS_PHE 0x00000020
#define DPAA2_NI_FAS_BLE 0x00000010
#define DPAA2_NI_FAS_L3CV 0x00000008 /* L3 csum validation performed */
#define DPAA2_NI_FAS_L3CE 0x00000004 /* L3 csum error */
#define DPAA2_NI_FAS_L4CV 0x00000002 /* L4 csum validation performed */
#define DPAA2_NI_FAS_L4CE 0x00000001 /* L4 csum error */
/* Mask for errors on the ingress path. */
#define DPAA2_NI_FAS_RX_ERR_MASK (DPAA2_NI_FAS_KSE | \
DPAA2_NI_FAS_EOFHE | \
DPAA2_NI_FAS_MNLE | \
DPAA2_NI_FAS_TIDE | \
DPAA2_NI_FAS_PIEE | \
DPAA2_NI_FAS_FLE | \
DPAA2_NI_FAS_FPE | \
DPAA2_NI_FAS_PTE | \
DPAA2_NI_FAS_ISP | \
DPAA2_NI_FAS_PHE | \
DPAA2_NI_FAS_BLE | \
DPAA2_NI_FAS_L3CE | \
DPAA2_NI_FAS_L4CE \
)
/* Option bits to select specific queue configuration options to apply. */
#define DPAA2_NI_QUEUE_OPT_USER_CTX 0x00000001
#define DPAA2_NI_QUEUE_OPT_DEST 0x00000002
#define DPAA2_NI_QUEUE_OPT_FLC 0x00000004
#define DPAA2_NI_QUEUE_OPT_HOLD_ACTIVE 0x00000008
#define DPAA2_NI_QUEUE_OPT_SET_CGID 0x00000040
#define DPAA2_NI_QUEUE_OPT_CLEAR_CGID 0x00000080
/* DPNI link configuration options. */
#define DPAA2_NI_LINK_OPT_AUTONEG ((uint64_t) 0x01u)
#define DPAA2_NI_LINK_OPT_HALF_DUPLEX ((uint64_t) 0x02u)
#define DPAA2_NI_LINK_OPT_PAUSE ((uint64_t) 0x04u)
#define DPAA2_NI_LINK_OPT_ASYM_PAUSE ((uint64_t) 0x08u)
#define DPAA2_NI_LINK_OPT_PFC_PAUSE ((uint64_t) 0x10u)
/*
* Number of times to retry a frame enqueue before giving up. Value determined
* empirically, in order to minimize the number of frames dropped on Tx.
*/
#define DPAA2_NI_ENQUEUE_RETRIES 10
enum dpaa2_ni_queue_type {
DPAA2_NI_QUEUE_RX = 0,
DPAA2_NI_QUEUE_TX,
DPAA2_NI_QUEUE_TX_CONF,
DPAA2_NI_QUEUE_RX_ERR
};
enum dpaa2_ni_dest_type {
DPAA2_NI_DEST_NONE = 0,
DPAA2_NI_DEST_DPIO,
DPAA2_NI_DEST_DPCON
};
enum dpaa2_ni_ofl_type {
DPAA2_NI_OFL_RX_L3_CSUM = 0,
DPAA2_NI_OFL_RX_L4_CSUM,
DPAA2_NI_OFL_TX_L3_CSUM,
DPAA2_NI_OFL_TX_L4_CSUM,
DPAA2_NI_OFL_FLCTYPE_HASH /* FD flow context for AIOP/CTLU */
};
/**
* @brief DPNI ingress traffic distribution mode.
*/
enum dpaa2_ni_dist_mode {
DPAA2_NI_DIST_MODE_NONE = 0,
DPAA2_NI_DIST_MODE_HASH,
DPAA2_NI_DIST_MODE_FS
};
/**
* @brief DPNI behavior in case of errors.
*/
enum dpaa2_ni_err_action {
DPAA2_NI_ERR_DISCARD = 0,
DPAA2_NI_ERR_CONTINUE,
DPAA2_NI_ERR_SEND_TO_ERROR_QUEUE
};
struct dpaa2_ni_channel;
struct dpaa2_ni_fq;
/**
* @brief Attributes of the DPNI object.
*
* options: ...
* wriop_ver: Revision of the underlying WRIOP hardware block.
*/
struct dpaa2_ni_attr {
uint32_t options;
uint16_t wriop_ver;
struct {
uint16_t fs;
uint8_t mac;
uint8_t vlan;
uint8_t qos;
} entries;
struct {
uint8_t queues;
uint8_t rx_tcs;
uint8_t tx_tcs;
uint8_t channels;
uint8_t cgs;
} num;
struct {
uint8_t fs;
uint8_t qos;
} key_size;
};
/**
* @brief Tx ring.
*
* fq: Parent (TxConf) frame queue.
* fqid: ID of the logical Tx queue.
* mbuf_br: Ring buffer for mbufs to transmit.
* mbuf_lock: Lock for the ring buffer.
*/
struct dpaa2_ni_tx_ring {
struct dpaa2_ni_fq *fq;
uint32_t fqid;
uint32_t txid; /* Tx ring index */
/* Ring buffer for indexes in "buf" array. */
struct buf_ring *idx_br;
struct mtx lock;
/* Buffers to DMA load/unload Tx mbufs. */
struct dpaa2_buf buf[DPAA2_NI_BUFS_PER_TX];
};
/**
* @brief A Frame Queue is the basic queuing structure used by the QMan.
*
* It comprises a list of frame descriptors (FDs), so it can be thought of
* as a queue of frames.
*
* NOTE: When frames on a FQ are ready to be processed, the FQ is enqueued
* onto a work queue (WQ).
*
* fqid: Frame queue ID, can be used to enqueue/dequeue or execute other
* commands on the queue through DPIO.
* txq_n: Number of configured Tx queues.
* tx_fqid: Frame queue IDs of the Tx queues which belong to the same flowid.
* Note that Tx queues are logical queues and not all management
* commands are available on these queue types.
* qdbin: Queue destination bin. Can be used with the DPIO enqueue
* operation based on QDID, QDBIN and QPRI. Note that all Tx queues
* with the same flowid have the same destination bin.
*/
struct dpaa2_ni_fq {
int (*consume)(struct dpaa2_ni_channel *,
struct dpaa2_ni_fq *, struct dpaa2_fd *);
struct dpaa2_ni_channel *chan;
uint32_t fqid;
uint16_t flowid;
uint8_t tc;
enum dpaa2_ni_queue_type type;
/* Optional fields (for TxConf queue). */
struct dpaa2_ni_tx_ring tx_rings[DPAA2_NI_MAX_TCS];
uint32_t tx_qdbin;
} __aligned(CACHE_LINE_SIZE);
/**
* @brief QBMan channel to process ingress traffic (Rx, Tx conf).
*
* NOTE: Several WQs are organized into a single WQ Channel.
*/
struct dpaa2_ni_channel {
device_t ni_dev;
device_t io_dev;
device_t con_dev;
uint16_t id;
uint16_t flowid;
/* For debug purposes only! */
uint64_t tx_frames;
uint64_t tx_dropped;
/* Context to configure CDAN. */
struct dpaa2_io_notif_ctx ctx;
/* Channel storage (to keep responses from VDQ command). */
struct dpaa2_buf store;
uint32_t store_sz; /* in frames */
uint32_t store_idx; /* frame index */
/* Recycled buffers to release back to the pool. */
uint32_t recycled_n;
bus_addr_t recycled[DPAA2_SWP_BUFS_PER_CMD];
/* Frame queues */
uint32_t rxq_n;
struct dpaa2_ni_fq rx_queues[DPAA2_NI_MAX_TCS];
struct dpaa2_ni_fq txc_queue;
};
/**
* @brief Configuration of the network interface queue.
*
* NOTE: This configuration is used to obtain information of a queue by
* DPNI_GET_QUEUE command and update it by DPNI_SET_QUEUE one.
*
* It includes binding of the queue to a DPIO or DPCON object to receive
* notifications and traffic on the CPU.
*
* user_ctx: (r/w) User defined data, presented along with the frames
* being dequeued from this queue.
* flow_ctx: (r/w) Set default FLC value for traffic dequeued from this queue.
* Please check description of FD structure for more information.
* Note that FLC values set using DPNI_ADD_FS_ENTRY, if any, take
* precedence over values per queue.
* dest_id: (r/w) The ID of a DPIO or DPCON object, depending on
* DEST_TYPE (in flags) value. This field is ignored for DEST_TYPE
* set to 0 (DPNI_DEST_NONE).
* fqid: (r) Frame queue ID, can be used to enqueue/dequeue or execute
* other commands on the queue through DPIO. Note that Tx queues
* are logical queues and not all management commands are available
* on these queue types.
* qdbin: (r) Queue destination bin. Can be used with the DPIO enqueue
* operation based on QDID, QDBIN and QPRI.
* type: Type of the queue to set configuration to.
* tc: Traffic class. Ignored for QUEUE_TYPE 2 and 3 (Tx confirmation
* and Rx error queues).
* idx: Selects a specific queue out of the set of queues in a TC.
* Accepted values are in range 0 to NUM_QUEUES1. This field is
* ignored for QUEUE_TYPE 3 (Rx error queue). For access to the
* shared Tx confirmation queue (for Tx confirmation mode 1), this
* field must be set to 0xff.
* cgid: (r/w) Congestion group ID.
* chan_id: (w) Channel index to be configured. Used only when QUEUE_TYPE is
* set to DPNI_QUEUE_TX.
* priority: (r/w) Sets the priority in the destination DPCON or DPIO for
* dequeued traffic. Supported values are 0 to # of priorities in
* destination DPCON or DPIO - 1. This field is ignored for
* DEST_TYPE set to 0 (DPNI_DEST_NONE), except if this DPNI is in
* AIOP context. In that case the DPNI_SET_QUEUE can be used to
* override the default assigned priority of the FQ from the TC.
* options: Option bits selecting specific configuration options to apply.
* See DPAA2_NI_QUEUE_OPT_* for details.
* dest_type: Type of destination for dequeued traffic.
* cgid_valid: (r) Congestion group ID is valid.
* stash_control: (r/w) If true, lowest 6 bits of FLC are used for stash control.
* Please check description of FD structure for more information.
* hold_active: (r/w) If true, this flag prevents the queue from being
* rescheduled between DPIOs while it carries traffic and is active
* on one DPIO. Can help reduce reordering if one queue is services
* on multiple CPUs, but the queue is also more likely to be trapped
* in one DPIO, especially when congested.
*/
struct dpaa2_ni_queue_cfg {
uint64_t user_ctx;
uint64_t flow_ctx;
uint32_t dest_id;
uint32_t fqid;
uint16_t qdbin;
enum dpaa2_ni_queue_type type;
uint8_t tc;
uint8_t idx;
uint8_t cgid;
uint8_t chan_id;
uint8_t priority;
uint8_t options;
enum dpaa2_ni_dest_type dest_type;
bool cgid_valid;
bool stash_control;
bool hold_active;
};
/**
* @brief Buffer layout attributes.
*
* pd_size: Size kept for private data (in bytes).
* fd_align: Frame data alignment.
* head_size: Data head room.
* tail_size: Data tail room.
* options: ...
* pass_timestamp: Timestamp is included in the buffer layout.
* pass_parser_result: Parsing results are included in the buffer layout.
* pass_frame_status: Frame status is included in the buffer layout.
* pass_sw_opaque: SW annotation is activated.
* queue_type: Type of a queue this configuration applies to.
*/
struct dpaa2_ni_buf_layout {
uint16_t pd_size;
uint16_t fd_align;
uint16_t head_size;
uint16_t tail_size;
uint16_t options;
bool pass_timestamp;
bool pass_parser_result;
bool pass_frame_status;
bool pass_sw_opaque;
enum dpaa2_ni_queue_type queue_type;
};
/**
* @brief Buffer pools configuration for a network interface.
*/
struct dpaa2_ni_pools_cfg {
uint8_t pools_num;
struct {
uint32_t bp_obj_id;
uint16_t buf_sz;
int backup_flag; /* 0 - regular pool, 1 - backup pool */
} pools[DPAA2_NI_MAX_POOLS];
};
/**
* @brief Errors behavior configuration for a network interface.
*
* err_mask: The errors mask to configure.
* action: Desired action for the errors selected in the mask.
* set_err_fas: Set to true to mark the errors in frame annotation
* status (FAS); relevant for non-discard actions only.
*/
struct dpaa2_ni_err_cfg {
uint32_t err_mask;
enum dpaa2_ni_err_action action;
bool set_err_fas;
};
/**
* @brief Link configuration.
*
* options: Mask of available options.
* adv_speeds: Speeds that are advertised for autoneg.
* rate: Rate in Mbps.
*/
struct dpaa2_ni_link_cfg {
uint64_t options;
uint64_t adv_speeds;
uint32_t rate;
};
/**
* @brief Link state.
*
* options: Mask of available options.
* adv_speeds: Speeds that are advertised for autoneg.
* sup_speeds: Speeds capability of the PHY.
* rate: Rate in Mbps.
* link_up: Link state (true if link is up, false otherwise).
* state_valid: Ignore/Update the state of the link.
*/
struct dpaa2_ni_link_state {
uint64_t options;
uint64_t adv_speeds;
uint64_t sup_speeds;
uint32_t rate;
bool link_up;
bool state_valid;
};
/**
* @brief QoS table configuration.
*
* kcfg_busaddr: Address of the buffer in I/O virtual address space which
* holds the QoS table key configuration.
* default_tc: Default traffic class to use in case of a lookup miss in
* the QoS table.
* discard_on_miss: Set to true to discard frames in case of no match.
* Default traffic class will be used otherwise.
* keep_entries: Set to true to keep existing QoS table entries. This
* option will work properly only for DPNI objects created
* with DPNI_OPT_HAS_KEY_MASKING option.
*/
struct dpaa2_ni_qos_table {
uint64_t kcfg_busaddr;
uint8_t default_tc;
bool discard_on_miss;
bool keep_entries;
};
/**
* @brief Context to add multicast physical addresses to the filter table.
*
* ifp: Network interface associated with the context.
* error: Result of the last MC command.
* nent: Number of entries added.
*/
struct dpaa2_ni_mcaddr_ctx {
struct ifnet *ifp;
int error;
int nent;
};
struct dpaa2_eth_dist_fields {
uint64_t rxnfc_field;
enum net_prot cls_prot;
int cls_field;
int size;
uint64_t id;
};
struct dpni_mask_cfg {
uint8_t mask;
uint8_t offset;
} __packed;
struct dpni_dist_extract {
uint8_t prot;
uint8_t efh_type; /* EFH type is in the 4 LSBs. */
uint8_t size;
uint8_t offset;
uint32_t field;
uint8_t hdr_index;
uint8_t constant;
uint8_t num_of_repeats;
uint8_t num_of_byte_masks;
uint8_t extract_type; /* Extraction type is in the 4 LSBs */
uint8_t _reserved[3];
struct dpni_mask_cfg masks[4];
} __packed;
struct dpni_ext_set_rx_tc_dist {
uint8_t num_extracts;
uint8_t _reserved[7];
struct dpni_dist_extract extracts[DPKG_MAX_NUM_OF_EXTRACTS];
} __packed;
/**
* @brief Software context for the DPAA2 Network Interface driver.
*/
struct dpaa2_ni_softc {
device_t dev;
struct resource *res[DPAA2_NI_MAX_RESOURCES];
uint16_t api_major;
uint16_t api_minor;
uint64_t rx_hash_fields;
uint16_t tx_data_off;
uint16_t tx_qdid;
uint32_t link_options;
int link_state;
uint16_t buf_align;
uint16_t buf_sz;
/* For debug purposes only! */
uint64_t rx_anomaly_frames;
uint64_t rx_single_buf_frames;
uint64_t rx_sg_buf_frames;
uint64_t rx_enq_rej_frames;
uint64_t rx_ieoi_err_frames;
uint64_t tx_single_buf_frames;
uint64_t tx_sg_frames;
/* Attributes of the DPAA2 network interface. */
struct dpaa2_ni_attr attr;
/* Helps to send commands to MC. */
struct dpaa2_cmd *cmd;
uint16_t rc_token;
uint16_t ni_token;
/* For network interface and miibus. */
struct ifnet *ifp;
uint32_t if_flags;
struct mtx lock;
device_t miibus;
struct mii_data *mii;
boolean_t fixed_link;
struct ifmedia fixed_ifmedia;
int media_status;
/* DMA resources */
bus_dma_tag_t bp_dmat; /* for buffer pool */
bus_dma_tag_t tx_dmat; /* for Tx buffers */
bus_dma_tag_t st_dmat; /* for channel storage */
bus_dma_tag_t rxd_dmat; /* for Rx distribution key */
bus_dma_tag_t qos_dmat; /* for QoS table key */
bus_dma_tag_t sgt_dmat; /* for scatter/gather tables */
struct dpaa2_buf qos_kcfg; /* QoS table key config. */
struct dpaa2_buf rxd_kcfg; /* Rx distribution key config. */
/* Channels and RxError frame queue */
uint32_t chan_n;
struct dpaa2_ni_channel *channels[DPAA2_NI_MAX_CHANNELS];
struct dpaa2_ni_fq rxe_queue; /* one per network interface */
/* Rx buffers for buffer pool. */
struct dpaa2_atomic buf_num;
struct dpaa2_atomic buf_free; /* for sysctl(9) only */
struct dpaa2_buf buf[DPAA2_NI_BUFS_MAX];
/* Interrupts */
int irq_rid[DPAA2_NI_MSI_COUNT];
struct resource *irq_res;
void *intr; /* interrupt handle */
/* Tasks */
struct taskqueue *bp_taskq;
struct task bp_task;
/* Callouts */
struct callout mii_callout;
struct {
uint32_t dpmac_id;
uint8_t addr[ETHER_ADDR_LEN];
device_t phy_dev;
int phy_loc;
} mac; /* Info about connected DPMAC (if exists). */
};
extern struct resource_spec dpaa2_ni_spec[];
#endif /* _DPAA2_NI_H */

View File

@ -0,0 +1,536 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause
*
* Copyright © 2013-2015 Freescale Semiconductor, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
*
* Original source file obtained from:
* drivers/net/ethernet/freescale/dpaa2/dpkg.h
*
* Commit: 4c86114194e644b6da9107d75910635c9e87179e
* Repository: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
*/
/*
* 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_NI_DPKG_H
#define _DPAA2_NI_DPKG_H
#define BIT(x) (1ul << (x))
/**
* DPKG_NUM_OF_MASKS - Number of masks per key extraction
*/
#define DPKG_NUM_OF_MASKS 4
/**
* DPKG_MAX_NUM_OF_EXTRACTS - Number of extractions per key profile
*/
#define DPKG_MAX_NUM_OF_EXTRACTS 10
/**
* enum dpkg_extract_from_hdr_type - Selecting extraction by header types
* @DPKG_FROM_HDR: Extract selected bytes from header, by offset
* @DPKG_FROM_FIELD: Extract selected bytes from header, by offset from field
* @DPKG_FULL_FIELD: Extract a full field
*/
enum dpkg_extract_from_hdr_type {
DPKG_FROM_HDR = 0,
DPKG_FROM_FIELD = 1,
DPKG_FULL_FIELD = 2
};
/**
* enum dpkg_extract_type - Enumeration for selecting extraction type
* @DPKG_EXTRACT_FROM_HDR: Extract from the header
* @DPKG_EXTRACT_FROM_DATA: Extract from data not in specific header
* @DPKG_EXTRACT_FROM_PARSE: Extract from parser-result;
* e.g. can be used to extract header existence;
* please refer to 'Parse Result definition' section in the parser BG
*/
enum dpkg_extract_type {
DPKG_EXTRACT_FROM_HDR = 0,
DPKG_EXTRACT_FROM_DATA = 1,
DPKG_EXTRACT_FROM_PARSE = 3
};
/**
* struct dpkg_mask - A structure for defining a single extraction mask
* @mask: Byte mask for the extracted content
* @offset: Offset within the extracted content
*/
struct dpkg_mask {
uint8_t mask;
uint8_t offset;
};
/* Protocol fields */
/* Ethernet fields */
#define NH_FLD_ETH_DA BIT(0)
#define NH_FLD_ETH_SA BIT(1)
#define NH_FLD_ETH_LENGTH BIT(2)
#define NH_FLD_ETH_TYPE BIT(3)
#define NH_FLD_ETH_FINAL_CKSUM BIT(4)
#define NH_FLD_ETH_PADDING BIT(5)
#define NH_FLD_ETH_ALL_FIELDS (BIT(6) - 1)
/* VLAN fields */
#define NH_FLD_VLAN_VPRI BIT(0)
#define NH_FLD_VLAN_CFI BIT(1)
#define NH_FLD_VLAN_VID BIT(2)
#define NH_FLD_VLAN_LENGTH BIT(3)
#define NH_FLD_VLAN_TYPE BIT(4)
#define NH_FLD_VLAN_ALL_FIELDS (BIT(5) - 1)
#define NH_FLD_VLAN_TCI (NH_FLD_VLAN_VPRI | \
NH_FLD_VLAN_CFI | \
NH_FLD_VLAN_VID)
/* IP (generic) fields */
#define NH_FLD_IP_VER BIT(0)
#define NH_FLD_IP_DSCP BIT(2)
#define NH_FLD_IP_ECN BIT(3)
#define NH_FLD_IP_PROTO BIT(4)
#define NH_FLD_IP_SRC BIT(5)
#define NH_FLD_IP_DST BIT(6)
#define NH_FLD_IP_TOS_TC BIT(7)
#define NH_FLD_IP_ID BIT(8)
#define NH_FLD_IP_ALL_FIELDS (BIT(9) - 1)
/* IPV4 fields */
#define NH_FLD_IPV4_VER BIT(0)
#define NH_FLD_IPV4_HDR_LEN BIT(1)
#define NH_FLD_IPV4_TOS BIT(2)
#define NH_FLD_IPV4_TOTAL_LEN BIT(3)
#define NH_FLD_IPV4_ID BIT(4)
#define NH_FLD_IPV4_FLAG_D BIT(5)
#define NH_FLD_IPV4_FLAG_M BIT(6)
#define NH_FLD_IPV4_OFFSET BIT(7)
#define NH_FLD_IPV4_TTL BIT(8)
#define NH_FLD_IPV4_PROTO BIT(9)
#define NH_FLD_IPV4_CKSUM BIT(10)
#define NH_FLD_IPV4_SRC_IP BIT(11)
#define NH_FLD_IPV4_DST_IP BIT(12)
#define NH_FLD_IPV4_OPTS BIT(13)
#define NH_FLD_IPV4_OPTS_COUNT BIT(14)
#define NH_FLD_IPV4_ALL_FIELDS (BIT(15) - 1)
/* IPV6 fields */
#define NH_FLD_IPV6_VER BIT(0)
#define NH_FLD_IPV6_TC BIT(1)
#define NH_FLD_IPV6_SRC_IP BIT(2)
#define NH_FLD_IPV6_DST_IP BIT(3)
#define NH_FLD_IPV6_NEXT_HDR BIT(4)
#define NH_FLD_IPV6_FL BIT(5)
#define NH_FLD_IPV6_HOP_LIMIT BIT(6)
#define NH_FLD_IPV6_ID BIT(7)
#define NH_FLD_IPV6_ALL_FIELDS (BIT(8) - 1)
/* ICMP fields */
#define NH_FLD_ICMP_TYPE BIT(0)
#define NH_FLD_ICMP_CODE BIT(1)
#define NH_FLD_ICMP_CKSUM BIT(2)
#define NH_FLD_ICMP_ID BIT(3)
#define NH_FLD_ICMP_SQ_NUM BIT(4)
#define NH_FLD_ICMP_ALL_FIELDS (BIT(5) - 1)
/* IGMP fields */
#define NH_FLD_IGMP_VERSION BIT(0)
#define NH_FLD_IGMP_TYPE BIT(1)
#define NH_FLD_IGMP_CKSUM BIT(2)
#define NH_FLD_IGMP_DATA BIT(3)
#define NH_FLD_IGMP_ALL_FIELDS (BIT(4) - 1)
/* TCP fields */
#define NH_FLD_TCP_PORT_SRC BIT(0)
#define NH_FLD_TCP_PORT_DST BIT(1)
#define NH_FLD_TCP_SEQ BIT(2)
#define NH_FLD_TCP_ACK BIT(3)
#define NH_FLD_TCP_OFFSET BIT(4)
#define NH_FLD_TCP_FLAGS BIT(5)
#define NH_FLD_TCP_WINDOW BIT(6)
#define NH_FLD_TCP_CKSUM BIT(7)
#define NH_FLD_TCP_URGPTR BIT(8)
#define NH_FLD_TCP_OPTS BIT(9)
#define NH_FLD_TCP_OPTS_COUNT BIT(10)
#define NH_FLD_TCP_ALL_FIELDS (BIT(11) - 1)
/* UDP fields */
#define NH_FLD_UDP_PORT_SRC BIT(0)
#define NH_FLD_UDP_PORT_DST BIT(1)
#define NH_FLD_UDP_LEN BIT(2)
#define NH_FLD_UDP_CKSUM BIT(3)
#define NH_FLD_UDP_ALL_FIELDS (BIT(4) - 1)
/* UDP-lite fields */
#define NH_FLD_UDP_LITE_PORT_SRC BIT(0)
#define NH_FLD_UDP_LITE_PORT_DST BIT(1)
#define NH_FLD_UDP_LITE_ALL_FIELDS (BIT(2) - 1)
/* UDP-encap-ESP fields */
#define NH_FLD_UDP_ENC_ESP_PORT_SRC BIT(0)
#define NH_FLD_UDP_ENC_ESP_PORT_DST BIT(1)
#define NH_FLD_UDP_ENC_ESP_LEN BIT(2)
#define NH_FLD_UDP_ENC_ESP_CKSUM BIT(3)
#define NH_FLD_UDP_ENC_ESP_SPI BIT(4)
#define NH_FLD_UDP_ENC_ESP_SEQUENCE_NUM BIT(5)
#define NH_FLD_UDP_ENC_ESP_ALL_FIELDS (BIT(6) - 1)
/* SCTP fields */
#define NH_FLD_SCTP_PORT_SRC BIT(0)
#define NH_FLD_SCTP_PORT_DST BIT(1)
#define NH_FLD_SCTP_VER_TAG BIT(2)
#define NH_FLD_SCTP_CKSUM BIT(3)
#define NH_FLD_SCTP_ALL_FIELDS (BIT(4) - 1)
/* DCCP fields */
#define NH_FLD_DCCP_PORT_SRC BIT(0)
#define NH_FLD_DCCP_PORT_DST BIT(1)
#define NH_FLD_DCCP_ALL_FIELDS (BIT(2) - 1)
/* IPHC fields */
#define NH_FLD_IPHC_CID BIT(0)
#define NH_FLD_IPHC_CID_TYPE BIT(1)
#define NH_FLD_IPHC_HCINDEX BIT(2)
#define NH_FLD_IPHC_GEN BIT(3)
#define NH_FLD_IPHC_D_BIT BIT(4)
#define NH_FLD_IPHC_ALL_FIELDS (BIT(5) - 1)
/* SCTP fields */
#define NH_FLD_SCTP_CHUNK_DATA_TYPE BIT(0)
#define NH_FLD_SCTP_CHUNK_DATA_FLAGS BIT(1)
#define NH_FLD_SCTP_CHUNK_DATA_LENGTH BIT(2)
#define NH_FLD_SCTP_CHUNK_DATA_TSN BIT(3)
#define NH_FLD_SCTP_CHUNK_DATA_STREAM_ID BIT(4)
#define NH_FLD_SCTP_CHUNK_DATA_STREAM_SQN BIT(5)
#define NH_FLD_SCTP_CHUNK_DATA_PAYLOAD_PID BIT(6)
#define NH_FLD_SCTP_CHUNK_DATA_UNORDERED BIT(7)
#define NH_FLD_SCTP_CHUNK_DATA_BEGGINING BIT(8)
#define NH_FLD_SCTP_CHUNK_DATA_END BIT(9)
#define NH_FLD_SCTP_CHUNK_DATA_ALL_FIELDS (BIT(10) - 1)
/* L2TPV2 fields */
#define NH_FLD_L2TPV2_TYPE_BIT BIT(0)
#define NH_FLD_L2TPV2_LENGTH_BIT BIT(1)
#define NH_FLD_L2TPV2_SEQUENCE_BIT BIT(2)
#define NH_FLD_L2TPV2_OFFSET_BIT BIT(3)
#define NH_FLD_L2TPV2_PRIORITY_BIT BIT(4)
#define NH_FLD_L2TPV2_VERSION BIT(5)
#define NH_FLD_L2TPV2_LEN BIT(6)
#define NH_FLD_L2TPV2_TUNNEL_ID BIT(7)
#define NH_FLD_L2TPV2_SESSION_ID BIT(8)
#define NH_FLD_L2TPV2_NS BIT(9)
#define NH_FLD_L2TPV2_NR BIT(10)
#define NH_FLD_L2TPV2_OFFSET_SIZE BIT(11)
#define NH_FLD_L2TPV2_FIRST_BYTE BIT(12)
#define NH_FLD_L2TPV2_ALL_FIELDS (BIT(13) - 1)
/* L2TPV3 fields */
#define NH_FLD_L2TPV3_CTRL_TYPE_BIT BIT(0)
#define NH_FLD_L2TPV3_CTRL_LENGTH_BIT BIT(1)
#define NH_FLD_L2TPV3_CTRL_SEQUENCE_BIT BIT(2)
#define NH_FLD_L2TPV3_CTRL_VERSION BIT(3)
#define NH_FLD_L2TPV3_CTRL_LENGTH BIT(4)
#define NH_FLD_L2TPV3_CTRL_CONTROL BIT(5)
#define NH_FLD_L2TPV3_CTRL_SENT BIT(6)
#define NH_FLD_L2TPV3_CTRL_RECV BIT(7)
#define NH_FLD_L2TPV3_CTRL_FIRST_BYTE BIT(8)
#define NH_FLD_L2TPV3_CTRL_ALL_FIELDS (BIT(9) - 1)
#define NH_FLD_L2TPV3_SESS_TYPE_BIT BIT(0)
#define NH_FLD_L2TPV3_SESS_VERSION BIT(1)
#define NH_FLD_L2TPV3_SESS_ID BIT(2)
#define NH_FLD_L2TPV3_SESS_COOKIE BIT(3)
#define NH_FLD_L2TPV3_SESS_ALL_FIELDS (BIT(4) - 1)
/* PPP fields */
#define NH_FLD_PPP_PID BIT(0)
#define NH_FLD_PPP_COMPRESSED BIT(1)
#define NH_FLD_PPP_ALL_FIELDS (BIT(2) - 1)
/* PPPoE fields */
#define NH_FLD_PPPOE_VER BIT(0)
#define NH_FLD_PPPOE_TYPE BIT(1)
#define NH_FLD_PPPOE_CODE BIT(2)
#define NH_FLD_PPPOE_SID BIT(3)
#define NH_FLD_PPPOE_LEN BIT(4)
#define NH_FLD_PPPOE_SESSION BIT(5)
#define NH_FLD_PPPOE_PID BIT(6)
#define NH_FLD_PPPOE_ALL_FIELDS (BIT(7) - 1)
/* PPP-Mux fields */
#define NH_FLD_PPPMUX_PID BIT(0)
#define NH_FLD_PPPMUX_CKSUM BIT(1)
#define NH_FLD_PPPMUX_COMPRESSED BIT(2)
#define NH_FLD_PPPMUX_ALL_FIELDS (BIT(3) - 1)
/* PPP-Mux sub-frame fields */
#define NH_FLD_PPPMUX_SUBFRM_PFF BIT(0)
#define NH_FLD_PPPMUX_SUBFRM_LXT BIT(1)
#define NH_FLD_PPPMUX_SUBFRM_LEN BIT(2)
#define NH_FLD_PPPMUX_SUBFRM_PID BIT(3)
#define NH_FLD_PPPMUX_SUBFRM_USE_PID BIT(4)
#define NH_FLD_PPPMUX_SUBFRM_ALL_FIELDS (BIT(5) - 1)
/* LLC fields */
#define NH_FLD_LLC_DSAP BIT(0)
#define NH_FLD_LLC_SSAP BIT(1)
#define NH_FLD_LLC_CTRL BIT(2)
#define NH_FLD_LLC_ALL_FIELDS (BIT(3) - 1)
/* NLPID fields */
#define NH_FLD_NLPID_NLPID BIT(0)
#define NH_FLD_NLPID_ALL_FIELDS (BIT(1) - 1)
/* SNAP fields */
#define NH_FLD_SNAP_OUI BIT(0)
#define NH_FLD_SNAP_PID BIT(1)
#define NH_FLD_SNAP_ALL_FIELDS (BIT(2) - 1)
/* LLC SNAP fields */
#define NH_FLD_LLC_SNAP_TYPE BIT(0)
#define NH_FLD_LLC_SNAP_ALL_FIELDS (BIT(1) - 1)
/* ARP fields */
#define NH_FLD_ARP_HTYPE BIT(0)
#define NH_FLD_ARP_PTYPE BIT(1)
#define NH_FLD_ARP_HLEN BIT(2)
#define NH_FLD_ARP_PLEN BIT(3)
#define NH_FLD_ARP_OPER BIT(4)
#define NH_FLD_ARP_SHA BIT(5)
#define NH_FLD_ARP_SPA BIT(6)
#define NH_FLD_ARP_THA BIT(7)
#define NH_FLD_ARP_TPA BIT(8)
#define NH_FLD_ARP_ALL_FIELDS (BIT(9) - 1)
/* RFC2684 fields */
#define NH_FLD_RFC2684_LLC BIT(0)
#define NH_FLD_RFC2684_NLPID BIT(1)
#define NH_FLD_RFC2684_OUI BIT(2)
#define NH_FLD_RFC2684_PID BIT(3)
#define NH_FLD_RFC2684_VPN_OUI BIT(4)
#define NH_FLD_RFC2684_VPN_IDX BIT(5)
#define NH_FLD_RFC2684_ALL_FIELDS (BIT(6) - 1)
/* User defined fields */
#define NH_FLD_USER_DEFINED_SRCPORT BIT(0)
#define NH_FLD_USER_DEFINED_PCDID BIT(1)
#define NH_FLD_USER_DEFINED_ALL_FIELDS (BIT(2) - 1)
/* Payload fields */
#define NH_FLD_PAYLOAD_BUFFER BIT(0)
#define NH_FLD_PAYLOAD_SIZE BIT(1)
#define NH_FLD_MAX_FRM_SIZE BIT(2)
#define NH_FLD_MIN_FRM_SIZE BIT(3)
#define NH_FLD_PAYLOAD_TYPE BIT(4)
#define NH_FLD_FRAME_SIZE BIT(5)
#define NH_FLD_PAYLOAD_ALL_FIELDS (BIT(6) - 1)
/* GRE fields */
#define NH_FLD_GRE_TYPE BIT(0)
#define NH_FLD_GRE_ALL_FIELDS (BIT(1) - 1)
/* MINENCAP fields */
#define NH_FLD_MINENCAP_SRC_IP BIT(0)
#define NH_FLD_MINENCAP_DST_IP BIT(1)
#define NH_FLD_MINENCAP_TYPE BIT(2)
#define NH_FLD_MINENCAP_ALL_FIELDS (BIT(3) - 1)
/* IPSEC AH fields */
#define NH_FLD_IPSEC_AH_SPI BIT(0)
#define NH_FLD_IPSEC_AH_NH BIT(1)
#define NH_FLD_IPSEC_AH_ALL_FIELDS (BIT(2) - 1)
/* IPSEC ESP fields */
#define NH_FLD_IPSEC_ESP_SPI BIT(0)
#define NH_FLD_IPSEC_ESP_SEQUENCE_NUM BIT(1)
#define NH_FLD_IPSEC_ESP_ALL_FIELDS (BIT(2) - 1)
/* MPLS fields */
#define NH_FLD_MPLS_LABEL_STACK BIT(0)
#define NH_FLD_MPLS_LABEL_STACK_ALL_FIELDS (BIT(1) - 1)
/* MACSEC fields */
#define NH_FLD_MACSEC_SECTAG BIT(0)
#define NH_FLD_MACSEC_ALL_FIELDS (BIT(1) - 1)
/* GTP fields */
#define NH_FLD_GTP_TEID BIT(0)
/* Supported protocols */
enum net_prot {
NET_PROT_NONE = 0,
NET_PROT_PAYLOAD,
NET_PROT_ETH,
NET_PROT_VLAN,
NET_PROT_IPV4,
NET_PROT_IPV6,
NET_PROT_IP,
NET_PROT_TCP,
NET_PROT_UDP,
NET_PROT_UDP_LITE,
NET_PROT_IPHC,
NET_PROT_SCTP,
NET_PROT_SCTP_CHUNK_DATA,
NET_PROT_PPPOE,
NET_PROT_PPP,
NET_PROT_PPPMUX,
NET_PROT_PPPMUX_SUBFRM,
NET_PROT_L2TPV2,
NET_PROT_L2TPV3_CTRL,
NET_PROT_L2TPV3_SESS,
NET_PROT_LLC,
NET_PROT_LLC_SNAP,
NET_PROT_NLPID,
NET_PROT_SNAP,
NET_PROT_MPLS,
NET_PROT_IPSEC_AH,
NET_PROT_IPSEC_ESP,
NET_PROT_UDP_ENC_ESP, /* RFC 3948 */
NET_PROT_MACSEC,
NET_PROT_GRE,
NET_PROT_MINENCAP,
NET_PROT_DCCP,
NET_PROT_ICMP,
NET_PROT_IGMP,
NET_PROT_ARP,
NET_PROT_CAPWAP_DATA,
NET_PROT_CAPWAP_CTRL,
NET_PROT_RFC2684,
NET_PROT_ICMPV6,
NET_PROT_FCOE,
NET_PROT_FIP,
NET_PROT_ISCSI,
NET_PROT_GTP,
NET_PROT_USER_DEFINED_L2,
NET_PROT_USER_DEFINED_L3,
NET_PROT_USER_DEFINED_L4,
NET_PROT_USER_DEFINED_L5,
NET_PROT_USER_DEFINED_SHIM1,
NET_PROT_USER_DEFINED_SHIM2,
NET_PROT_DUMMY_LAST
};
/**
* struct dpkg_extract - A structure for defining a single extraction
* @type: Determines how the union below is interpreted:
* DPKG_EXTRACT_FROM_HDR: selects 'from_hdr';
* DPKG_EXTRACT_FROM_DATA: selects 'from_data';
* DPKG_EXTRACT_FROM_PARSE: selects 'from_parse'
* @extract: Selects extraction method
* @extract.from_hdr: Used when 'type = DPKG_EXTRACT_FROM_HDR'
* @extract.from_data: Used when 'type = DPKG_EXTRACT_FROM_DATA'
* @extract.from_parse: Used when 'type = DPKG_EXTRACT_FROM_PARSE'
* @extract.from_hdr.prot: Any of the supported headers
* @extract.from_hdr.type: Defines the type of header extraction:
* DPKG_FROM_HDR: use size & offset below;
* DPKG_FROM_FIELD: use field, size and offset below;
* DPKG_FULL_FIELD: use field below
* @extract.from_hdr.field: One of the supported fields (NH_FLD_)
* @extract.from_hdr.size: Size in bytes
* @extract.from_hdr.offset: Byte offset
* @extract.from_hdr.hdr_index: Clear for cases not listed below;
* Used for protocols that may have more than a single
* header, 0 indicates an outer header;
* Supported protocols (possible values):
* NET_PROT_VLAN (0, HDR_INDEX_LAST);
* NET_PROT_MPLS (0, 1, HDR_INDEX_LAST);
* NET_PROT_IP(0, HDR_INDEX_LAST);
* NET_PROT_IPv4(0, HDR_INDEX_LAST);
* NET_PROT_IPv6(0, HDR_INDEX_LAST);
* @extract.from_data.size: Size in bytes
* @extract.from_data.offset: Byte offset
* @extract.from_parse.size: Size in bytes
* @extract.from_parse.offset: Byte offset
* @num_of_byte_masks: Defines the number of valid entries in the array below;
* This is also the number of bytes to be used as masks
* @masks: Masks parameters
*/
struct dpkg_extract {
enum dpkg_extract_type type;
union {
struct {
enum net_prot prot;
enum dpkg_extract_from_hdr_type type;
uint32_t field;
uint8_t size;
uint8_t offset;
uint8_t hdr_index;
} from_hdr;
struct {
uint8_t size;
uint8_t offset;
} from_data;
struct {
uint8_t size;
uint8_t offset;
} from_parse;
} extract;
uint8_t num_of_byte_masks;
struct dpkg_mask masks[DPKG_NUM_OF_MASKS];
};
/**
* struct dpkg_profile_cfg - A structure for defining a full Key Generation
* profile (rule)
* @num_extracts: Defines the number of valid entries in the array below
* @extracts: Array of required extractions
*/
struct dpkg_profile_cfg {
uint8_t num_extracts;
struct dpkg_extract extracts[DPKG_MAX_NUM_OF_EXTRACTS];
};
#endif /* _DPAA2_NI_DPKG_H */

3585
sys/dev/dpaa2/dpaa2_rc.c Normal file

File diff suppressed because it is too large Load Diff

1169
sys/dev/dpaa2/dpaa2_swp.c Normal file

File diff suppressed because it is too large Load Diff

504
sys/dev/dpaa2/dpaa2_swp.h Normal file
View File

@ -0,0 +1,504 @@
/*-
* 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_SWP_H
#define _DPAA2_SWP_H
#include <sys/bus.h>
#include "dpaa2_types.h"
#include "dpaa2_bp.h"
/*
* DPAA2 QBMan software portal.
*/
/* All QBMan commands and result structures use this "valid bit" encoding. */
#define DPAA2_SWP_VALID_BIT ((uint32_t) 0x80)
#define DPAA2_SWP_TIMEOUT 100000 /* in us */
#define DPAA2_SWP_CMD_PARAMS_N 8u
#define DPAA2_SWP_RSP_PARAMS_N 8u
/*
* Maximum number of buffers that can be acquired/released through a single
* QBMan command.
*/
#define DPAA2_SWP_BUFS_PER_CMD 7u
/*
* Number of times to retry DPIO portal operations while waiting for portal to
* finish executing current command and become available.
*
* We want to avoid being stuck in a while loop in case hardware becomes
* unresponsive, but not give up too easily if the portal really is busy for
* valid reasons.
*/
#define DPAA2_SWP_BUSY_RETRIES 1000
/* Versions of the QBMan software portals. */
#define DPAA2_SWP_REV_4000 0x04000000
#define DPAA2_SWP_REV_4100 0x04010000
#define DPAA2_SWP_REV_4101 0x04010001
#define DPAA2_SWP_REV_5000 0x05000000
#define DPAA2_SWP_REV_MASK 0xFFFF0000
/* Registers in the cache-inhibited area of the software portal. */
#define DPAA2_SWP_CINH_CR 0x600 /* Management Command reg.*/
#define DPAA2_SWP_CINH_EQCR_PI 0x800 /* Enqueue Ring, Producer Index */
#define DPAA2_SWP_CINH_EQCR_CI 0x840 /* Enqueue Ring, Consumer Index */
#define DPAA2_SWP_CINH_CR_RT 0x900 /* CR Read Trigger */
#define DPAA2_SWP_CINH_VDQCR_RT 0x940 /* VDQCR Read Trigger */
#define DPAA2_SWP_CINH_EQCR_AM_RT 0x980
#define DPAA2_SWP_CINH_RCR_AM_RT 0x9C0
#define DPAA2_SWP_CINH_DQPI 0xA00 /* DQRR Producer Index reg. */
#define DPAA2_SWP_CINH_DQRR_ITR 0xA80 /* DQRR interrupt timeout reg. */
#define DPAA2_SWP_CINH_DCAP 0xAC0 /* DQRR Consumption Ack. reg. */
#define DPAA2_SWP_CINH_SDQCR 0xB00 /* Static Dequeue Command reg. */
#define DPAA2_SWP_CINH_EQCR_AM_RT2 0xB40
#define DPAA2_SWP_CINH_RCR_PI 0xC00 /* Release Ring, Producer Index */
#define DPAA2_SWP_CINH_RAR 0xCC0 /* Release Array Allocation reg. */
#define DPAA2_SWP_CINH_CFG 0xD00
#define DPAA2_SWP_CINH_ISR 0xE00
#define DPAA2_SWP_CINH_IER 0xE40
#define DPAA2_SWP_CINH_ISDR 0xE80
#define DPAA2_SWP_CINH_IIR 0xEC0
#define DPAA2_SWP_CINH_ITPR 0xF40
/* Registers in the cache-enabled area of the software portal. */
#define DPAA2_SWP_CENA_EQCR(n) (0x000 + ((uint32_t)(n) << 6))
#define DPAA2_SWP_CENA_DQRR(n) (0x200 + ((uint32_t)(n) << 6))
#define DPAA2_SWP_CENA_RCR(n) (0x400 + ((uint32_t)(n) << 6))
#define DPAA2_SWP_CENA_CR (0x600) /* Management Command reg. */
#define DPAA2_SWP_CENA_RR(vb) (0x700 + ((uint32_t)(vb) >> 1))
#define DPAA2_SWP_CENA_VDQCR (0x780)
#define DPAA2_SWP_CENA_EQCR_CI (0x840)
/* Registers in the cache-enabled area of the software portal (memory-backed). */
#define DPAA2_SWP_CENA_DQRR_MEM(n) (0x0800 + ((uint32_t)(n) << 6))
#define DPAA2_SWP_CENA_RCR_MEM(n) (0x1400 + ((uint32_t)(n) << 6))
#define DPAA2_SWP_CENA_CR_MEM (0x1600) /* Management Command reg. */
#define DPAA2_SWP_CENA_RR_MEM (0x1680) /* Management Response reg. */
#define DPAA2_SWP_CENA_VDQCR_MEM (0x1780)
#define DPAA2_SWP_CENA_EQCR_CI_MEMBACK (0x1840)
/* Shifts in the portal's configuration register. */
#define DPAA2_SWP_CFG_DQRR_MF_SHIFT 20
#define DPAA2_SWP_CFG_EST_SHIFT 16
#define DPAA2_SWP_CFG_CPBS_SHIFT 15
#define DPAA2_SWP_CFG_WN_SHIFT 14
#define DPAA2_SWP_CFG_RPM_SHIFT 12
#define DPAA2_SWP_CFG_DCM_SHIFT 10
#define DPAA2_SWP_CFG_EPM_SHIFT 8
#define DPAA2_SWP_CFG_VPM_SHIFT 7
#define DPAA2_SWP_CFG_CPM_SHIFT 6
#define DPAA2_SWP_CFG_SD_SHIFT 5
#define DPAA2_SWP_CFG_SP_SHIFT 4
#define DPAA2_SWP_CFG_SE_SHIFT 3
#define DPAA2_SWP_CFG_DP_SHIFT 2
#define DPAA2_SWP_CFG_DE_SHIFT 1
#define DPAA2_SWP_CFG_EP_SHIFT 0
/* Static Dequeue Command Register attribute codes */
#define DPAA2_SDQCR_FC_SHIFT 29 /* Dequeue Command Frame Count */
#define DPAA2_SDQCR_FC_MASK 0x1
#define DPAA2_SDQCR_DCT_SHIFT 24 /* Dequeue Command Type */
#define DPAA2_SDQCR_DCT_MASK 0x3
#define DPAA2_SDQCR_TOK_SHIFT 16 /* Dequeue Command Token */
#define DPAA2_SDQCR_TOK_MASK 0xff
#define DPAA2_SDQCR_SRC_SHIFT 0 /* Dequeue Source */
#define DPAA2_SDQCR_SRC_MASK 0xffff
/*
* Read trigger bit is used to trigger QMan to read a command from memory,
* without having software perform a cache flush to force a write of the command
* to QMan.
*
* NOTE: Implemented in QBMan 5.0 or above.
*/
#define DPAA2_SWP_RT_MODE ((uint32_t)0x100)
/* Interrupt Enable Register bits. */
#define DPAA2_SWP_INTR_EQRI 0x01
#define DPAA2_SWP_INTR_EQDI 0x02
#define DPAA2_SWP_INTR_DQRI 0x04
#define DPAA2_SWP_INTR_RCRI 0x08
#define DPAA2_SWP_INTR_RCDI 0x10
#define DPAA2_SWP_INTR_VDCI 0x20
/* "Write Enable" bitmask for a command to configure SWP WQ Channel.*/
#define DPAA2_WQCHAN_WE_EN (0x1u) /* Enable CDAN generation */
#define DPAA2_WQCHAN_WE_ICD (0x2u) /* Interrupt Coalescing Disable */
#define DPAA2_WQCHAN_WE_CTX (0x4u)
/* Definitions for parsing DQRR entries. */
#define DPAA2_DQRR_RESULT_MASK (0x7Fu)
#define DPAA2_DQRR_RESULT_DQ (0x60u)
#define DPAA2_DQRR_RESULT_FQRN (0x21u)
#define DPAA2_DQRR_RESULT_FQRNI (0x22u)
#define DPAA2_DQRR_RESULT_FQPN (0x24u)
#define DPAA2_DQRR_RESULT_FQDAN (0x25u)
#define DPAA2_DQRR_RESULT_CDAN (0x26u)
#define DPAA2_DQRR_RESULT_CSCN_MEM (0x27u)
#define DPAA2_DQRR_RESULT_CGCU (0x28u)
#define DPAA2_DQRR_RESULT_BPSCN (0x29u)
#define DPAA2_DQRR_RESULT_CSCN_WQ (0x2au)
/* Frame dequeue statuses */
#define DPAA2_DQ_STAT_FQEMPTY (0x80u) /* FQ is empty */
#define DPAA2_DQ_STAT_HELDACTIVE (0x40u) /* FQ is held active */
#define DPAA2_DQ_STAT_FORCEELIGIBLE (0x20u) /* FQ force eligible */
#define DPAA2_DQ_STAT_VALIDFRAME (0x10u) /* valid frame */
#define DPAA2_DQ_STAT_ODPVALID (0x04u) /* FQ ODP enable */
#define DPAA2_DQ_STAT_VOLATILE (0x02u) /* volatile dequeue (VDC) */
#define DPAA2_DQ_STAT_EXPIRED (0x01u) /* VDC is expired */
/*
* Portal flags.
*
* TODO: Use the same flags for both MC and software portals.
*/
#define DPAA2_SWP_DEF 0x0u
#define DPAA2_SWP_NOWAIT_ALLOC 0x2u /* Do not sleep during init */
#define DPAA2_SWP_LOCKED 0x4000u /* Wait till portal's unlocked */
#define DPAA2_SWP_DESTROYED 0x8000u /* Terminate any operations */
/* Command return codes. */
#define DPAA2_SWP_STAT_OK 0x0
#define DPAA2_SWP_STAT_NO_MEMORY 0x9 /* No memory available */
#define DPAA2_SWP_STAT_PORTAL_DISABLED 0xFD /* QBMan portal disabled */
#define DPAA2_SWP_STAT_EINVAL 0xFE /* Invalid argument */
#define DPAA2_SWP_STAT_ERR 0xFF /* General error */
/* Opaque token for static dequeues. */
#define DPAA2_SWP_SDQCR_TOKEN 0xBBu
/* Opaque token for static dequeues. */
#define DPAA2_SWP_VDQCR_TOKEN 0xCCu
#define DPAA2_SWP_LOCK(__swp, __flags) do { \
mtx_assert(&(__swp)->lock, MA_NOTOWNED); \
mtx_lock(&(__swp)->lock); \
*(__flags) = (__swp)->flags; \
(__swp)->flags |= DPAA2_SWP_LOCKED; \
} while (0)
#define DPAA2_SWP_UNLOCK(__swp) do { \
mtx_assert(&(__swp)->lock, MA_OWNED); \
(__swp)->flags &= ~DPAA2_SWP_LOCKED; \
mtx_unlock(&(__swp)->lock); \
} while (0)
enum dpaa2_fd_format {
DPAA2_FD_SINGLE = 0,
DPAA2_FD_LIST,
DPAA2_FD_SG
};
/**
* @brief Enqueue command descriptor.
*
* NOTE: 32 bytes.
*/
struct dpaa2_eq_desc {
uint8_t verb;
uint8_t dca;
uint16_t seqnum;
uint16_t orpid;
uint16_t _reserved;
uint32_t tgtid;
uint32_t tag;
uint16_t qdbin;
uint8_t qpri;
uint8_t _reserved1[3];
uint8_t wae;
uint8_t rspid;
uint64_t rsp_addr;
} __packed;
/**
* @brief Frame Dequeue Response (FDR) descriptor.
*
* NOTE: 32 bytes.
*/
struct dpaa2_fdr_desc {
uint8_t verb;
uint8_t stat;
uint16_t seqnum;
uint16_t oprid;
uint8_t _reserved;
uint8_t tok;
uint32_t fqid;
uint32_t _reserved1;
uint32_t fq_byte_cnt;
uint32_t fq_frm_cnt;
uint64_t fqd_ctx;
} __packed;
/**
* @brief State Change Notification Message (SCNM).
*
* NOTE: 16 bytes.
*/
struct dpaa2_scn {
uint8_t verb;
uint8_t stat;
uint8_t state;
uint8_t _reserved;
uint32_t rid_tok;
uint64_t ctx;
} __packed;
/**
* @brief DPAA2 frame descriptor.
*
* addr: Memory address of the start of the buffer holding the
* frame data or the buffer containing the scatter/gather
* list.
* data_length: Length of the frame data (in bytes).
* bpid_ivp_bmt: Buffer pool ID (14 bit + BMT bit + IVP bit)
* offset_fmt_sl: Frame data offset, frame format and short-length fields.
* frame_ctx: Frame context. This field allows the sender of a frame
* to communicate some out-of-band information to the
* receiver of the frame.
* ctrl: Control bits (ERR, CBMT, ASAL, PTAC, DROPP, SC, DD).
* flow_ctx: Frame flow context. Associates the frame with a flow
* structure. QMan may use the FLC field for 3 purposes:
* stashing control, order definition point identification,
* and enqueue replication control.
*
* NOTE: 32 bytes.
*/
struct dpaa2_fd {
uint64_t addr;
uint32_t data_length;
uint16_t bpid_ivp_bmt;
uint16_t offset_fmt_sl;
uint32_t frame_ctx;
uint32_t ctrl;
uint64_t flow_ctx;
} __packed;
/**
* @brief DPAA2 scatter/gather entry.
*
* NOTE: 16 bytes.
*/
struct dpaa2_sg_entry {
uint64_t addr;
uint32_t len;
uint16_t bpid;
uint16_t offset_fmt;
} __packed;
/**
* @brief Frame Dequeue Response (FDR).
*
* NOTE: 64 bytes.
*/
struct dpaa2_fdr {
struct dpaa2_fdr_desc desc;
struct dpaa2_fd fd;
} __packed;
/**
* @brief Dequeue Response Message.
*
* NOTE: 64 bytes.
*/
struct dpaa2_dq {
union {
struct {
uint8_t verb;
uint8_t _reserved[63];
} common;
struct dpaa2_fdr fdr; /* Frame Dequeue Response */
struct dpaa2_scn scn; /* State Change Notification */
};
} __packed;
/**
* @brief Descriptor of the QBMan software portal.
*
* cena_res: Unmapped cache-enabled part of the portal's I/O memory.
* cena_map: Mapped cache-enabled part of the portal's I/O memory.
* cinh_res: Unmapped cache-inhibited part of the portal's I/O memory.
* cinh_map: Mapped cache-inhibited part of the portal's I/O memory.
*
* dpio_dev: Device associated with the DPIO object to manage this
* portal.
* swp_version: Hardware IP version of the software portal.
* swp_clk: QBMAN clock frequency value in Hz.
* swp_cycles_ratio: How many 256 QBMAN cycles fit into one ns.
* swp_id: Software portal ID.
*
* has_notif: True if the notification mode is used.
* has_8prio: True for a channel with 8 priority WQs. Ignored unless
* "has_notif" is true.
*/
struct dpaa2_swp_desc {
struct resource *cena_res;
struct resource_map *cena_map;
struct resource *cinh_res;
struct resource_map *cinh_map;
device_t dpio_dev;
uint32_t swp_version;
uint32_t swp_clk;
uint32_t swp_cycles_ratio;
uint16_t swp_id;
bool has_notif;
bool has_8prio;
};
/**
* @brief Command holds data to be written to the software portal.
*/
struct dpaa2_swp_cmd {
uint64_t params[DPAA2_SWP_CMD_PARAMS_N];
};
/**
* @brief Command response holds data received from the software portal.
*/
struct dpaa2_swp_rsp {
uint64_t params[DPAA2_SWP_RSP_PARAMS_N];
};
/**
* @brief QBMan software portal.
*
* res: Unmapped cache-enabled and cache-inhibited parts of the portal.
* map: Mapped cache-enabled and cache-inhibited parts of the portal.
* desc: Descriptor of the QBMan software portal.
* lock: Lock to guard an access to the portal.
* cv: Conditional variable helps to wait for the helper object's state
* change.
* flags: Current state of the object.
* sdq: Push dequeues status.
* mc: Management commands data.
* mr: Management response data.
* dqrr: Dequeue Response Ring is used to issue frame dequeue responses
* from the QBMan to the driver.
* eqcr: Enqueue Command Ring is used to issue frame enqueue commands
* from the driver to the QBMan.
*/
struct dpaa2_swp {
struct resource *cena_res;
struct resource_map *cena_map;
struct resource *cinh_res;
struct resource_map *cinh_map;
struct mtx lock;
struct dpaa2_swp_desc *desc;
uint16_t flags;
/* Static Dequeue Command Register value (to obtain CDANs). */
uint32_t sdq;
/* Volatile Dequeue Command (to obtain frames). */
struct {
uint32_t valid_bit; /* 0x00 or 0x80 */
} vdq;
struct {
bool atomic;
bool writes_cinh;
bool mem_backed;
} cfg; /* Software portal configuration. */
struct {
uint32_t valid_bit; /* 0x00 or 0x80 */
} mc;
struct {
uint32_t valid_bit; /* 0x00 or 0x80 */
} mr;
struct {
uint32_t next_idx;
uint32_t valid_bit;
uint8_t ring_size;
bool reset_bug; /* dqrr reset workaround */
uint32_t irq_threshold;
uint32_t irq_itp;
} dqrr;
struct {
uint32_t pi; /* producer index */
uint32_t pi_vb; /* PI valid bits */
uint32_t pi_ring_size;
uint32_t pi_ci_mask;
uint32_t ci;
int available;
uint32_t pend;
uint32_t no_pfdr;
} eqcr;
};
/* Management routines. */
int dpaa2_swp_init_portal(struct dpaa2_swp **swp, struct dpaa2_swp_desc *desc,
uint16_t flags);
void dpaa2_swp_free_portal(struct dpaa2_swp *swp);
uint32_t dpaa2_swp_set_cfg(uint8_t max_fill, uint8_t wn, uint8_t est,
uint8_t rpm, uint8_t dcm, uint8_t epm, int sd, int sp, int se, int dp,
int de, int ep);
/* Read/write registers of a software portal. */
void dpaa2_swp_write_reg(struct dpaa2_swp *swp, uint32_t o, uint32_t v);
uint32_t dpaa2_swp_read_reg(struct dpaa2_swp *swp, uint32_t o);
/* Helper routines. */
void dpaa2_swp_set_ed_norp(struct dpaa2_eq_desc *ed, bool resp_always);
void dpaa2_swp_set_ed_fq(struct dpaa2_eq_desc *ed, uint32_t fqid);
void dpaa2_swp_set_intr_trigger(struct dpaa2_swp *swp, uint32_t mask);
uint32_t dpaa2_swp_get_intr_trigger(struct dpaa2_swp *swp);
uint32_t dpaa2_swp_read_intr_status(struct dpaa2_swp *swp);
void dpaa2_swp_clear_intr_status(struct dpaa2_swp *swp, uint32_t mask);
void dpaa2_swp_set_push_dequeue(struct dpaa2_swp *swp, uint8_t chan_idx,
bool en);
int dpaa2_swp_set_irq_coalescing(struct dpaa2_swp *swp, uint32_t threshold,
uint32_t holdoff);
/* Software portal commands. */
int dpaa2_swp_conf_wq_channel(struct dpaa2_swp *swp, uint16_t chan_id,
uint8_t we_mask, bool cdan_en, uint64_t ctx);
int dpaa2_swp_query_bp(struct dpaa2_swp *swp, uint16_t bpid,
struct dpaa2_bp_conf *conf);
int dpaa2_swp_release_bufs(struct dpaa2_swp *swp, uint16_t bpid, bus_addr_t *buf,
uint32_t buf_num);
int dpaa2_swp_dqrr_next_locked(struct dpaa2_swp *swp, struct dpaa2_dq *dq,
uint32_t *idx);
int dpaa2_swp_pull(struct dpaa2_swp *swp, uint16_t chan_id,
struct dpaa2_buf *buf, uint32_t frames_n);
int dpaa2_swp_enq(struct dpaa2_swp *swp, struct dpaa2_eq_desc *ed,
struct dpaa2_fd *fd);
int dpaa2_swp_enq_mult(struct dpaa2_swp *swp, struct dpaa2_eq_desc *ed,
struct dpaa2_fd *fd, uint32_t *flags, int frames_n);
#endif /* _DPAA2_SWP_H */

View File

@ -0,0 +1,96 @@
#-
# 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 <machine/bus.h>
#include <dev/dpaa2/dpaa2_mc.h>
#include <dev/dpaa2/dpaa2_swp.h>
#include <dev/dpaa2/dpaa2_bp.h>
/**
* @brief QBMan software portal interface.
*
* Software portals are used by data path software executing on a processor core
* to communicate with the Queue Manager (QMan) which acts as a central resource
* in DPAA2, managing the queueing of data between multiple processor cores,
* network interfaces, and hardware accelerators in a multicore SoC.
*/
INTERFACE dpaa2_swp;
/**
* @brief Enqueue multiple frames to a frame queue using one Frame Queue ID.
*
* dev: DPIO device.
* fqid: Frame Queue ID.
* fd: Frame descriptor to enqueue.
* frames_n: Number of frames to enqueue.
*/
METHOD int enq_multiple_fq {
device_t dev;
uint32_t fqid;
struct dpaa2_fd *fd;
int frames_n;
}
/**
* @brief Configure the channel data availability notification (CDAN)
* in a particular WQ channel paired with DPIO.
*
* dev: DPIO device.
* ctx: Context to configure data availability notifications (CDAN).
*/
METHOD int conf_wq_channel {
device_t dev;
struct dpaa2_io_notif_ctx *ctx;
};
/**
* @brief Release one or more buffer pointers to a QBMan buffer pool.
*
* dev: DPIO device.
* bpid: Buffer pool ID.
* buf: Array of buffers physical addresses.
* buf_num: Number of the buffers in the array.
*/
METHOD int release_bufs {
device_t dev;
uint16_t bpid;
bus_addr_t *buf;
uint32_t buf_num;
};
/**
* @brief Query current configuration/state of the buffer pool.
*
* dev: DPIO device.
* bpid: Buffer pool ID.
* conf: Configuration/state of the buffer pool.
*/
METHOD int query_bp {
device_t dev;
uint16_t bpid;
struct dpaa2_bp_conf *conf;
}

114
sys/dev/dpaa2/dpaa2_types.h Normal file
View File

@ -0,0 +1,114 @@
/*-
* 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_TYPES_H
#define _DPAA2_TYPES_H
#include <machine/atomic.h>
/**
* @brief Types of the DPAA2 devices.
*/
enum dpaa2_dev_type {
DPAA2_DEV_MC = 7500, /* Management Complex (firmware bus) */
DPAA2_DEV_RC, /* Resource Container (firmware bus) */
DPAA2_DEV_IO, /* I/O object (to work with QBMan portal) */
DPAA2_DEV_NI, /* Network Interface */
DPAA2_DEV_MCP, /* MC portal */
DPAA2_DEV_BP, /* Buffer Pool */
DPAA2_DEV_CON, /* Concentrator */
DPAA2_DEV_MAC, /* MAC object */
DPAA2_DEV_MUX, /* MUX (Datacenter bridge) object */
DPAA2_DEV_SW, /* Ethernet Switch */
DPAA2_DEV_NOTYPE /* Shouldn't be assigned to any DPAA2 device. */
};
/**
* @brief Types of the DPAA2 buffers.
*/
enum dpaa2_buf_type {
DPAA2_BUF_RX = 75, /* Rx buffer */
DPAA2_BUF_TX, /* Tx buffer */
DPAA2_BUF_STORE /* Channel storage, key configuration */
};
/**
* @brief DMA-mapped buffer (for Rx/Tx buffers, channel storage, etc.).
*/
struct dpaa2_buf {
enum dpaa2_buf_type type;
union {
struct {
bus_dma_tag_t dmat; /* DMA tag for this buffer */
bus_dmamap_t dmap;
bus_addr_t paddr;
void *vaddr;
struct mbuf *m; /* associated mbuf */
} rx;
struct {
bus_dma_tag_t dmat; /* DMA tag for this buffer */
bus_dmamap_t dmap;
bus_addr_t paddr;
void *vaddr;
struct mbuf *m; /* associated mbuf */
uint64_t idx;
/* for scatter/gather table */
bus_dma_tag_t sgt_dmat;
bus_dmamap_t sgt_dmap;
bus_addr_t sgt_paddr;
void *sgt_vaddr;
} tx;
struct {
bus_dma_tag_t dmat; /* DMA tag for this buffer */
bus_dmamap_t dmap;
bus_addr_t paddr;
void *vaddr;
} store;
};
};
struct dpaa2_atomic {
volatile int counter;
};
/* Handy wrappers over atomic operations. */
#define DPAA2_ATOMIC_XCHG(a, val) \
(atomic_swap_int(&(a)->counter, (val)))
#define DPAA2_ATOMIC_READ(a) \
(atomic_load_acq_int(&(a)->counter))
#define DPAA2_ATOMIC_ADD(a, val) \
(atomic_add_acq_int(&(a)->counter, (val)))
/* Convert DPAA2 type to/from string. */
const char *dpaa2_ttos(enum dpaa2_dev_type type);
enum dpaa2_dev_type dpaa2_stot(const char *str);
#endif /* _DPAA2_TYPES_H */

View File

@ -0,0 +1,64 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright © 2021-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.
*/
#ifndef __MEMAC_MDIO_H
#define __MEMAC_MDIO_H
/* -------------------------------------------------------------------------- */
struct memacphy_softc_common {
device_t dev;
device_t dpnidev;
int phy;
};
int memacphy_miibus_readreg(device_t, int, int);
int memacphy_miibus_writereg(device_t, int, int, int);
void memacphy_miibus_statchg(struct memacphy_softc_common *);
int memacphy_set_ni_dev(struct memacphy_softc_common *, device_t);
int memacphy_get_phy_loc(struct memacphy_softc_common *, int *);
/* -------------------------------------------------------------------------- */
struct memac_mdio_softc_common {
device_t dev;
struct resource *mem_res;
bool is_little_endian;
};
int memac_miibus_readreg(struct memac_mdio_softc_common *, int, int);
int memac_miibus_writereg(struct memac_mdio_softc_common *, int, int, int);
ssize_t memac_mdio_get_property(device_t, device_t, const char *,
void *, size_t, device_property_type_t);
int memac_mdio_read_ivar(device_t, device_t, int, uintptr_t *);
int memac_mdio_generic_attach(struct memac_mdio_softc_common *);
int memac_mdio_generic_detach(struct memac_mdio_softc_common *);
#endif /* __MEMAC_MDIO_H */

View File

@ -0,0 +1,310 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright © 2021-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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <contrib/dev/acpica/include/acpi.h>
#include <dev/acpica/acpivar.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include "memac_mdio.h"
#include "memac_mdio_if.h"
#include "acpi_bus_if.h"
#include "miibus_if.h"
/* -------------------------------------------------------------------------- */
struct memacphy_softc_acpi {
struct memacphy_softc_common scc;
int uid;
uint64_t phy_channel;
char compatible[64];
};
static void
memacphy_acpi_miibus_statchg(device_t dev)
{
struct memacphy_softc_acpi *sc;
sc = device_get_softc(dev);
memacphy_miibus_statchg(&sc->scc);
}
static int
memacphy_acpi_set_ni_dev(device_t dev, device_t nidev)
{
struct memacphy_softc_acpi *sc;
sc = device_get_softc(dev);
return (memacphy_set_ni_dev(&sc->scc, nidev));
}
static int
memacphy_acpi_get_phy_loc(device_t dev, int *phy_loc)
{
struct memacphy_softc_acpi *sc;
sc = device_get_softc(dev);
return (memacphy_get_phy_loc(&sc->scc, phy_loc));
}
static int
memacphy_acpi_probe(device_t dev)
{
device_set_desc(dev, "MEMAC PHY (acpi)");
return (BUS_PROBE_DEFAULT);
}
static int
memacphy_acpi_attach(device_t dev)
{
struct memacphy_softc_acpi *sc;
ACPI_HANDLE h;
ssize_t s;
sc = device_get_softc(dev);
sc->scc.dev = dev;
h = acpi_get_handle(dev);
s = acpi_GetInteger(h, "_UID", &sc->uid);
if (ACPI_FAILURE(s)) {
device_printf(dev, "Cannot get '_UID' property: %zd\n", s);
return (ENXIO);
}
s = device_get_property(dev, "phy-channel",
&sc->phy_channel, sizeof(sc->phy_channel), DEVICE_PROP_UINT64);
if (s != -1)
sc->scc.phy = sc->phy_channel;
else
sc->scc.phy = -1;
s = device_get_property(dev, "compatible",
sc->compatible, sizeof(sc->compatible), DEVICE_PROP_ANY);
if (bootverbose)
device_printf(dev, "UID %#04x phy-channel %ju compatible '%s' phy %u\n",
sc->uid, sc->phy_channel,
sc->compatible[0] != '\0' ? sc->compatible : "", sc->scc.phy);
if (sc->scc.phy == -1)
return (ENXIO);
return (0);
}
static device_method_t memacphy_acpi_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, memacphy_acpi_probe),
DEVMETHOD(device_attach, memacphy_acpi_attach),
DEVMETHOD(device_detach, bus_generic_detach),
/* MII interface */
DEVMETHOD(miibus_readreg, memacphy_miibus_readreg),
DEVMETHOD(miibus_writereg, memacphy_miibus_writereg),
DEVMETHOD(miibus_statchg, memacphy_acpi_miibus_statchg),
/* memac */
DEVMETHOD(memac_mdio_set_ni_dev, memacphy_acpi_set_ni_dev),
DEVMETHOD(memac_mdio_get_phy_loc, memacphy_acpi_get_phy_loc),
DEVMETHOD_END
};
DEFINE_CLASS_0(memacphy_acpi, memacphy_acpi_driver, memacphy_acpi_methods,
sizeof(struct memacphy_softc_acpi));
EARLY_DRIVER_MODULE(memacphy_acpi, memac_mdio_acpi, memacphy_acpi_driver, 0, 0,
BUS_PASS_SUPPORTDEV);
DRIVER_MODULE(miibus, memacphy_acpi, miibus_driver, 0, 0);
MODULE_DEPEND(memacphy_acpi, miibus, 1, 1, 1);
/* -------------------------------------------------------------------------- */
struct memac_mdio_softc_acpi {
struct memac_mdio_softc_common scc;
};
static int
memac_acpi_miibus_readreg(device_t dev, int phy, int reg)
{
struct memac_mdio_softc_acpi *sc;
sc = device_get_softc(dev);
return (memac_miibus_readreg(&sc->scc, phy, reg));
}
static int
memac_acpi_miibus_writereg(device_t dev, int phy, int reg, int data)
{
struct memac_mdio_softc_acpi *sc;
sc = device_get_softc(dev);
return (memac_miibus_writereg(&sc->scc, phy, reg, data));
}
/* Context for walking PHY child devices. */
struct memac_mdio_walk_ctx {
device_t dev;
int count;
int countok;
};
static char *memac_mdio_ids[] = {
"NXP0006",
NULL
};
static int
memac_mdio_acpi_probe(device_t dev)
{
int rc;
if (acpi_disabled("fsl_memac_mdio"))
return (ENXIO);
rc = ACPI_ID_PROBE(device_get_parent(dev), dev, memac_mdio_ids, NULL);
if (rc <= 0)
device_set_desc(dev, "Freescale XGMAC MDIO Bus");
return (rc);
}
static ACPI_STATUS
memac_mdio_acpi_probe_child(ACPI_HANDLE h, device_t *dev, int level, void *arg)
{
struct memac_mdio_walk_ctx *ctx;
struct acpi_device *ad;
device_t child;
uint32_t adr;
ctx = (struct memac_mdio_walk_ctx *)arg;
ctx->count++;
if (ACPI_FAILURE(acpi_GetInteger(h, "_ADR", &adr)))
return (AE_OK);
/* Technically M_ACPIDEV */
if ((ad = malloc(sizeof(*ad), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL)
return (AE_OK);
child = device_add_child(ctx->dev, "memacphy_acpi", -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
memac_mdio_acpi_attach(device_t dev)
{
struct memac_mdio_softc_acpi *sc;
struct memac_mdio_walk_ctx ctx;
int error;
sc = device_get_softc(dev);
sc->scc.dev = dev;
error = memac_mdio_generic_attach(&sc->scc);
if (error != 0)
return (error);
ctx.dev = dev;
ctx.count = 0;
ctx.countok = 0;
ACPI_SCAN_CHILDREN(device_get_parent(dev), dev, 1,
memac_mdio_acpi_probe_child, &ctx);
if (ctx.countok > 0) {
bus_generic_probe(dev);
bus_generic_attach(dev);
}
return (0);
}
static int
memac_mdio_acpi_detach(device_t dev)
{
struct memac_mdio_softc_acpi *sc;
sc = device_get_softc(dev);
return (memac_mdio_generic_detach(&sc->scc));
}
static device_method_t memac_mdio_acpi_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, memac_mdio_acpi_probe),
DEVMETHOD(device_attach, memac_mdio_acpi_attach),
DEVMETHOD(device_detach, memac_mdio_acpi_detach),
/* MII interface */
DEVMETHOD(miibus_readreg, memac_acpi_miibus_readreg),
DEVMETHOD(miibus_writereg, memac_acpi_miibus_writereg),
/* .. */
DEVMETHOD(bus_add_child, bus_generic_add_child),
DEVMETHOD(bus_read_ivar, memac_mdio_read_ivar),
DEVMETHOD(bus_get_property, memac_mdio_get_property),
DEVMETHOD_END
};
DEFINE_CLASS_0(memac_mdio_acpi, memac_mdio_acpi_driver, memac_mdio_acpi_methods,
sizeof(struct memac_mdio_softc_acpi));
EARLY_DRIVER_MODULE(memac_mdio_acpi, acpi, memac_mdio_acpi_driver, 0, 0,
BUS_PASS_SUPPORTDEV);
DRIVER_MODULE(miibus, memac_mdio_acpi, miibus_driver, 0, 0);
MODULE_DEPEND(memac_mdio_acpi, miibus, 1, 1, 1);
MODULE_VERSION(memac_mdio_acpi, 1);

View File

@ -0,0 +1,306 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright © 2021-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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include "memac_mdio.h"
#include "miibus_if.h"
/* #define MEMAC_MDIO_DEBUG */
/* -------------------------------------------------------------------------- */
int
memacphy_miibus_readreg(device_t dev, int phy, int reg)
{
return (MIIBUS_READREG(device_get_parent(dev), phy, reg));
}
int
memacphy_miibus_writereg(device_t dev, int phy, int reg, int data)
{
return (MIIBUS_WRITEREG(device_get_parent(dev), phy, reg, data));
}
void
memacphy_miibus_statchg(struct memacphy_softc_common *sc)
{
if (sc->dpnidev != NULL)
MIIBUS_STATCHG(sc->dpnidev);
}
int
memacphy_set_ni_dev(struct memacphy_softc_common *sc, device_t nidev)
{
if (nidev == NULL)
return (EINVAL);
#if defined(MEMAC_MDIO_DEBUG)
if (bootverbose)
device_printf(sc->dev, "setting nidev %p (%s)\n",
nidev, device_get_nameunit(nidev));
#endif
if (sc->dpnidev != NULL)
return (EBUSY);
sc->dpnidev = nidev;
return (0);
}
int
memacphy_get_phy_loc(struct memacphy_softc_common *sc, int *phy_loc)
{
int error;
if (phy_loc == NULL)
return (EINVAL);
if (sc->phy == -1) {
*phy_loc = MII_PHY_ANY;
error = ENODEV;
} else {
*phy_loc = sc->phy;
error = 0;
}
#if defined(MEMAC_MDIO_DEBUG)
if (bootverbose)
device_printf(sc->dev, "returning phy_loc %d, error %d\n",
*phy_loc, error);
#endif
return (error);
}
/* -------------------------------------------------------------------------- */
/*
* MDIO Ethernet Management Interface Registers (internal PCS MDIO PHY)
* 0x0030 MDIO Configuration Register (MDIO_CFG)
* 0x0034 MDIO Control Register (MDIO_CTL)
* 0x0038 MDIO Data Register (MDIO_DATA)
* 0x003c MDIO Register Address Register (MDIO_ADDR)
*
* External MDIO interfaces
* 0x0030 External MDIO Configuration Register (EMDIO_CFG)
* 0x0034 External MDIO Control Register (EMDIO_CTL)
* 0x0038 External MDIO Data Register (EMDIO_DATA)
* 0x003c External MDIO Register Address Register (EMDIO_ADDR)
*/
#define MDIO_CFG 0x00030
#define MDIO_CFG_MDIO_RD_ER (1 << 1)
#define MDIO_CFG_ENC45 (1 << 6)
#define MDIO_CFG_BUSY (1 << 31)
#define MDIO_CTL 0x00034
#define MDIO_CTL_READ (1 << 15)
#define MDIO_CTL_PORT_ADDR(_x) (((_x) & 0x1f) << 5)
#define MDIO_CTL_DEV_ADDR(_x) ((_x) & 0x1f)
#define MDIO_DATA 0x00038
#define MDIO_ADDR 0x0003c
static uint32_t
memac_read_4(struct memac_mdio_softc_common *sc, uint32_t reg)
{
uint32_t v, r;
v = bus_read_4(sc->mem_res, reg);
if (sc->is_little_endian)
r = le32toh(v);
else
r = be32toh(v);
return (r);
}
static void
memac_write_4(struct memac_mdio_softc_common *sc, uint32_t reg, uint32_t val)
{
uint32_t v;
if (sc->is_little_endian)
v = htole32(val);
else
v = htobe32(val);
bus_write_4(sc->mem_res, reg, v);
}
static uint32_t
memac_miibus_wait_no_busy(struct memac_mdio_softc_common *sc)
{
uint32_t count, val;
for (count = 1000; count > 0; count--) {
val = memac_read_4(sc, MDIO_CFG);
if ((val & MDIO_CFG_BUSY) == 0)
break;
DELAY(1);
}
if (count == 0)
return (0xffff);
return (0);
}
int
memac_miibus_readreg(struct memac_mdio_softc_common *sc, int phy, int reg)
{
uint32_t cfg, ctl, val;
/* Set proper Clause 45 mode. */
cfg = memac_read_4(sc, MDIO_CFG);
/* XXX 45 support? */
cfg &= ~MDIO_CFG_ENC45; /* Use Clause 22 */
memac_write_4(sc, MDIO_CFG, cfg);
val = memac_miibus_wait_no_busy(sc);
if (val != 0)
return (0xffff);
/* To whom do we want to talk to.. */
ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(reg);
/* XXX do we need two writes for this to work reliably? */
memac_write_4(sc, MDIO_CTL, ctl | MDIO_CTL_READ);
val = memac_miibus_wait_no_busy(sc);
if (val != 0)
return (0xffff);
cfg = memac_read_4(sc, MDIO_CFG);
if (cfg & MDIO_CFG_MDIO_RD_ER)
return (0xffff);
val = memac_read_4(sc, MDIO_DATA);
val &= 0xffff;
#if defined(MEMAC_MDIO_DEBUG)
device_printf(sc->dev, "phy read %d:%d = %#06x\n", phy, reg, val);
#endif
return (val);
}
int
memac_miibus_writereg(struct memac_mdio_softc_common *sc, int phy, int reg, int data)
{
uint32_t cfg, ctl, val;
#if defined(MEMAC_MDIO_DEBUG)
device_printf(sc->dev, "phy write %d:%d\n", phy, reg);
#endif
/* Set proper Clause 45 mode. */
cfg = memac_read_4(sc, MDIO_CFG);
/* XXX 45 support? */
cfg &= ~MDIO_CFG_ENC45; /* Use Clause 22 */
memac_write_4(sc, MDIO_CFG, cfg);
val = memac_miibus_wait_no_busy(sc);
if (val != 0)
return (0xffff);
/* To whom do we want to talk to.. */
ctl = MDIO_CTL_PORT_ADDR(phy) | MDIO_CTL_DEV_ADDR(reg);
memac_write_4(sc, MDIO_CTL, ctl);
memac_write_4(sc, MDIO_DATA, data & 0xffff);
val = memac_miibus_wait_no_busy(sc);
if (val != 0)
return (0xffff);
return (0);
}
ssize_t
memac_mdio_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));
}
int
memac_mdio_read_ivar(device_t dev, device_t child, int index, uintptr_t *result)
{
return (BUS_READ_IVAR(device_get_parent(dev), dev, index, result));
}
int
memac_mdio_generic_attach(struct memac_mdio_softc_common *sc)
{
int rid;
rid = 0;
sc->mem_res = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY,
&rid, RF_ACTIVE | RF_SHAREABLE);
if (sc->mem_res == NULL) {
device_printf(sc->dev, "%s: cannot allocate mem resource\n",
__func__);
return (ENXIO);
}
sc->is_little_endian = device_has_property(sc->dev, "little-endian");
return (0);
}
int
memac_mdio_generic_detach(struct memac_mdio_softc_common *sc)
{
if (sc->mem_res != NULL)
bus_release_resource(sc->dev, SYS_RES_MEMORY,
rman_get_rid(sc->mem_res), sc->mem_res);
return (0);
}

View File

@ -0,0 +1,308 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright © 2021-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 <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/endian.h>
#include <sys/socket.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/simplebus.h>
#include <net/if.h>
#include <net/if_var.h>
#include <net/if_media.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include "memac_mdio.h"
#include "memac_mdio_if.h"
#include "ofw_bus_if.h"
#include "miibus_if.h"
/* -------------------------------------------------------------------------- */
struct memacphy_softc_fdt {
struct memacphy_softc_common scc;
uint32_t reg;
phandle_t xref;
};
static void
memacphy_fdt_miibus_statchg(device_t dev)
{
struct memacphy_softc_fdt *sc;
sc = device_get_softc(dev);
memacphy_miibus_statchg(&sc->scc);
}
static int
memacphy_fdt_set_ni_dev(device_t dev, device_t nidev)
{
struct memacphy_softc_fdt *sc;
sc = device_get_softc(dev);
return (memacphy_set_ni_dev(&sc->scc, nidev));
}
static int
memacphy_fdt_get_phy_loc(device_t dev, int *phy_loc)
{
struct memacphy_softc_fdt *sc;
sc = device_get_softc(dev);
return (memacphy_get_phy_loc(&sc->scc, phy_loc));
}
static int
memacphy_fdt_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
device_set_desc(dev, "MEMAC PHY (fdt)");
return (BUS_PROBE_DEFAULT);
}
static int
memacphy_fdt_attach(device_t dev)
{
struct memacphy_softc_fdt *sc;
phandle_t node;
ssize_t s;
int error;
sc = device_get_softc(dev);
sc->scc.dev = dev;
node = ofw_bus_get_node(dev);
s = device_get_property(dev, "reg", &sc->reg, sizeof(sc->reg),
DEVICE_PROP_UINT32);
if (s != -1)
sc->scc.phy = sc->reg;
else
sc->scc.phy = -1;
sc->xref = OF_xref_from_node(node);
error = OF_device_register_xref(sc->xref, dev);
if (error != 0)
device_printf(dev, "Failed to register xref %#x\n", sc->xref);
if (bootverbose)
device_printf(dev, "node %#x '%s': reg %#x xref %#x phy %u\n",
node, ofw_bus_get_name(dev), sc->reg, sc->xref, sc->scc.phy);
if (sc->scc.phy == -1)
error = ENXIO;
return (error);
}
static device_method_t memacphy_fdt_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, memacphy_fdt_probe),
DEVMETHOD(device_attach, memacphy_fdt_attach),
DEVMETHOD(device_detach, bus_generic_detach),
/* MII interface */
DEVMETHOD(miibus_readreg, memacphy_miibus_readreg),
DEVMETHOD(miibus_writereg, memacphy_miibus_writereg),
DEVMETHOD(miibus_statchg, memacphy_fdt_miibus_statchg),
/* memac */
DEVMETHOD(memac_mdio_set_ni_dev, memacphy_fdt_set_ni_dev),
DEVMETHOD(memac_mdio_get_phy_loc, memacphy_fdt_get_phy_loc),
DEVMETHOD_END
};
DEFINE_CLASS_0(memacphy_fdt, memacphy_fdt_driver, memacphy_fdt_methods,
sizeof(struct memacphy_softc_fdt));
EARLY_DRIVER_MODULE(memacphy_fdt, memac_mdio_fdt, memacphy_fdt_driver, 0, 0,
BUS_PASS_SUPPORTDEV);
DRIVER_MODULE(miibus, memacphy_fdt, miibus_driver, 0, 0);
MODULE_DEPEND(memacphy_fdt, miibus, 1, 1, 1);
/* -------------------------------------------------------------------------- */
/*
* Order in this softc is important; memac_mdio_fdt_attach() calls
* simplebus_init() which expects sb_sc at the beginning.
*/
struct memac_mdio_softc_fdt {
struct simplebus_softc sb_sc; /* Must stay first. */
struct memac_mdio_softc_common scc;
};
static int
memac_fdt_miibus_readreg(device_t dev, int phy, int reg)
{
struct memac_mdio_softc_fdt *sc;
sc = device_get_softc(dev);
return (memac_miibus_readreg(&sc->scc, phy, reg));
}
static int
memac_fdt_miibus_writereg(device_t dev, int phy, int reg, int data)
{
struct memac_mdio_softc_fdt *sc;
sc = device_get_softc(dev);
return (memac_miibus_writereg(&sc->scc, phy, reg, data));
}
static struct ofw_compat_data compat_data[] = {
{ "fsl,fman-memac-mdio", 1 },
{ NULL, 0 }
};
static int
memac_mdio_fdt_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
return (ENXIO);
device_set_desc(dev, "Freescale XGMAC MDIO Bus (FDT)");
return (BUS_PROBE_DEFAULT);
}
static int
memac_mdio_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, NULL, -1, NULL);
if (childdev == NULL)
return (ENXIO);
return (device_probe_and_attach(childdev));
}
static int
memac_mdio_fdt_attach(device_t dev)
{
struct memac_mdio_softc_fdt *sc;
phandle_t node, child;
int error;
sc = device_get_softc(dev);
sc->scc.dev = dev;
error = memac_mdio_generic_attach(&sc->scc);
if (error != 0)
return (error);
/* Attach the *phy* children represented in the device tree. */
bus_generic_probe(dev);
bus_enumerate_hinted_children(dev);
node = ofw_bus_get_node(dev);
simplebus_init(dev, node);
for (child = OF_child(node); child > 0; child = OF_peer(child)) {
if (!OF_hasprop(child, "reg"))
continue;
if (memac_mdio_fdt_probe_child(dev, child) != 0)
continue;
}
return (0);
}
static int
memac_mdio_fdt_detach(device_t dev)
{
struct memac_mdio_softc_fdt *sc;
sc = device_get_softc(dev);
return (memac_mdio_generic_detach(&sc->scc));
}
static const struct ofw_bus_devinfo *
memac_simplebus_get_devinfo(device_t bus, device_t child)
{
return (OFW_BUS_GET_DEVINFO(device_get_parent(bus), child));
}
static device_method_t memac_mdio_fdt_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, memac_mdio_fdt_probe),
DEVMETHOD(device_attach, memac_mdio_fdt_attach),
DEVMETHOD(device_detach, memac_mdio_fdt_detach),
/* MII interface */
DEVMETHOD(miibus_readreg, memac_fdt_miibus_readreg),
DEVMETHOD(miibus_writereg, memac_fdt_miibus_writereg),
/* OFW/simplebus */
DEVMETHOD(ofw_bus_get_devinfo, memac_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),
/* Bus interface */
DEVMETHOD(bus_add_child, bus_generic_add_child),
DEVMETHOD(bus_read_ivar, memac_mdio_read_ivar),
DEVMETHOD(bus_get_property, memac_mdio_get_property),
DEVMETHOD_END
};
DEFINE_CLASS_0(memac_mdio_fdt, memac_mdio_fdt_driver, memac_mdio_fdt_methods,
sizeof(struct memac_mdio_softc_fdt));
EARLY_DRIVER_MODULE(memac_mdio_fdt, simplebus, memac_mdio_fdt_driver, 0, 0,
BUS_PASS_SUPPORTDEV);
DRIVER_MODULE(miibus, memac_mdio_fdt, miibus_driver, 0, 0);
MODULE_DEPEND(memac_mdio_fdt, miibus, 1, 1, 1);
MODULE_VERSION(memac_mdio_fdt, 1);

View File

@ -0,0 +1,42 @@
#-
# SPDX-License-Identifier: BSD-2-Clause
#
# 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.
#
# $FreeBSD$
#
#include <machine/bus.h>
INTERFACE memac_mdio;
METHOD int set_ni_dev {
device_t dev;
device_t nidev;
};
METHOD int get_phy_loc {
device_t dev;
int *phy_loc;
};

View File

@ -99,6 +99,7 @@ SUBDIR= \
dc \
dcons \
dcons_crom \
${_dpaa2} \
${_dpdk_lpm4} \
${_dpdk_lpm6} \
${_dpms} \
@ -639,6 +640,7 @@ _cxgb= cxgb
.if ${MACHINE_CPUARCH} == "aarch64"
_allwinner= allwinner
_armv8crypto= armv8crypto
_dpaa2= dpaa2
_dwwdt= dwwdt
_em= em
_enetc= enetc

View File

@ -0,0 +1,43 @@
.PATH: ${SRCTOP}/sys/dev/dpaa2
KMOD= dpaa2
SRCS= dpaa2_mc.c
SRCS+= dpaa2_rc.c
SRCS+= dpaa2_io.c
SRCS+= dpaa2_bp.c
SRCS+= dpaa2_ni.c
SRCS+= dpaa2_mcp.c
SRCS+= dpaa2_swp.c
SRCS+= dpaa2_mac.c
SRCS+= dpaa2_con.c
SRCS+= dpaa2_cmd_if.c dpaa2_cmd_if.h
SRCS+= dpaa2_swp_if.c dpaa2_swp_if.h
SRCS+= dpaa2_mc_if.c dpaa2_mc_if.h
SRCS+= memac_mdio_common.c memac_mdio_if.c memac_mdio_if.h
SRCS+= bus_if.h device_if.h miibus_if.h
SRCS+= pcib_if.h pci_if.h
.if !empty(OPT_ACPI)
SRCS+= dpaa2_mc_acpi.c \
memac_mdio_acpi.c \
opt_acpi.h \
acpi_if.h \
acpi_bus_if.h
.endif
.if !empty(OPT_FDT)
SRCS+= dpaa2_mc_fdt.c \
memac_mdio_fdt.c \
opt_platform.h \
ofw_bus_if.h
.endif
MFILES= dev/dpaa2/dpaa2_cmd_if.m \
dev/dpaa2/dpaa2_swp_if.m \
dev/dpaa2/dpaa2_mc_if.m \
dev/dpaa2/memac_mdio_if.m
.include <bsd.kmod.mk>