aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/dpaa/dpaa_eth.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/dpaa/dpaa_eth.c')
-rw-r--r--sys/dev/dpaa/dpaa_eth.c719
1 files changed, 719 insertions, 0 deletions
diff --git a/sys/dev/dpaa/dpaa_eth.c b/sys/dev/dpaa/dpaa_eth.c
new file mode 100644
index 000000000000..6424a6e0b0c3
--- /dev/null
+++ b/sys/dev/dpaa/dpaa_eth.c
@@ -0,0 +1,719 @@
+/*-
+ * Copyright (c) 2026 Justin Hibbits
+ * Copyright (c) 2012 Semihalf.
+ * 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.
+ *
+ * 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/smp.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/if_types.h>
+#include <net/if_arp.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+
+#include <dev/mii/mii.h>
+#include <dev/mii/miivar.h>
+
+#include <vm/vm.h>
+#include <vm/pmap.h>
+
+#include "miibus_if.h"
+
+#include "bman.h"
+#include "dpaa_common.h"
+#include "dpaa_eth.h"
+#include "fman.h"
+#include "fman_parser.h"
+#include "fman_port.h"
+#include "fman_if.h"
+#include "fman_port_if.h"
+#include "if_dtsec.h"
+#include "qman.h"
+#include "qman_var.h"
+#include "qman_portal_if.h"
+
+
+#define DPAA_ETH_LOCK(sc) mtx_lock(&(sc)->sc_lock)
+#define DPAA_ETH_UNLOCK(sc) mtx_unlock(&(sc)->sc_lock)
+#define DPAA_ETH_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_lock, MA_OWNED)
+
+/**
+ * @group dTSEC RM private defines.
+ * @{
+ */
+#define DTSEC_BPOOLS_USED (1)
+#define DTSEC_MAX_TX_QUEUE_LEN 256
+
+struct dpaa_eth_frame_info {
+ struct mbuf *fi_mbuf;
+ struct fman_internal_context fi_ic;
+ struct dpaa_sgte fi_sgt[DPAA_NUM_OF_SG_TABLE_ENTRY];
+};
+
+enum dpaa_eth_pool_params {
+ DTSEC_RM_POOL_RX_LOW_MARK = 16,
+ DTSEC_RM_POOL_RX_HIGH_MARK = 64,
+ DTSEC_RM_POOL_RX_MAX_SIZE = 256,
+
+ DTSEC_RM_POOL_FI_LOW_MARK = 16,
+ DTSEC_RM_POOL_FI_HIGH_MARK = 64,
+ DTSEC_RM_POOL_FI_MAX_SIZE = 256,
+};
+
+#define DTSEC_RM_FQR_RX_CHANNEL 0x401
+#define DTSEC_RM_FQR_TX_CONF_CHANNEL 0
+enum dpaa_eth_fq_params {
+ DTSEC_RM_FQR_RX_WQ = 1,
+ DTSEC_RM_FQR_TX_WQ = 1,
+ DTSEC_RM_FQR_TX_CONF_WQ = 1
+};
+/** @} */
+
+
+/**
+ * @group dTSEC Frame Info routines.
+ * @{
+ */
+void
+dpaa_eth_fi_pool_free(struct dpaa_eth_softc *sc)
+{
+
+ if (sc->sc_fi_zone != NULL)
+ uma_zdestroy(sc->sc_fi_zone);
+}
+
+int
+dpaa_eth_fi_pool_init(struct dpaa_eth_softc *sc)
+{
+
+ snprintf(sc->sc_fi_zname, sizeof(sc->sc_fi_zname), "%s: Frame Info",
+ device_get_nameunit(sc->sc_dev));
+
+ sc->sc_fi_zone = uma_zcreate(sc->sc_fi_zname,
+ sizeof(struct dpaa_eth_frame_info), NULL, NULL, NULL, NULL,
+ UMA_ALIGN_PTR, 0);
+
+ return (0);
+}
+
+static struct dpaa_eth_frame_info *
+dpaa_eth_fi_alloc(struct dpaa_eth_softc *sc)
+{
+ struct dpaa_eth_frame_info *fi;
+
+ fi = uma_zalloc(sc->sc_fi_zone, M_NOWAIT | M_ZERO);
+
+ return (fi);
+}
+
+static void
+dpaa_eth_fi_free(struct dpaa_eth_softc *sc, struct dpaa_eth_frame_info *fi)
+{
+
+ uma_zfree(sc->sc_fi_zone, fi);
+}
+/** @} */
+
+
+/**
+ * @group dTSEC FMan PORT routines.
+ * @{
+ */
+int
+dpaa_eth_fm_port_rx_init(struct dpaa_eth_softc *sc)
+{
+ struct fman_port_params params;
+ int error;
+
+ params.dflt_fqid = sc->sc_rx_fqid;
+ params.err_fqid = sc->sc_rx_fqid;
+ params.rx_params.num_pools = 1;
+ params.rx_params.bpools[0].bpid = bman_get_bpid(sc->sc_rx_pool);
+ params.rx_params.bpools[0].size = MCLBYTES;
+ error = FMAN_PORT_CONFIG(sc->sc_rx_port, &params);
+ error = FMAN_PORT_INIT(sc->sc_rx_port);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "couldn't initialize FM Port RX.\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+int
+dpaa_eth_fm_port_tx_init(struct dpaa_eth_softc *sc)
+{
+ struct fman_port_params params;
+ int error;
+
+ params.dflt_fqid = sc->sc_tx_conf_fqid;
+ params.err_fqid = sc->sc_tx_conf_fqid;
+
+ error = FMAN_PORT_CONFIG(sc->sc_tx_port, &params);
+ error = FMAN_PORT_INIT(sc->sc_tx_port);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "couldn't initialize FM Port TX.\n");
+ return (ENXIO);
+ }
+
+ return (0);
+}
+/** @} */
+
+
+/**
+ * @group dTSEC buffer pools routines.
+ * @{
+ */
+static int
+dpaa_eth_pool_rx_put_buffer(struct dpaa_eth_softc *sc, uint8_t *buffer,
+ void *context)
+{
+
+ uma_zfree(sc->sc_rx_zone, buffer);
+
+ return (0);
+}
+
+static int
+dtsec_add_buffers(struct dpaa_eth_softc *sc, int count)
+{
+ struct bman_buffer bufs[8] = {};
+ int err;
+ int c;
+
+ while (count > 0) {
+ c = min(8, count);
+ for (int i = 0; i < c; i++) {
+ void *b;
+ vm_paddr_t pa;
+
+ b = uma_zalloc(sc->sc_rx_zone, M_NOWAIT);
+ if (b == NULL)
+ return (ENOMEM);
+ pa = pmap_kextract((vm_offset_t)b);
+ bufs[i].buf_hi = (pa >> 32);
+ bufs[i].buf_lo = (pa & 0xffffffff);
+ }
+
+ err = bman_put_buffers(sc->sc_rx_pool, bufs, c);
+ if (err != 0)
+ return (err);
+ count -= c;
+ }
+
+ return (0);
+}
+
+static void
+dpaa_eth_pool_rx_depleted(void *h_App, bool in)
+{
+ struct dpaa_eth_softc *sc;
+ unsigned int count;
+
+ sc = h_App;
+
+ if (!in)
+ return;
+
+ while (1) {
+ count = bman_count(sc->sc_rx_pool);
+ if (count > DTSEC_RM_POOL_RX_HIGH_MARK)
+ return;
+
+ /* Can only release 8 buffers at a time */
+ count = min(DTSEC_RM_POOL_RX_HIGH_MARK - count + 8, 8);
+ if (dtsec_add_buffers(sc, count) != 0)
+ return;
+ }
+}
+
+void
+dpaa_eth_pool_rx_free(struct dpaa_eth_softc *sc)
+{
+
+ if (sc->sc_rx_pool != NULL)
+ bman_pool_destroy(sc->sc_rx_pool);
+
+ if (sc->sc_rx_zone != NULL)
+ uma_zdestroy(sc->sc_rx_zone);
+}
+
+int
+dpaa_eth_pool_rx_init(struct dpaa_eth_softc *sc)
+{
+
+ /* MCLBYTES must be less than PAGE_SIZE */
+ CTASSERT(MCLBYTES < PAGE_SIZE);
+
+ snprintf(sc->sc_rx_zname, sizeof(sc->sc_rx_zname), "%s: RX Buffers",
+ device_get_nameunit(sc->sc_dev));
+
+ sc->sc_rx_zone = uma_zcreate(sc->sc_rx_zname, MCLBYTES, NULL,
+ NULL, NULL, NULL, MCLBYTES - 1, 0);
+
+ sc->sc_rx_pool = bman_pool_create(&sc->sc_rx_bpid, MCLBYTES,
+ DTSEC_RM_POOL_RX_MAX_SIZE, DTSEC_RM_POOL_RX_LOW_MARK,
+ DTSEC_RM_POOL_RX_HIGH_MARK, 0, 0, dpaa_eth_pool_rx_depleted, sc);
+ if (sc->sc_rx_pool == NULL) {
+ device_printf(sc->sc_dev, "NULL rx pool somehow\n");
+ dpaa_eth_pool_rx_free(sc);
+ return (EIO);
+ }
+
+ dtsec_add_buffers(sc, DTSEC_RM_POOL_RX_HIGH_MARK);
+
+ return (0);
+}
+/** @} */
+
+
+/**
+ * @group dTSEC Frame Queue Range routines.
+ * @{
+ */
+static void
+dpaa_eth_fq_mext_free(struct mbuf *m)
+{
+ struct dpaa_eth_softc *sc;
+ void *buffer;
+
+ buffer = m->m_ext.ext_arg1;
+ sc = m->m_ext.ext_arg2;
+ if (bman_count(sc->sc_rx_pool) <= DTSEC_RM_POOL_RX_MAX_SIZE)
+ bman_put_buffer(sc->sc_rx_pool,
+ pmap_kextract((vm_offset_t)buffer), sc->sc_rx_bpid);
+ else
+ dpaa_eth_pool_rx_put_buffer(sc, buffer, NULL);
+}
+
+static int
+dpaa_eth_update_csum_flags(struct qman_fd *frame,
+ struct fman_parse_result *prs, struct mbuf *m)
+{
+ uint16_t l3r = be16toh(prs->l3r);
+
+ /* TODO: nested protocols? */
+ if ((l3r & L3R_FIRST_IP_M) != 0) {
+ m->m_pkthdr.csum_flags |= CSUM_L3_CALC;
+ if ((l3r & L3R_FIRST_ERROR) == 0)
+ m->m_pkthdr.csum_flags |= CSUM_L3_VALID;
+ }
+ if (frame->cmd_stat & DPAA_FD_RX_STATUS_L4CV) {
+ m->m_pkthdr.csum_flags |= CSUM_L4_CALC;
+ m->m_pkthdr.csum_data = 0xffff;
+ if ((prs->l4r & L4R_TYPE_M) != 0 &&
+ (prs->l4r & L4R_ERR) == 0)
+ m->m_pkthdr.csum_flags |= CSUM_L4_VALID;
+ }
+
+ return (0);
+}
+
+static int
+dpaa_eth_fq_rx_callback(device_t portal, struct qman_fq *fq,
+ struct qman_fd *frame, void *app)
+{
+ struct dpaa_eth_softc *sc;
+ struct mbuf *m;
+ struct fman_internal_context *frame_ic;
+ void *frame_va;
+
+ m = NULL;
+ sc = app;
+
+ frame_va = DPAA_FD_GET_ADDR(frame);
+ frame_ic = frame_va; /* internal context at head of the frame */
+ /* Only simple (single- or multi-) frames are supported. */
+ KASSERT(frame->format == 0 || frame->format == 4,
+ ("%s(): Got unsupported frame format 0x%02X!", __func__,
+ frame->format));
+
+ if ((frame->cmd_stat & DPAA_FD_CMD_STAT_ERR_M) != 0) {
+ device_printf(sc->sc_dev, "RX error: 0x%08X\n",
+ frame->cmd_stat);
+ goto err;
+ }
+
+ m = m_gethdr(M_NOWAIT, MT_HEADER);
+ if (m == NULL)
+ goto err;
+
+ if (frame->format == 0) {
+ /* Single-frame format */
+ m_extadd(m, (char *)frame_va + frame->offset, frame->length,
+ dpaa_eth_fq_mext_free, frame_va, sc, 0, EXT_NET_DRV);
+ } else {
+ struct dpaa_sgte *sgt =
+ (struct dpaa_sgte *)(char *)frame_va + frame->offset;
+ /* Simple multi-frame format */
+ for (int i = 0; i < DPAA_NUM_OF_SG_TABLE_ENTRY; i++) {
+ if (sgt[i].length > 0)
+ m_extadd(m, PHYS_TO_DMAP(sgt[i].addr),
+ sgt[i].length, dpaa_eth_fq_mext_free,
+ PHYS_TO_DMAP(sgt[i].addr), sc, 0,
+ EXT_NET_DRV);
+ if (sgt[i].final)
+ break;
+ }
+ /* Free the SGT buffer, it's no longer needed. */
+ bman_put_buffer(sc->sc_rx_pool, frame->addr, sc->sc_rx_bpid);
+ }
+
+ if (if_getcapenable(sc->sc_ifnet) & (IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6))
+ dpaa_eth_update_csum_flags(frame, &frame_ic->prs, m);
+
+ m->m_pkthdr.rcvif = sc->sc_ifnet;
+ m->m_len = frame->length;
+ m_fixhdr(m);
+
+ if_input(sc->sc_ifnet, m);
+
+ return (1);
+
+err:
+ bman_put_buffer(sc->sc_rx_pool, frame->addr, sc->sc_rx_bpid);
+ if (m != NULL)
+ m_freem(m);
+
+ return (1);
+}
+
+static int
+dpaa_eth_fq_tx_confirm_callback(device_t portal, struct qman_fq *fq,
+ struct qman_fd *frame, void *app)
+{
+ struct dpaa_eth_frame_info *fi;
+ struct dpaa_eth_softc *sc;
+ unsigned int qlen;
+ struct dpaa_sgte *sgt0;
+
+ sc = app;
+
+ if ((frame->cmd_stat & DPAA_FD_TX_STAT_ERR_M) != 0)
+ device_printf(sc->sc_dev, "TX error: 0x%08X\n",
+ frame->cmd_stat);
+
+ /*
+ * We are storing struct dpaa_eth_frame_info in first entry
+ * of scatter-gather table.
+ */
+ sgt0 = (struct dpaa_sgte *)PHYS_TO_DMAP(frame->addr + frame->offset);
+ fi = (struct dpaa_eth_frame_info *)PHYS_TO_DMAP(sgt0->addr);
+
+ /* Free transmitted frame */
+ m_freem(fi->fi_mbuf);
+ dpaa_eth_fi_free(sc, fi);
+
+ qlen = qman_fq_get_counter(sc->sc_tx_conf_fq, QMAN_COUNTER_FRAME);
+
+ if (qlen == 0) {
+ DPAA_ETH_LOCK(sc);
+
+ if (sc->sc_tx_fq_full) {
+ sc->sc_tx_fq_full = 0;
+ dpaa_eth_if_start_locked(sc);
+ }
+
+ DPAA_ETH_UNLOCK(sc);
+ }
+
+ return (1);
+}
+
+void
+dpaa_eth_fq_rx_free(struct dpaa_eth_softc *sc)
+{
+ int cpu;
+
+ if (sc->sc_rx_fq)
+ qman_fq_free(sc->sc_rx_fq);
+ if (sc->sc_rx_channel != 0) {
+ CPU_FOREACH(cpu) {
+ device_t portal = DPCPU_ID_GET(cpu, qman_affine_portal);
+ QMAN_PORTAL_STATIC_DEQUEUE_RM_CHANNEL(portal,
+ sc->sc_rx_channel);
+ }
+ qman_free_channel(sc->sc_rx_channel);
+ }
+}
+
+int
+dpaa_eth_fq_rx_init(struct dpaa_eth_softc *sc)
+{
+ void *fq;
+ int error;
+ int cpu;
+
+ /* Default Frame Queue */
+ if (sc->sc_rx_channel == 0)
+ sc->sc_rx_channel = qman_alloc_channel();
+ fq = qman_fq_create(1, sc->sc_rx_channel, DTSEC_RM_FQR_RX_WQ,
+ false, 0, false, false, true, false, 0, 0, 0);
+ if (fq == NULL) {
+ device_printf(sc->sc_dev,
+ "could not create default RX queue\n");
+ return (EIO);
+ }
+
+ CPU_FOREACH(cpu) {
+ device_t portal = DPCPU_ID_GET(cpu, qman_affine_portal);
+ QMAN_PORTAL_STATIC_DEQUEUE_CHANNEL(portal, sc->sc_rx_channel);
+ }
+
+ sc->sc_rx_fq = fq;
+ sc->sc_rx_fqid = qman_fq_get_fqid(fq);
+
+ error = qman_fq_register_cb(fq, dpaa_eth_fq_rx_callback, sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not register RX callback\n");
+ dpaa_eth_fq_rx_free(sc);
+ return (EIO);
+ }
+
+ return (0);
+}
+
+void
+dpaa_eth_fq_tx_free(struct dpaa_eth_softc *sc)
+{
+
+ if (sc->sc_tx_fq)
+ qman_fq_free(sc->sc_tx_fq);
+
+ if (sc->sc_tx_conf_fq)
+ qman_fq_free(sc->sc_tx_conf_fq);
+}
+
+int
+dpaa_eth_fq_tx_init(struct dpaa_eth_softc *sc)
+{
+ int error;
+ void *fq;
+
+ /* TX Frame Queue */
+ fq = qman_fq_create(1, sc->sc_port_tx_qman_chan,
+ DTSEC_RM_FQR_TX_WQ, false, 0, false, false, true, false, 0, 0, 0);
+ if (fq == NULL) {
+ device_printf(sc->sc_dev, "could not create default TX queue"
+ "\n");
+ return (EIO);
+ }
+
+ sc->sc_tx_fq = fq;
+
+ if (sc->sc_rx_channel == 0)
+ sc->sc_rx_channel = qman_alloc_channel();
+ /* TX Confirmation Frame Queue */
+ fq = qman_fq_create(1, sc->sc_rx_channel,
+ DTSEC_RM_FQR_TX_CONF_WQ, false, 0, false, false, true, false, 0, 0,
+ 0);
+ if (fq == NULL) {
+ device_printf(sc->sc_dev, "could not create TX confirmation "
+ "queue\n");
+ dpaa_eth_fq_tx_free(sc);
+ return (EIO);
+ }
+
+ sc->sc_tx_conf_fq = fq;
+ sc->sc_tx_conf_fqid = qman_fq_get_fqid(fq);
+
+ error = qman_fq_register_cb(fq, dpaa_eth_fq_tx_confirm_callback, sc);
+ if (error != 0) {
+ device_printf(sc->sc_dev, "could not register TX confirmation "
+ "callback\n");
+ dpaa_eth_fq_tx_free(sc);
+ return (EIO);
+ }
+
+ return (0);
+}
+/** @} */
+
+/* Returns the cmd_stat field for the frame descriptor */
+static uint32_t
+dpaa_eth_tx_add_csum(struct dpaa_eth_frame_info *fi)
+{
+ struct mbuf *m = fi->fi_mbuf;
+ struct fman_parse_result *prs = &fi->fi_ic.prs;
+ uint32_t csum_flags = m->m_pkthdr.csum_flags;
+ uint8_t ether_size = ETHER_HDR_LEN;
+
+ if ((csum_flags & CSUM_FLAGS_TX) == 0)
+ return (0);
+
+ if (m->m_flags & M_VLANTAG)
+ ether_size += ETHER_VLAN_ENCAP_LEN;
+ if (csum_flags & CSUM_IP)
+ prs->l3r = L3R_FIRST_IPV4;
+ if (csum_flags & CSUM_IP_UDP) {
+ prs->l4r = L4R_TYPE_UDP;
+ prs->l4_off = ether_size + sizeof(struct ip);
+ } else if (csum_flags & CSUM_IP_TCP) {
+ prs->l4r = L4R_TYPE_TCP;
+ prs->l4_off = ether_size + sizeof(struct ip);
+ } else if (csum_flags & CSUM_IP6_UDP) {
+ prs->l3r = L3R_FIRST_IPV6;
+ prs->l4r = L4R_TYPE_UDP;
+ prs->l4_off = ether_size + sizeof(struct ip6_hdr);
+ } else if (csum_flags & CSUM_IP6_TCP) {
+ prs->l3r = L3R_FIRST_IPV6;
+ prs->l4r = L4R_TYPE_TCP;
+ prs->l4_off = ether_size + sizeof(struct ip6_hdr);
+ }
+
+ prs->ip_off[0] = ether_size;
+
+ return (DPAA_FD_TX_CMD_RPD | DPAA_FD_TX_CMD_DTC);
+}
+
+/**
+ * @group dTSEC IFnet routines.
+ * @{
+ */
+void
+dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc)
+{
+ vm_size_t dsize, psize, ssize;
+ struct dpaa_eth_frame_info *fi;
+ unsigned int qlen, i;
+ struct mbuf *m0, *m;
+ vm_offset_t vaddr;
+ struct dpaa_fd fd;
+
+ DPAA_ETH_LOCK_ASSERT(sc);
+ /* TODO: IFF_DRV_OACTIVE */
+
+ if ((sc->sc_mii->mii_media_status & IFM_ACTIVE) == 0)
+ return;
+
+ if ((if_getdrvflags(sc->sc_ifnet) & IFF_DRV_RUNNING) != IFF_DRV_RUNNING)
+ return;
+
+ while (!if_sendq_empty(sc->sc_ifnet)) {
+ /* Check length of the TX queue */
+ qlen = qman_fq_get_counter(sc->sc_tx_fq, QMAN_COUNTER_FRAME);
+
+ if (qlen >= DTSEC_MAX_TX_QUEUE_LEN) {
+ sc->sc_tx_fq_full = 1;
+ return;
+ }
+
+ fi = dpaa_eth_fi_alloc(sc);
+ if (fi == NULL)
+ return;
+
+ m0 = if_dequeue(sc->sc_ifnet);
+ if (m0 == NULL) {
+ dpaa_eth_fi_free(sc, fi);
+ return;
+ }
+
+ i = 0;
+ m = m0;
+ psize = 0;
+ dsize = 0;
+ fi->fi_mbuf = m0;
+ while (m && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
+ if (m->m_len == 0)
+ continue;
+
+ /*
+ * First entry in scatter-gather table is used to keep
+ * pointer to frame info structure.
+ */
+ fi->fi_sgt[i].addr = pmap_kextract((vm_offset_t)fi);
+ i++;
+
+ dsize = m->m_len;
+ vaddr = (vm_offset_t)m->m_data;
+ while (dsize > 0 && i < DPAA_NUM_OF_SG_TABLE_ENTRY) {
+ ssize = PAGE_SIZE - (vaddr & PAGE_MASK);
+ if (m->m_len < ssize)
+ ssize = m->m_len;
+
+ fi->fi_sgt[i].addr = pmap_kextract(vaddr);
+ fi->fi_sgt[i].length = ssize;
+
+ fi->fi_sgt[i].extension = 0;
+ fi->fi_sgt[i].final = 0;
+ fi->fi_sgt[i].bpid = 0;
+ fi->fi_sgt[i].offset = 0;
+
+ dsize -= ssize;
+ vaddr += ssize;
+ psize += ssize;
+ i++;
+ }
+
+ if (dsize > 0)
+ break;
+
+ m = m->m_next;
+ }
+
+ /* Check if SG table was constructed properly */
+ if (m != NULL || dsize != 0) {
+ dpaa_eth_fi_free(sc, fi);
+ m_freem(m0);
+ continue;
+ }
+
+ fi->fi_sgt[i - 1].final = 1;
+
+ fd.addr = pmap_kextract((vm_offset_t)&fi->fi_ic);
+ fd.length = psize;
+ fd.format = DPAA_FD_FORMAT_SHORT_MBSF;
+
+ fd.liodn = 0;
+ fd.bpid = 0;
+ fd.eliodn = 0;
+ fd.offset = offsetof(struct dpaa_eth_frame_info, fi_sgt) -
+ offsetof(struct dpaa_eth_frame_info, fi_ic);
+ fd.cmd_stat = dpaa_eth_tx_add_csum(fi);
+
+ DPAA_ETH_UNLOCK(sc);
+ if (qman_fq_enqueue(sc->sc_tx_fq, &fd) != 0) {
+ dpaa_eth_fi_free(sc, fi);
+ m_freem(m0);
+ }
+ DPAA_ETH_LOCK(sc);
+ }
+}
+/** @} */