dpaa: Migrate from NCSW base to a home-grown driver

The NCSW reference base requires tuning for each target, and currently
is configured for FMANv2.  This doesn't readily work on FMANv3 devices,
such as the T-series powerpc, or the LS1043 ARM.  Since Freescale/NXP
abandoned the NCSW driver a decade ago, it makes sense to abandon it
ourselves as well.  This new driver uses a combination of the NCSW
driver and the Linux driver (BSD/GPL dual licensed) as a reference, but
contains no actual code from them.

The DPAA (Data Path Acceleration Architecture) subsystem consists of the
following components:

* BMan -- Buffer Manager.  Manages buffer pools of different sizes (one
  size per pool, up to 64 pools)
* QMan -- Queue Manager.  Manages the interfaces between DPAA-based
  components and the CPU(s).
* FMan -- Frame Manager.  Responsible for all ethernet-related
  processing.  Consists itself of the following components:
  * Ports -- interfaces to the QMan.  An ethernet interface consists of
    2 ports.
    Ports use "Next-invoked action" (NIA) descriptors to form a pipeline
    for processing on receive and transmit.
  * Parser -- performs protocol header parsing and validation.  Both
    hardware and software parsers are available.
  * KeyGen -- Key generator, used to start the classification process
    (for the Policer), generating FQIDs and other keys based on the
    frame input.
  * Policer -- performs traffic shaping and classification
* MAC -- SoC specific ethernet MAC (dTSEC, TGEC, mEMAC).  Currently
  supports dTSEC and mEMAC, along with their MDIO blocks.

Additional components not yet handled:
SEC -- Security engine (crypto)
RE -- RAID engine
RapidIO
DCE -- Decompression/Compression engine, supports ZLIB, DEFLATE, and
GZIP, as well as base64 encoding and decoding.

BMan and QMan are accessed via cache-coherent portals, using ring
buffers as I/O.  The intent is for portals to be per-CPU (core/thread)
to reduce locking contention and improve performance.  This driver pins
interrupt handlers to the CPU "owning" a given portal, and uses critical
sections to prevent switching while accessing the portal.
This commit is contained in:
Justin Hibbits
2026-04-23 23:38:19 -04:00
parent d22051b18e
commit fd8d34ce27
41 changed files with 6044 additions and 3394 deletions
+21
View File
@@ -89,6 +89,27 @@ dev/adb/adb_hb_if.m optional adb
dev/adb/adb_if.m optional adb
dev/adb/adb_buttons.c optional adb
dev/agp/agp_apple.c optional agp powermac
dev/dpaa/bman_portals.c optional dpaa fdt
dev/dpaa/bman.c optional dpaa
dev/dpaa/bman_fdt.c optional dpaa fdt
dev/dpaa/dpaa_eth.c optional dpaa
dev/dpaa/fman.c optional dpaa fdt
dev/dpaa/fman_fdt.c optional dpaa fdt
dev/dpaa/fman_if.m optional dpaa
dev/dpaa/fman_mdio.c optional dpaa fdt
dev/dpaa/fman_port_if.m optional dpaa
dev/dpaa/fman_port.c optional dpaa
dev/dpaa/fman_xmdio.c optional dpaa fdt mdio miibus
dev/dpaa/dpaa_common.c optional dpaa
dev/dpaa/if_dtsec.c optional dpaa
dev/dpaa/if_dtsec_fdt.c optional dpaa fdt
dev/dpaa/if_memac.c optional dpaa
dev/dpaa/if_memac_fdt.c optional dpaa
dev/dpaa/portals_common.c optional dpaa
dev/dpaa/qman_portal_if.m optional dpaa
dev/dpaa/qman_portals.c optional dpaa fdt
dev/dpaa/qman.c optional dpaa
dev/dpaa/qman_fdt.c optional dpaa fdt
dev/fb/fb.c optional sc
dev/gpio/qoriq_gpio.c optional mpc85xx gpio
dev/hwpmc/hwpmc_e500.c optional hwpmc
+226 -223
View File
@@ -1,27 +1,7 @@
/*-
* Copyright (c) 2011-2012 Semihalf.
* All rights reserved.
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
* Copyright (c) 2026 Justin Hibbits
*/
#include <sys/param.h>
@@ -29,6 +9,7 @@
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/proc.h>
@@ -36,63 +17,161 @@
#include <sys/rman.h>
#include <sys/sched.h>
#include <machine/bus.h>
#include <machine/tlb.h>
#include "bman.h"
#include "dpaa_common.h"
#include "bman_var.h"
#define BMAN_POOL_SWDET(n) (0x000 + 4 * (n))
#define BMAN_POOL_HWDET(n) (0x100 + 4 * (n))
#define BMAN_POOL_SWDXT(n) (0x200 + 4 * (n))
#define BMAN_POOL_HWDXT(n) (0x300 + 4 * (n))
#define FBPR_FP_LWIT 0x804
#define BMAN_IP_REV_1 0x0bf8
#define IP_MAJ_S 8
#define IP_MAJ_M 0x0000ff00
#define IP_MIN_M 0x000000ff
#define BMAN_IP_REV_2 0x0bfc
#define BMAN_FBPR_BARE 0x0c00
#define BMAN_FBPR_BAR 0x0c04
#define BMAN_FBPR_AR 0x0c10
#define BMAN_LIODNR 0x0d08
#define BMAN_POOL_CONTENT(n) (0x0600 + 4 * (n))
#define BMAN_ECSR 0x0a00
#define BMAN_ECIR 0x0a04
#define ECIR_PORTAL(r) (((r) >> 24) & 0x0f)
#define ECIR_VERB(r) (((r) >> 16) & 0x07)
#define ECIR_R 0x00080000
#define ECIR_POOL(r) ((r) & 0x3f)
#define BMAN_CECR 0x0a34 /* Corruption Error Capture Register */
#define BMAN_CEAR 0x0a38 /* Corruption Error Address Register */
#define BMAN_AECR 0x0a34 /* Acces Error Capture Register */
#define BMAN_AEAR 0x0a38 /* Acces Error Address Register */
#define BMAN_ERR_ISR 0x0e00
#define BMAN_ERR_IER 0x0e04
#define BMAN_ERR_ISDR 0x0e08
#define ERR_EMAI 0x00000040
#define ERR_EMCI 0x00000020
#define ERR_IVCI 0x00000010
#define ERR_FLWI 0x00000008
#define ERR_MBEI 0x00000004
#define ERR_SBEI 0x00000002
#define ERR_BSCN 0x00000001
static MALLOC_DEFINE(M_BMAN, "bman", "DPAA Buffer Manager structures");
static struct bman_softc *bman_sc;
extern t_Handle bman_portal_setup(struct bman_softc *bsc);
static void
bman_exception(t_Handle h_App, e_BmExceptions exception)
bman_isr(void *arg)
{
struct bman_softc *sc;
const char *message;
struct bman_softc *sc = arg;
uint32_t ier, isr, isr_bit;
uint32_t reg;
sc = h_App;
ier = bus_read_4(sc->sc_rres, BMAN_ERR_IER);
isr = bus_read_4(sc->sc_rres, BMAN_ERR_ISR);
switch (exception) {
case e_BM_EX_INVALID_COMMAND:
message = "Invalid Command Verb";
break;
case e_BM_EX_FBPR_THRESHOLD:
message = "FBPR pool exhaused. Consider increasing "
"BMAN_MAX_BUFFERS";
break;
case e_BM_EX_SINGLE_ECC:
message = "Single bit ECC error";
break;
case e_BM_EX_MULTI_ECC:
message = "Multi bit ECC error";
break;
default:
message = "Unknown error";
isr_bit = (isr & ier);
if (isr_bit == 0)
goto end;
if (isr_bit & ERR_EMAI) {
device_printf(sc->sc_dev, "External memory access error\n");
reg = bus_read_4(sc->sc_rres, BMAN_AECR);
if (reg <= 63)
device_printf(sc->sc_dev, " pool %d\n", reg);
else
device_printf(sc->sc_dev, " FBPR free list\n");
reg = bus_read_4(sc->sc_rres, BMAN_AEAR);
device_printf(sc->sc_dev, " offset: %#x\n", reg);
}
device_printf(sc->sc_dev, "BMAN Exception: %s.\n", message);
if (isr_bit & ERR_EMCI) {
device_printf(sc->sc_dev, "External memory corruption error\n");
reg = bus_read_4(sc->sc_rres, BMAN_CECR);
if (reg <= 63)
device_printf(sc->sc_dev, " pool %d\n", reg);
else
device_printf(sc->sc_dev, " FBPR free list\n");
reg = bus_read_4(sc->sc_rres, BMAN_CEAR);
device_printf(sc->sc_dev, " offset: %#x\n", reg);
}
if (isr_bit & ERR_IVCI) {
reg = bus_read_4(sc->sc_rres, BMAN_ECIR);
device_printf(sc->sc_dev, "Invalid verb command\n");
device_printf(sc->sc_dev, "Portal: %d, ring: %s\n",
ECIR_POOL(reg), (reg & ECIR_R) ? "RCR" : "Command");
device_printf(sc->sc_dev, "verb: 0x%02x, pool: %d\n",
ECIR_VERB(reg), ECIR_POOL(reg));
}
if (isr_bit & (ERR_MBEI | ERR_SBEI)) {
if (isr_bit & ERR_MBEI)
device_printf(sc->sc_dev, "Multi-bit ECC error\n");
if (isr_bit & ERR_MBEI)
device_printf(sc->sc_dev, "Single-bit ECC error\n");
/* TODO: Add more error details for ECC errors. */
}
end:
bus_write_4(sc->sc_rres, BMAN_ERR_ISR, isr);
}
static void
bman_get_version(struct bman_softc *sc)
{
uint32_t reg = bus_read_4(sc->sc_rres, BMAN_IP_REV_1);
sc->sc_major = (reg & IP_MAJ_M) >> IP_MAJ_S;
sc->sc_minor = (reg & IP_MIN_M);
}
static int
bman_set_memory(struct bman_softc *sc, vm_paddr_t pa, vm_size_t size)
{
vm_paddr_t bar_pa;
if ((pa & (size - 1)) != 0 || (size & (size - 1)) != 0) {
device_printf(sc->sc_dev,
"invalid memory configuration: pa: %#jx, size: %#jx\n",
(uintmax_t)pa, (uintmax_t)size);
return (ENXIO);
}
bar_pa = bus_read_4(sc->sc_rres, BMAN_FBPR_BARE);
bar_pa <<= 32;
bar_pa |= bus_read_4(sc->sc_rres, BMAN_FBPR_BAR);
if (bar_pa != 0 && bar_pa != pa) {
device_printf(sc->sc_dev,
"attempted to reinitialize BMan with different BAR\n");
return (ENOMEM);
} else if (bar_pa == pa)
return (0);
bus_write_4(sc->sc_rres, BMAN_FBPR_BARE, pa >> 32);
bus_write_4(sc->sc_rres, BMAN_FBPR_BAR, pa & 0xffffffff);
bus_write_4(sc->sc_rres, BMAN_FBPR_AR, ilog2(size) - 1);
return (0);
}
int
bman_attach(device_t dev)
{
struct bman_softc *sc;
t_BmRevisionInfo rev;
t_Error error;
t_BmParam bp;
vm_paddr_t bp_pa;
size_t bp_size;
int bp_count;
sc = device_get_softc(dev);
sc->sc_dev = dev;
bman_sc = sc;
/* Check if MallocSmart allocator is ready */
if (XX_MallocSmartInit() != E_OK)
return (ENXIO);
/* Allocate resources */
sc->sc_rrid = 0;
sc->sc_rres = bus_alloc_resource_anywhere(dev, SYS_RES_MEMORY,
&sc->sc_rrid, BMAN_CCSR_SIZE, RF_ACTIVE);
sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
sc->sc_rrid, RF_ACTIVE);
if (sc->sc_rres == NULL)
return (ENXIO);
@@ -102,37 +181,34 @@ bman_attach(device_t dev)
if (sc->sc_ires == NULL)
goto err;
/* Initialize BMAN */
memset(&bp, 0, sizeof(bp));
bp.guestId = NCSW_MASTER_ID;
bp.baseAddress = rman_get_bushandle(sc->sc_rres);
bp.totalNumOfBuffers = BMAN_MAX_BUFFERS;
bp.f_Exception = bman_exception;
bp.h_App = sc;
bp.errIrq = (uintptr_t)sc->sc_ires;
bp.partBpidBase = 0;
bp.partNumOfPools = BM_MAX_NUM_OF_POOLS;
bman_get_version(sc);
if (sc->sc_major == 2 && sc->sc_minor == 0)
bp_count = BMAN_MAX_POOLS_1023;
else
bp_count = BMAN_MAX_POOLS;
sc->sc_bh = BM_Config(&bp);
if (sc->sc_bh == NULL)
/* TODO: LIODN */
bus_write_4(sc->sc_rres, BMAN_LIODNR, 0);
sc->sc_vmem = vmem_create("BMan Pools", 0, bp_count, 1, 0, M_WAITOK);
/* Pool is reserved memory, so no need to track it ourselves. */
dpaa_map_private_memory(dev, 0, "fsl,bman-fbpr", &bp_pa, &bp_size);
bman_set_memory(sc, bp_pa, bp_size);
/* Warn if FBPR drops below 5% total. */
bus_write_4(sc->sc_rres, FBPR_FP_LWIT, (bp_size / 8) / 20);
/* Clear interrupt status, and enable all interrupts. */
bus_write_4(sc->sc_rres, BMAN_ERR_ISR, 0xffffffff);
bus_write_4(sc->sc_rres, BMAN_ERR_IER, 0xffffffff);
bus_write_4(sc->sc_rres, BMAN_ERR_ISDR, 0);
/* Enable the IRQ line now. */
if (bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_NET, NULL, bman_isr,
sc, &sc->sc_icookie) != 0)
goto err;
/* Warn if there is less than 5% free FPBR's in pool */
error = BM_ConfigFbprThreshold(sc->sc_bh, (BMAN_MAX_BUFFERS / 8) / 20);
if (error != E_OK)
goto err;
error = BM_Init(sc->sc_bh);
if (error != E_OK)
goto err;
error = BM_GetRevision(sc->sc_bh, &rev);
if (error != E_OK)
goto err;
device_printf(dev, "Hardware version: %d.%d.\n",
rev.majorRev, rev.minorRev);
return (0);
err:
@@ -147,9 +223,10 @@ bman_detach(device_t dev)
sc = device_get_softc(dev);
if (sc->sc_bh != NULL)
BM_Free(sc->sc_bh);
if (sc->sc_vmem != NULL)
vmem_destroy(sc->sc_vmem);
if (sc->sc_icookie != NULL)
bus_teardown_intr(dev, sc->sc_ires, sc->sc_icookie);
if (sc->sc_ires != NULL)
bus_release_resource(dev, SYS_RES_IRQ,
sc->sc_irid, sc->sc_ires);
@@ -186,179 +263,105 @@ bman_shutdown(device_t dev)
* BMAN API
*/
t_Handle
bman_pool_create(uint8_t *bpid, uint16_t bufferSize, uint16_t maxBuffers,
uint16_t minBuffers, uint16_t allocBuffers, t_GetBufFunction *f_GetBuf,
t_PutBufFunction *f_PutBuf, uint32_t dep_sw_entry, uint32_t dep_sw_exit,
uint32_t dep_hw_entry, uint32_t dep_hw_exit,
t_BmDepletionCallback *f_Depletion, t_Handle h_BufferPool,
t_PhysToVirt *f_PhysToVirt, t_VirtToPhys *f_VirtToPhys)
struct bman_pool *
bman_new_pool(void)
{
uint32_t thresholds[MAX_DEPLETION_THRESHOLDS];
struct bman_softc *sc;
t_Handle pool, portal;
t_BmPoolParam bpp;
int error;
vmem_addr_t bpid;
struct bman_pool *pool;
sc = bman_sc;
pool = NULL;
sched_pin();
if (vmem_alloc(sc->sc_vmem, 1, M_FIRSTFIT | M_NOWAIT, &bpid) != 0)
return (NULL);
portal = bman_portal_setup(sc);
if (portal == NULL)
goto err;
pool = malloc(sizeof(*pool), M_BMAN, M_WAITOK | M_ZERO);
memset(&bpp, 0, sizeof(bpp));
bpp.h_Bm = sc->sc_bh;
bpp.h_BmPortal = portal;
bpp.h_App = h_BufferPool;
bpp.numOfBuffers = allocBuffers;
bpp.bufferPoolInfo.h_BufferPool = h_BufferPool;
bpp.bufferPoolInfo.f_GetBuf = f_GetBuf;
bpp.bufferPoolInfo.f_PutBuf = f_PutBuf;
bpp.bufferPoolInfo.f_PhysToVirt = f_PhysToVirt;
bpp.bufferPoolInfo.f_VirtToPhys = f_VirtToPhys;
bpp.bufferPoolInfo.bufferSize = bufferSize;
pool = BM_POOL_Config(&bpp);
if (pool == NULL)
goto err;
/*
* Buffer context must be disabled on FreeBSD
* as it could cause memory corruption.
*/
BM_POOL_ConfigBuffContextMode(pool, 0);
if (minBuffers != 0 || maxBuffers != 0) {
error = BM_POOL_ConfigStockpile(pool, maxBuffers, minBuffers);
if (error != E_OK)
goto err;
}
if (f_Depletion != NULL) {
thresholds[BM_POOL_DEP_THRESH_SW_ENTRY] = dep_sw_entry;
thresholds[BM_POOL_DEP_THRESH_SW_EXIT] = dep_sw_exit;
thresholds[BM_POOL_DEP_THRESH_HW_ENTRY] = dep_hw_entry;
thresholds[BM_POOL_DEP_THRESH_HW_EXIT] = dep_hw_exit;
error = BM_POOL_ConfigDepletion(pool, f_Depletion, thresholds);
if (error != E_OK)
goto err;
}
error = BM_POOL_Init(pool);
if (error != E_OK)
goto err;
*bpid = BM_POOL_GetId(pool);
sc->sc_bpool_cpu[*bpid] = PCPU_GET(cpuid);
sched_unpin();
pool->bpid = bpid;
return (pool);
}
err:
if (pool != NULL)
BM_POOL_Free(pool);
struct bman_pool *
bman_pool_create(uint8_t *bpid, uint16_t buffer_size, uint16_t max_buffers,
uint32_t dep_sw_entry, uint32_t dep_sw_exit,
uint32_t dep_hw_entry, uint32_t dep_hw_exit,
bm_depletion_handler dep_cb, void *arg)
{
struct bman_softc *sc;
struct bman_pool *bp;
sched_unpin();
sc = bman_sc;
bp = bman_new_pool();
if (bpid != NULL)
*bpid = bp->bpid;
return (NULL);
if (dep_cb) {
bp->dep_cb = dep_cb;
bus_write_4(sc->sc_rres, BMAN_POOL_SWDET(bp->bpid),
dep_sw_entry);
bus_write_4(sc->sc_rres, BMAN_POOL_SWDXT(bp->bpid),
dep_sw_exit);
bus_write_4(sc->sc_rres, BMAN_POOL_HWDET(bp->bpid),
dep_hw_entry);
bus_write_4(sc->sc_rres, BMAN_POOL_HWDXT(bp->bpid),
dep_hw_exit);
bp->arg = arg;
bman_portal_enable_scn(DPCPU_GET(bman_affine_portal), bp);
}
return (bp);
}
int
bman_pool_destroy(t_Handle pool)
bman_pool_destroy(struct bman_pool *pool)
{
struct bman_softc *sc;
sc = bman_sc;
thread_lock(curthread);
sched_bind(curthread, sc->sc_bpool_cpu[BM_POOL_GetId(pool)]);
thread_unlock(curthread);
BM_POOL_Free(pool);
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
/* Need to error, or print a warning, if the pool isn't empty */
if (bman_count(pool) != 0)
return (EBUSY);
vmem_free(bman_sc->sc_vmem, pool->bpid, 1);
free(pool, M_BMAN);
return (0);
}
int
bman_pool_fill(t_Handle pool, uint16_t nbufs)
bman_put_buffers(struct bman_pool *pool, struct bman_buffer *buffers, int count)
{
struct bman_softc *sc;
t_Handle portal;
struct bman_portal_softc *portal;
int error;
sc = bman_sc;
sched_pin();
critical_enter();
portal = bman_portal_setup(sc);
portal = DPCPU_GET(bman_affine_portal);
if (portal == NULL) {
sched_unpin();
critical_exit();
return (EIO);
}
error = BM_POOL_FillBufs(pool, portal, nbufs);
sched_unpin();
return ((error == E_OK) ? 0 : EIO);
}
void *
bman_get_buffer(t_Handle pool)
{
struct bman_softc *sc;
t_Handle portal;
void *buffer;
sc = bman_sc;
sched_pin();
portal = bman_portal_setup(sc);
if (portal == NULL) {
sched_unpin();
return (NULL);
while (count > 0) {
int c = min(count, 8);
error = bman_release(pool, buffers, c);
buffers += c;
count -= c;
}
buffer = BM_POOL_GetBuf(pool, portal);
critical_exit();
sched_unpin();
return (buffer);
}
int
bman_put_buffer(t_Handle pool, void *buffer)
{
struct bman_softc *sc;
t_Handle portal;
int error;
sc = bman_sc;
sched_pin();
portal = bman_portal_setup(sc);
if (portal == NULL) {
sched_unpin();
return (EIO);
}
error = BM_POOL_PutBuf(pool, portal, buffer);
sched_unpin();
return ((error == E_OK) ? 0 : EIO);
return (error);
}
uint32_t
bman_count(t_Handle pool)
bman_get_bpid(struct bman_pool *pool)
{
return (pool->bpid);
}
uint32_t
bman_count(struct bman_pool *pool)
{
return (BM_POOL_GetCounter(pool, e_BM_POOL_COUNTERS_CONTENT));
return (bus_read_4(bman_sc->sc_rres, BMAN_POOL_CONTENT(pool->bpid)));
}
+49 -125
View File
@@ -1,3 +1,9 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
/*-
* Copyright (c) 2011-2012 Semihalf.
* All rights reserved.
@@ -27,29 +33,16 @@
#ifndef _BMAN_H
#define _BMAN_H
#include <sys/vmem.h>
#include <machine/vmparam.h>
#include <contrib/ncsw/inc/Peripherals/bm_ext.h>
/*
* BMAN Configuration
*/
/* Maximum number of buffers in all BMAN pools */
#define BMAN_MAX_BUFFERS 4096
/*
* Portal definitions
*/
#define BMAN_CE_PA(base) (base)
#define BMAN_CI_PA(base) ((base) + 0x100000)
#define BMAN_PORTAL_CE_PA(base, n) \
(BMAN_CE_PA(base) + ((n) * BMAN_PORTAL_CE_SIZE))
#define BMAN_PORTAL_CI_PA(base, n) \
(BMAN_CI_PA(base) + ((n) * BMAN_PORTAL_CI_SIZE))
#define BMAN_CCSR_SIZE 0x1000
struct bman_softc {
device_t sc_dev; /* device handle */
@@ -57,129 +50,42 @@ struct bman_softc {
struct resource *sc_rres; /* register resource */
int sc_irid; /* interrupt rid */
struct resource *sc_ires; /* interrupt resource */
bool sc_regs_mapped[MAXCPU]; /* register mapping status */
t_Handle sc_bh; /* BMAN handle */
t_Handle sc_bph[MAXCPU]; /* BMAN portal handles */
vm_paddr_t sc_bp_pa; /* BMAN portals PA */
unsigned int sc_bpool_cpu[BM_MAX_NUM_OF_POOLS];
void *sc_icookie;
vmem_t *sc_vmem; /* resource pool */
int sc_major;
int sc_minor;
};
struct bman_buffer {
uint16_t bpid;
uint16_t buf_hi;
uint32_t buf_lo;
} __aligned(8);
struct bman_pool;
struct bman_buffer;
typedef void (*bm_depletion_handler)(void *, bool);
/*
* External API
*/
/*
* @brief Function to create BMAN pool.
*
* @param bpid The pointer to variable where Buffer Pool ID will be
* stored.
*
* @param bufferSize The size of buffers in newly created pool.
*
* @param maxBuffers The maximum number of buffers in software stockpile.
* Set to 0 if software stockpile should not be created.
*
* @param minBuffers The minimum number of buffers in software stockpile.
* Set to 0 if software stockpile should not be created.
*
* @param allocBuffers The number of buffers to preallocate during pool
* creation.
*
* @param f_GetBuf The buffer allocating function. Called only by
* bman_pool_create() and bman_pool_fill().
*
* @param f_PutBuf The buffer freeing function. Called only by
* bman_pool_destroy().
*
* @param dep_sw_entry The software portal depletion entry threshold.
* Set to 0 if depletion should not be signaled on
* software portal.
*
* @param dep_sw_exit The software portal depletion exit threshold.
* Set to 0 if depletion should not be signaled on
* software portal.
*
* @param dep_hw_entry The hardware portal depletion entry threshold.
* Set to 0 if depletion should not be signaled on
* hardware portal.
*
* @param dep_hw_exit The hardware portal depletion exit threshold.
* Set to 0 if depletion should not be signaled on
* hardware portal.
*
* @param f_Depletion The software portal depletion notification function.
* Set to NULL if depletion notification is not used.
*
* @param h_BufferPool The user provided buffer pool context passed to
* f_GetBuf, f_PutBuf and f_Depletion functions.
*
* @param f_PhysToVirt The PA to VA translation function. Set to NULL if
* default one should be used.
*
* @param f_VirtToPhys The VA to PA translation function. Set to NULL if
* default one should be used.
*
* @returns Handle to newly created BMAN pool or NULL on error.
*
* @cautions If pool uses software stockpile, all accesses to given
* pool must be protected by lock. Even if only hardware
* portal depletion notification is used, the caller must
* provide valid @p f_Depletion function.
*/
t_Handle bman_pool_create(uint8_t *bpid, uint16_t bufferSize,
uint16_t maxBuffers, uint16_t minBuffers, uint16_t allocBuffers,
t_GetBufFunction *f_GetBuf, t_PutBufFunction *f_PutBuf,
uint32_t dep_sw_entry, uint32_t dep_sw_exit, uint32_t dep_hw_entry,
uint32_t dep_hw_exit, t_BmDepletionCallback *f_Depletion,
t_Handle h_BufferPool, t_PhysToVirt *f_PhysToVirt,
t_VirtToPhys *f_VirtToPhys);
/*
* @brief Fill pool with buffers.
*
* The bman_pool_fill() function fills the BMAN pool with buffers. The buffers
* are allocated through f_GetBuf function (see bman_pool_create() description).
*
* @param pool The BMAN pool handle.
* @param nbufs The number of buffers to allocate. To maximize
* performance this value should be multiple of 8.
*
* @returns Zero on success or error code on failure.
*/
int bman_pool_fill(t_Handle pool, uint16_t nbufs);
struct bman_pool *bman_new_pool(void);
struct bman_pool *bman_pool_create(uint8_t *bpid, uint16_t buffer_size,
uint16_t max_buffers, uint32_t dep_sw_entry, uint32_t dep_sw_exit, uint32_t
dep_hw_entry, uint32_t dep_hw_exit, bm_depletion_handler dep_cb, void *arg);
/*
* @brief Destroy pool.
*
* The bman_pool_destroy() function destroys the BMAN pool. Buffers for pool
* are free through f_PutBuf function (see bman_pool_create() description).
* The bman_pool_destroy() function destroys the BMAN pool.
* The buffer pool must be empty.
*
* @param pool The BMAN pool handle.
*
* @returns Zero on success or error code on failure.
* @return 0 on success, EBUSY if the pool is not empty.
*/
int bman_pool_destroy(t_Handle pool);
/*
* @brief Get a buffer from BMAN pool.
*
* @param pool The BMAN pool handle.
*
* @returns Pointer to the buffer or NULL if pool is empty.
*/
void *bman_get_buffer(t_Handle pool);
/*
* @brief Put a buffer to BMAN pool.
*
* @param pool The BMAN pool handle.
* @param buffer The pointer to buffer.
*
* @returns Zero on success or error code on failure.
*/
int bman_put_buffer(t_Handle pool, void *buffer);
int bman_pool_destroy(struct bman_pool *pool);
/*
* @brief Count free buffers in given pool.
@@ -188,7 +94,25 @@ int bman_put_buffer(t_Handle pool, void *buffer);
*
* @returns Number of free buffers in pool.
*/
uint32_t bman_count(t_Handle pool);
uint32_t bman_count(struct bman_pool *pool);
int bman_put_buffers(struct bman_pool *, struct bman_buffer *, int);
static inline int
bman_put_buffer(struct bman_pool *p, vm_paddr_t buf, int bpid)
{
struct bman_buffer b = {
.bpid = bpid,
.buf_hi = ((uintptr_t)buf) >> 32,
.buf_lo = ((uintptr_t)buf) & 0xffffffff
};
return (bman_put_buffers(p, &b, 1));
}
int bman_acquire(struct bman_pool *, struct bman_buffer *, uint8_t);
int bman_create_affine_portal(device_t, vm_offset_t, vm_offset_t, int);
void bman_destroy_affine_portal(int);
uint32_t bman_get_bpid(struct bman_pool *);
/*
* Bus i/f
+23 -125
View File
@@ -40,6 +40,7 @@
#include <dev/ofw/ofw_subr.h>
#include "bman.h"
#include "bman_var.h"
#include "portals.h"
#define FBMAN_DEVSTR "Freescale Buffer Manager"
@@ -59,12 +60,7 @@ static device_method_t bman_methods[] = {
DEVMETHOD_END
};
static driver_t bman_driver = {
"bman",
bman_methods,
sizeof(struct bman_softc),
};
DEFINE_CLASS_0(bman, bman_driver, bman_methods, sizeof(struct bman_softc));
EARLY_DRIVER_MODULE(bman, simplebus, bman_driver, 0, 0, BUS_PASS_SUPPORTDEV);
static int
@@ -82,144 +78,46 @@ bman_fdt_probe(device_t dev)
/*
* BMAN Portals
*/
#define BMAN_PORT_DEVSTR "Freescale Buffer Manager - Portals"
#define BMAN_PORT_DEVSTR "Freescale Buffer Manager - Portal"
static device_probe_t bman_portals_fdt_probe;
static device_attach_t bman_portals_fdt_attach;
static int portal_ncpus;
static device_probe_t bman_portal_fdt_probe;
static device_attach_t bman_portal_fdt_attach;
static device_method_t bm_portals_methods[] = {
static device_method_t bman_portal_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, bman_portals_fdt_probe),
DEVMETHOD(device_attach, bman_portals_fdt_attach),
DEVMETHOD(device_detach, bman_portals_detach),
DEVMETHOD(device_probe, bman_portal_fdt_probe),
DEVMETHOD(device_attach, bman_portal_fdt_attach),
DEVMETHOD(device_detach, bman_portal_detach),
DEVMETHOD_END
};
static driver_t bm_portals_driver = {
"bman-portals",
bm_portals_methods,
sizeof(struct dpaa_portals_softc),
};
EARLY_DRIVER_MODULE(bman_portals, ofwbus, bm_portals_driver, 0, 0,
BUS_PASS_BUS);
static void
get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep)
{
*addrp = 2;
*sizep = 1;
OF_getencprop(node, "#address-cells", addrp, sizeof(*addrp));
OF_getencprop(node, "#size-cells", sizep, sizeof(*sizep));
}
DEFINE_CLASS_0(bman_portal, bman_portal_driver, bman_portal_methods,
sizeof(struct bman_portal_softc));
EARLY_DRIVER_MODULE(bman_portal, simplebus, bman_portal_driver, 0, 0,
BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
static int
bman_portals_fdt_probe(device_t dev)
bman_portal_fdt_probe(device_t dev)
{
phandle_t node;
if (ofw_bus_is_compatible(dev, "simple-bus")) {
node = ofw_bus_get_node(dev);
for (node = OF_child(node); node > 0; node = OF_peer(node)) {
if (ofw_bus_node_is_compatible(node, "fsl,bman-portal"))
break;
}
if (node <= 0)
return (ENXIO);
} else if (!ofw_bus_is_compatible(dev, "fsl,bman-portals"))
if (!ofw_bus_is_compatible(dev, "fsl,bman-portal"))
return (ENXIO);
device_set_desc(dev, BMAN_PORT_DEVSTR);
return (BUS_PROBE_DEFAULT);
}
static int
bman_portals_fdt_attach(device_t dev)
bman_portal_fdt_attach(device_t dev)
{
struct dpaa_portals_softc *sc;
struct resource_list_entry *rle;
phandle_t node, child, cpu_node;
vm_paddr_t portal_pa;
vm_size_t portal_size;
uint32_t addr, size;
ihandle_t cpu;
int cpu_num, cpus, intr_rid;
struct dpaa_portals_devinfo di;
struct ofw_bus_devinfo ofw_di = {};
int portal_cpu = portal_ncpus;
cpus = 0;
sc = device_get_softc(dev);
sc->sc_dev = dev;
/* Don't attach to more portals than we have CPUs */
if (mp_ncpus == portal_ncpus)
return (ENXIO);
node = ofw_bus_get_node(dev);
get_addr_props(node, &addr, &size);
portal_ncpus++;
/* Find portals tied to CPUs */
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
if (cpus >= mp_ncpus)
break;
if (!ofw_bus_node_is_compatible(child, "fsl,bman-portal")) {
continue;
}
/* Checkout related cpu */
if (OF_getprop(child, "cpu-handle", (void *)&cpu,
sizeof(cpu)) > 0) {
cpu_node = OF_instance_to_package(cpu);
/* Acquire cpu number */
if (OF_getencprop(cpu_node, "reg", &cpu_num, sizeof(cpu_num)) <= 0) {
device_printf(dev, "Could not retrieve CPU number.\n");
return (ENXIO);
}
} else
cpu_num = cpus;
cpus++;
if (ofw_bus_gen_setup_devinfo(&ofw_di, child) != 0) {
device_printf(dev, "could not set up devinfo\n");
continue;
}
resource_list_init(&di.di_res);
if (ofw_bus_reg_to_rl(dev, child, addr, size, &di.di_res)) {
device_printf(dev, "%s: could not process 'reg' "
"property\n", ofw_di.obd_name);
ofw_bus_gen_destroy_devinfo(&ofw_di);
continue;
}
if (ofw_bus_intr_to_rl(dev, child, &di.di_res, &intr_rid)) {
device_printf(dev, "%s: could not process "
"'interrupts' property\n", ofw_di.obd_name);
resource_list_free(&di.di_res);
ofw_bus_gen_destroy_devinfo(&ofw_di);
continue;
}
di.di_intr_rid = intr_rid;
ofw_reg_to_paddr(child, 0, &portal_pa, &portal_size, NULL);
rle = resource_list_find(&di.di_res, SYS_RES_MEMORY, 0);
if (sc->sc_dp_pa == 0)
sc->sc_dp_pa = portal_pa - rle->start;
portal_size = rle->end + 1;
rle = resource_list_find(&di.di_res, SYS_RES_MEMORY, 1);
portal_size = ulmax(rle->end + 1, portal_size);
sc->sc_dp_size = ulmax(sc->sc_dp_size, portal_size);
if (dpaa_portal_alloc_res(dev, &di, cpu_num))
goto err;
}
ofw_bus_gen_destroy_devinfo(&ofw_di);
return (bman_portals_attach(dev));
err:
resource_list_free(&di.di_res);
ofw_bus_gen_destroy_devinfo(&ofw_di);
bman_portals_detach(dev);
return (ENXIO);
return (bman_portal_attach(dev, portal_cpu));
}
+268 -118
View File
@@ -1,27 +1,7 @@
/*-
* Copyright (c) 2012 Semihalf.
* All rights reserved.
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
* Copyright (c) 2026 Justin Hibbits
*/
#include "opt_platform.h"
@@ -30,6 +10,8 @@
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/cpuset.h>
#include <sys/interrupt.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/mutex.h>
@@ -46,132 +28,300 @@
#include <powerpc/mpc85xx/mpc85xx.h>
#include "bman.h"
#include "bman_var.h"
#include "portals.h"
t_Handle bman_portal_setup(struct bman_softc *);
#define BCSP_CFG 0x0100
#define CFG_RPM_M 0x00000003
#define CFG_RPM_PI 0x00000000
#define CFG_RPM_PE 0x00000001
#define CFG_RPM_VBM 0x00000002
#define BCSP_SCN0 0x0200
#define BCSP_SCN1 0x0204
#define BCSP_ISR 0x0e00
#define BCSP_IER 0x0e04
#define BCSP_ISDR 0x0e08
#define INTR_RCDI 0x00000004
#define INTR_RCRI 0x00000002
#define INTR_BSCN 0x00000001
struct dpaa_portals_softc *bp_sc;
#define BMAN_CE_CR 0x0000
#define BMAN_CE_RR0 0x0100
#define BMAN_CE_RR1 0x0140
#define BMAN_CE_RR(n) (BMAN_CE_RR0 + 0x40 * (n))
#define BMAN_CE_RCR 0x1000
#define BCSP_RCR_PI_CENA 0x3000
#define BCSP_RCR_CI_CENA 0x3100
#define BCSP_RCR_PI_CINH 0x000
#define BCSP_RCR_CI_CINH 0x004
#define BMAN_MC_VERB_VBIT 0x80
#define BMAN_MC_VERB_ACQUIRE 0x10
#define BMAN_MC_VERB_QUERY 0x40
#define BMAN_RCR_VERB_BPID0 0x20
#define BMAN_RCR_VERB_BPID_BUF 0x30
struct bman_mc_command {
uint8_t verb;
uint8_t cd;
uint8_t rsvd[62];
};
union bman_mc_result {
struct {
uint8_t verb;
uint8_t cd;
uint8_t rsvd[62];
};
struct {
uint64_t rsvd_q1[5];
uint64_t bp_as;
uint64_t rsvd_q2;
uint64_t bp_ds;
};
struct bman_buffer bufs[8];
};
struct bman_rcr_entry {
union {
struct {
uint8_t verb;
uint8_t bpid;
uint8_t rsvd[62];
};
struct bman_buffer bufs[8];
};
};
static void bman_portal_isr(void *arg);
static union bman_mc_result *bman_mc_send(struct bman_portal_softc *p,
uint8_t verb, uint8_t cd);
DPCPU_DEFINE(struct bman_portal_softc *, bman_affine_portal);
DPAA_RING(bman_rcr, 8, BCSP_RCR_PI_CENA, BCSP_RCR_CI_CENA,
BCSP_RCR_PI_CINH, BCSP_RCR_CI_CINH);
static uint32_t
bm_ci_read(struct bman_portal_softc *sc, bus_size_t off)
{
return (bus_read_4(sc->sc_base.sc_mres[1], off));
}
static void
bm_ci_write(struct bman_portal_softc *sc, bus_size_t off, uint32_t val)
{
bus_write_4(sc->sc_base.sc_mres[1], off, val);
}
int
bman_portals_attach(device_t dev)
bman_portal_attach(device_t dev, int cpu)
{
struct dpaa_portals_softc *sc;
struct bman_portal_softc *sc = device_get_softc(dev);
sc = bp_sc = device_get_softc(dev);
/* Map bman portal to physical address space */
if (law_enable(OCP85XX_TGTIF_BMAN, sc->sc_dp_pa, sc->sc_dp_size)) {
bman_portals_detach(dev);
return (ENXIO);
}
/* Set portal properties for XX_VirtToPhys() */
XX_PortalSetInfo(dev);
sc->sc_base.sc_cpu = cpu;
dpaa_portal_alloc_res(dev, cpu);
bm_ci_write(sc, BCSP_ISDR, 0);
bm_ci_write(sc, BCSP_IER, INTR_RCRI | INTR_BSCN);
bus_setup_intr(dev, sc->sc_base.sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
NULL, bman_portal_isr, sc, &sc->sc_base.sc_intr_cookie);
bus_bind_intr(dev, sc->sc_base.sc_ires, cpu);
/* Select valid-bit mode for rings */
bus_write_4(sc->sc_base.sc_mres[1], BCSP_CFG, CFG_RPM_VBM);
/* Disable pool depletion notifications. */
bm_ci_write(sc, BCSP_SCN0, 0);
bm_ci_write(sc, BCSP_SCN1, 0);
DPCPU_ID_SET(cpu, bman_affine_portal, sc);
sc->sc_rcr.ring =
(struct bman_rcr_entry *)(sc->sc_base.sc_ce_va + BMAN_CE_RCR);
bman_rcr_ring_init(&sc->sc_rcr, &sc->sc_base);
/* Starting MC polarity is always 1 */
sc->mc.polarity = BMAN_MC_VERB_VBIT;
bus_attach_children(dev);
return (0);
}
int
bman_portals_detach(device_t dev)
bman_portal_detach(device_t dev)
{
struct dpaa_portals_softc *sc;
struct bman_portal_softc *sc;
int i;
bp_sc = NULL;
sc = device_get_softc(dev);
for (i = 0; i < ARRAY_SIZE(sc->sc_dp); i++) {
if (sc->sc_dp[i].dp_ph != NULL) {
thread_lock(curthread);
sched_bind(curthread, i);
thread_unlock(curthread);
BM_PORTAL_Free(sc->sc_dp[i].dp_ph);
/* TODO: Unmap TLB regions */
thread_lock(curthread);
sched_bind(curthread, sc->sc_base.sc_cpu);
thread_unlock(curthread);
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
}
if (sc->sc_base.sc_ires != NULL)
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_base.sc_ires);
if (sc->sc_dp[i].dp_ires != NULL) {
XX_DeallocIntr((uintptr_t)sc->sc_dp[i].dp_ires);
bus_release_resource(dev, SYS_RES_IRQ,
sc->sc_dp[i].dp_irid, sc->sc_dp[i].dp_ires);
}
}
for (i = 0; i < ARRAY_SIZE(sc->sc_rres); i++) {
if (sc->sc_rres[i] != NULL)
for (i = 0; i < nitems(sc->sc_base.sc_mres); i++) {
if (sc->sc_base.sc_mres[i] != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_rrid[i],
sc->sc_rres[i]);
i, sc->sc_base.sc_mres[i]);
}
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
return (0);
}
t_Handle
bman_portal_setup(struct bman_softc *bsc)
static uint64_t
bman_query(struct bman_portal_softc *sc, bool depletion)
{
struct dpaa_portals_softc *sc;
t_BmPortalParam bpp;
t_Handle portal;
unsigned int cpu;
uintptr_t p;
union bman_mc_result *mc_res;
uint64_t res;
/* Return NULL if we're not ready or while detach */
if (bp_sc == NULL)
return (NULL);
sc = bp_sc;
sched_pin();
portal = NULL;
cpu = PCPU_GET(cpuid);
/* Check if portal is ready */
while (atomic_cmpset_acq_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph,
0, -1) == 0) {
p = atomic_load_acq_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph);
/* Return if portal is already initialized */
if (p != 0 && p != -1) {
sched_unpin();
return ((t_Handle)p);
}
/* Not inititialized and "owned" by another thread */
sched_relinquish(curthread);
}
/* Map portal registers */
dpaa_portal_map_registers(sc);
/* Configure and initialize portal */
bpp.ceBaseAddress = rman_get_bushandle(sc->sc_rres[0]);
bpp.ciBaseAddress = rman_get_bushandle(sc->sc_rres[1]);
bpp.h_Bm = bsc->sc_bh;
bpp.swPortalId = cpu;
bpp.irq = (uintptr_t)sc->sc_dp[cpu].dp_ires;
portal = BM_PORTAL_Config(&bpp);
if (portal == NULL)
critical_enter();
mc_res = bman_mc_send(sc, BMAN_MC_VERB_QUERY, 0);
if (mc_res == NULL)
goto err;
if (BM_PORTAL_Init(portal) != E_OK)
goto err;
if (depletion)
res = mc_res->bp_ds;
else
res = mc_res->bp_as;
critical_exit();
atomic_store_rel_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph, (uintptr_t)portal);
sched_unpin();
return (portal);
return (res);
err:
if (portal != NULL)
BM_PORTAL_Free(portal);
atomic_store_rel_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph, 0);
sched_unpin();
return (NULL);
critical_exit();
device_printf(sc->sc_base.sc_dev, "Timeout querying depltetion\n");
return (0);
}
static void
bman_portal_isr(void *arg)
{
struct bman_portal_softc *sc = arg;
uint32_t intrs;
intrs = bm_ci_read(sc, BCSP_ISR);
/* Release Command Ring interrupt. */
if (intrs & INTR_RCRI) {
bman_rcr_update(&sc->sc_rcr, &sc->sc_base);
}
/* Buffer Pool State Change Notification. */
if (intrs & INTR_BSCN) {
struct bman_pool *pool;
uint64_t res = bman_query(sc, true);
if (__predict_true(res != 0)) {
int idx = flsll(res);
pool = sc->sc_pools[64 - idx];
KASSERT(pool != NULL,
("state change on unassociated bpid %d\n", idx));
pool->dep_cb(pool->arg, true);
}
}
bm_ci_write(sc, BCSP_ISR, intrs);
}
/* RCR */
int
bman_release(struct bman_pool *pool, const struct bman_buffer *bufs,
uint8_t count)
{
struct bman_portal_softc *portal;
struct bman_rcr_entry *rcr;
if (count > 8)
return (EINVAL);
critical_enter();
portal = DPCPU_GET(bman_affine_portal);
rcr = bman_rcr_start(&portal->sc_rcr, &portal->sc_base);
bzero(rcr, sizeof(*rcr));
/* This should be safe, because bpid must be less than 256. */
for (int i = 0; i < count; i++)
rcr->bufs[i] = bufs[i];
rcr->bufs[0].bpid = pool->bpid;
bman_rcr_commit(&portal->sc_rcr, BMAN_RCR_VERB_BPID0 | count);
critical_exit();
return (0);
}
/* MC commands */
/* Assumes pinned */
static union bman_mc_result *
bman_mc_send(struct bman_portal_softc *p, uint8_t verb, uint8_t cd)
{
int res_idx;
struct bman_mc_command *command;
union bman_mc_result *rr;
uintptr_t ce_va = p->sc_base.sc_ce_va;
command = (struct bman_mc_command *)(ce_va + BMAN_CE_CR);
dpaa_zero_line(command);
command->cd = cd;
dpaa_lw_barrier();
command->verb = verb | p->mc.polarity;
res_idx = (p->mc.polarity ? 1 : 0);
p->mc.polarity ^= BMAN_MC_VERB_VBIT;
dpaa_flush_line(command);
rr = (union bman_mc_result *)(ce_va + BMAN_CE_RR(res_idx));
for (;;) {
if (rr->verb != 0)
break;
dpaa_flush_line(rr);
}
return (rr);
}
int
bman_acquire(struct bman_pool *pool, struct bman_buffer *bufs, uint8_t count)
{
union bman_mc_result *rr;
if (count > 8 || count == 0)
return (EINVAL);
critical_enter();
rr = bman_mc_send(DPCPU_GET(bman_affine_portal),
BMAN_MC_VERB_ACQUIRE | count,
pool->bpid);
critical_exit();
if (rr == NULL)
return (ETIMEDOUT);
if ((rr->verb & ~BMAN_MC_VERB_VBIT) == 0)
return (ENOMEM);
memcpy(bufs, rr, count * sizeof(*bufs));
return (0);
}
/*
* Enable pool state change notifications on this portal. This requires the
* pool to already be configured with the callback to handle state changes.
*/
void
bman_portal_enable_scn(struct bman_portal_softc *sc, struct bman_pool *pool)
{
uint32_t reg, reg_ptr;
if (pool->bpid >= 32)
reg_ptr = BCSP_SCN1;
else
reg_ptr = BCSP_SCN0;
reg = bm_ci_read(sc, reg_ptr);
reg |= (1 << (31 - pool->bpid));
bm_ci_write(sc, reg_ptr, reg);
sc->sc_pools[pool->bpid] = pool;
}
+44
View File
@@ -0,0 +1,44 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#ifndef BMAN_VAR_H
#define BMAN_VAR_H
#include "dpaa_common.h"
#include "portals.h"
#define BMAN_MAX_POOLS 64
#define BMAN_MAX_POOLS_1023 8
DPAA_RING_DECLARE(bman_rcr);
struct bman_mc {
uint8_t polarity;
bool busy;
};
struct bman_portal_softc {
struct dpaa_portal_softc sc_base;
struct bman_mc mc;
struct bman_rcr_ring sc_rcr;
struct bman_pool *sc_pools[BMAN_MAX_POOLS];
};
struct bman_pool {
uint32_t bpid;
bm_depletion_handler dep_cb;
void *arg;
};
DPCPU_DECLARE(struct bman_portal_softc *, bman_affine_portal);
int bman_release(struct bman_pool *pool, const struct bman_buffer *bufs,
uint8_t count);
void bman_portal_enable_scn(struct bman_portal_softc *, struct bman_pool *);
#endif
+82
View File
@@ -0,0 +1,82 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <dev/fdt/fdt_common.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "dpaa_common.h"
#define FDT_REG_CELLS 4
int
dpaa_map_private_memory(device_t dev, int idx, const char *compat,
vm_paddr_t *addrp, size_t *sizep)
{
phandle_t node;
pcell_t cells[idx + 1];
pcell_t *cell_alloc;
int addr_cells, size_cells;
uint64_t tmp;
u_long align, base, size;
vm_paddr_t alloc_base;
vm_size_t alloc_range_size;
ssize_t alloc_size;
void *reserved;
int rv;
node = ofw_bus_get_node(dev);
if (OF_getencprop(node, "memory-region", cells, sizeof(cells)) <= 0)
return (ENXIO);
node = OF_node_from_xref(cells[idx]);
/* If the memory is already reserved, we just need to return it. */
if (fdt_regsize(node, &base, &size) == 0)
goto success;
rv = fdt_addrsize_cells(OF_parent(node), &addr_cells, &size_cells);
if (rv != 0)
return (rv);
if (OF_getprop(node, "alignment", &tmp, sizeof(tmp)) <= 0)
return (ENXIO);
align = fdt_data_get(&tmp, addr_cells);
if (OF_getprop(node, "size", &tmp, sizeof(tmp)) <= 0)
return (ENXIO);
size = fdt_data_get(&tmp, size_cells);
alloc_size =
OF_getencprop_alloc(node, "alloc-ranges", (void **)&cell_alloc);
if (alloc_size < 0)
return (ENXIO);
alloc_size /= sizeof(pcell_t);
for (int i = 0; i < alloc_size; i += (addr_cells + size_cells)) {
alloc_base = fdt_data_get(&cell_alloc[i], addr_cells);
alloc_range_size =
fdt_data_get(&cell_alloc[i + addr_cells], size_cells);
reserved = contigmalloc(size, M_DEVBUF, M_NOWAIT | M_ZERO,
alloc_base, alloc_base + alloc_range_size, align, 0);
if (reserved != NULL)
break;
}
if (reserved == NULL)
return (ENOMEM);
/* Flush the cache (zeroed memory) because it won't be touched later. */
cpu_flush_dcache(reserved, size);
base = pmap_kextract((vm_offset_t)reserved);
success:
*addrp = base;
*sizep = size;
return (0);
}
+165
View File
@@ -0,0 +1,165 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#ifndef DPAA_COMMON_H
#define DPAA_COMMON_H
#include <machine/atomic.h>
int dpaa_map_private_memory(device_t dev, int idx, const char *compat,
vm_paddr_t *addrp, size_t *sizep);
struct dpaa_fd {
uint64_t liodn:8;
uint64_t bpid:8;
uint64_t eliodn:4;
uint64_t _rsvd1:4;
uint64_t addr:40;
uint32_t format:3;
uint32_t offset:9;
uint32_t length:20;
uint32_t cmd_stat;
} __packed;
#define DPAA_FD_FORMAT_SHORT_MBSF 4
#define DPAA_FD_GET_ADDR(fd) ((void *)PHYS_TO_DMAP(fd->addr))
struct dpaa_sgte {
uint64_t addr;
uint32_t extension:1;
uint32_t final:1;
uint32_t length:30;
uint16_t bpid;
uint16_t offset;
} __packed;
struct qman_fqr;
#define DPAA_NUM_OF_SG_TABLE_ENTRY 16
/*
* Ring API infrastructure
*
* BMan and QMan both use cache-enabled rings. Abstract this away to a more
* generalized interface to reduce code copying.
*
* Requirements:
* - Before calling <ring>_init() the ring base (ring->ring) must be initialized
* to the base of the ring.
*/
#define DPAA_RING_DECLARE(pfx) \
struct pfx##_ring { \
struct pfx##_entry *ring; \
struct pfx##_entry *cursor; \
uint8_t vbit; \
uint8_t avail; \
uint8_t ci; \
uint8_t ithresh; \
}
/*
* Ring functions:
*
* ring_cyc_diff() -- get the (circular) difference of `l - f`
* ring_ring_init() -- Set up the ring structures. Portal must be
* initialized beforehand, and ring->ring must be nonzero.
* ring_CARRYCLEAR() -- stealth math to do circular roll-over
* ring_INC() -- Increment the cursor within the ring
* ring_update() -- Update ring entry availability count
* ring_start() -- Reserve the next entry in the ring if available.
* ring_commit() -- Commit the reserved ring entry by setting the verb and
* AVB bit
*/
#define DPAA_RING(pfx,sz,pi_e,ci_e,pi_i,ci_i) \
static inline int \
pfx##_cyc_diff(uint8_t size, uint8_t f, uint8_t l) \
{ \
if (f <= l) \
return (uint8_t)(l - f); \
return (uint8_t)(l + size - f); \
} \
static inline void \
pfx##_ring_init(struct pfx##_ring *ring, struct dpaa_portal_softc *portal)\
{ \
uint32_t pi = *(uint32_t*)(portal->sc_ci_va + pi_i) & (sz - 1); \
uint32_t ci = *(uint32_t*)(portal->sc_ci_va + ci_i); \
ring->ci = ci & (sz - 1); \
ring->vbit = !!(ci & sz) << 7; \
ring->cursor = ring->ring + pi; \
ring->avail = sz - 1 - pfx##_cyc_diff(sz, ring->ci, pi); \
} \
static inline void * \
pfx##_CARRYCLEAR(struct pfx##_entry *p) \
{ \
return ((void *)((uintptr_t)p & (~(uintptr_t)(sz << 6)))); \
} \
static inline void \
pfx##_INC(struct pfx##_ring *ring) \
{ \
struct pfx##_entry *partial = ring->cursor + 1; \
ring->cursor = pfx##_CARRYCLEAR(partial); \
if (partial != ring->cursor) \
ring->vbit ^= 0x80; \
} \
static inline uint8_t \
pfx##_update(struct pfx##_ring *ring, struct dpaa_portal_softc *portal) \
{ \
uint8_t diff, old_ci = ring->ci; \
ring->ci = *(uint32_t*)(portal->sc_ci_va + ci_i) & (sz - 1); \
diff = pfx##_cyc_diff(sz, old_ci, ring->ci); \
ring->avail += diff; \
return (diff); \
} \
static inline struct pfx##_entry * __unused \
pfx##_start(struct pfx##_ring *ring, struct dpaa_portal_softc *portal) \
{ \
if (ring->avail <= 1) { \
pfx##_update(ring, portal); \
if (ring->avail == 0) \
return (NULL); \
} \
dpaa_zero_line(ring->cursor); \
return (ring->cursor); \
} \
static inline void __unused \
pfx##_commit(struct pfx##_ring *ring, uint8_t verb) \
{ \
struct pfx##_entry *entry = ring->cursor; \
dpaa_lw_barrier(); \
entry->verb = verb | ring->vbit; \
dpaa_flush_line(entry); \
pfx##_INC(ring); \
ring->avail--; \
} struct hack
#ifdef __powerpc__
static inline void
dpaa_flush_line(void *line)
{
__asm __volatile ("dcbf 0, %0" :: "r"(line) : "memory");
}
static inline void
dpaa_zero_line(void *line)
{
__asm __volatile ("dcbz 0, %0" :: "r"(line) : "memory");
}
static inline void
dpaa_touch_line(void *line)
{
__asm __volatile ("dcbt 0, %0" :: "r"(line) : "memory");
}
static inline void
dpaa_lw_barrier(void)
{
powerpc_lwsync();
}
#endif
#endif
+634
View File
@@ -0,0 +1,634 @@
/*-
* 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 <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_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 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);
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_fq_rx_callback(device_t portal, struct qman_fq *fq,
struct qman_fd *frame, void *app)
{
struct dpaa_eth_softc *sc;
struct mbuf *m;
void *frame_va;
m = NULL;
sc = app;
frame_va = DPAA_FD_GET_ADDR(frame);
KASSERT(frame->format == 0,
("%s(): Got unsupported frame format 0x%02X!", __func__,
frame->format));
KASSERT(frame->offset == 0,
("%s(): Only offset 0 is supported!", __func__));
if (frame->cmd_stat != 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;
m_extadd(m, frame_va, MCLBYTES, dpaa_eth_fq_mext_free, frame_va, sc, 0,
EXT_NET_DRV);
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 != 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);
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);
}
/** @} */
/**
* @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.
*/
memset(&fi->fi_sgt[i], 0, sizeof(fi->fi_sgt[i]));
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_sgt);
fd.length = psize;
fd.format = DPAA_FD_FORMAT_SHORT_MBSF;
fd.liodn = 0;
fd.bpid = 0;
fd.eliodn = 0;
fd.offset = 0;
fd.cmd_stat = 0;
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);
}
}
/** @} */
+116
View File
@@ -0,0 +1,116 @@
/*-
* 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.
*/
#ifndef DPAA_ETH_H_
#define DPAA_ETH_H_
struct dpaa_eth_softc {
/* XXX MII bus requires that struct ifnet is first!!! */
if_t sc_ifnet;
device_t sc_dev;
struct resource *sc_mem;
struct mtx sc_lock;
int sc_mac_enet_mode;
/* RX Pool */
struct bman_pool *sc_rx_pool;
uint8_t sc_rx_bpid;
uma_zone_t sc_rx_zone;
char sc_rx_zname[64];
/* RX Frame Queue */
struct qman_fq *sc_rx_fq;
uint32_t sc_rx_fqid;
/* TX Frame Queue */
struct qman_fq *sc_tx_fq;
bool sc_tx_fq_full;
struct qman_fq *sc_tx_conf_fq;
uint32_t sc_tx_conf_fqid;
/* Methods */
int (*sc_port_rx_init)
(struct dpaa_eth_softc *sc, int unit);
int (*sc_port_tx_init)
(struct dpaa_eth_softc *sc, int unit);
void (*sc_start_locked)
(struct dpaa_eth_softc *sc);
/* dTSEC data */
uint8_t sc_eth_id; /* Ethernet ID within its frame manager */
uintptr_t sc_mac_mem_offset;
int sc_mac_mdio_irq;
uint8_t sc_mac_addr[6];
int sc_port_rx_hw_id;
int sc_port_tx_hw_id;
uint32_t sc_port_tx_qman_chan;
int sc_phy_addr;
bool sc_hidden;
device_t sc_mdio;
int sc_rev_major;
int sc_rev_minor;
device_t sc_rx_port;
device_t sc_tx_port;
int sc_rx_channel;
/* MII data */
struct mii_data *sc_mii;
device_t sc_mii_dev;
struct mtx sc_mii_lock;
struct callout sc_tick_callout;
/* Frame Info Zone */
uma_zone_t sc_fi_zone;
char sc_fi_zname[64];
};
/**
* @group dTSEC Regular Mode API.
* @{
*/
int dpaa_eth_fm_port_rx_init(struct dpaa_eth_softc *sc);
int dpaa_eth_fm_port_tx_init(struct dpaa_eth_softc *sc);
void dpaa_eth_if_start_locked(struct dpaa_eth_softc *sc);
int dpaa_eth_pool_rx_init(struct dpaa_eth_softc *sc);
void dpaa_eth_pool_rx_free(struct dpaa_eth_softc *sc);
int dpaa_eth_fi_pool_init(struct dpaa_eth_softc *sc);
void dpaa_eth_fi_pool_free(struct dpaa_eth_softc *sc);
int dpaa_eth_fq_rx_init(struct dpaa_eth_softc *sc);
int dpaa_eth_fq_tx_init(struct dpaa_eth_softc *sc);
void dpaa_eth_fq_rx_free(struct dpaa_eth_softc *sc);
void dpaa_eth_fq_tx_free(struct dpaa_eth_softc *sc);
/** @} */
#endif /* DPAA_ETH_H_ */
+642 -238
View File
File diff suppressed because it is too large Load Diff
+67 -6
View File
@@ -28,6 +28,23 @@
#define FMAN_H_
#include <dev/fdt/simplebus.h>
#include <sys/vmem.h>
#define FMAN_BMI_FIFO_UNITS 0x100
#define FMAN_BMI_FIFO_ALIGN 0x100
#define FM_FD_ERR_DMA 0x01000000
#define FM_FD_ERR_FPE 0x00080000
#define FM_FD_ERR_FSE 0x00040000
#define FM_FD_ERR_DIS 0x00020000
#define FM_FD_ERR_EOF 0x00008000
#define FM_FD_ERR_NSS 0x00004000
#define FM_FD_ERR_KSO 0x00002000
#define FM_FD_ERR_IPP 0x00000200
#define FM_FD_ERR_PTE 0x00000080
#define FM_FD_ERR_ISP 0x00000040
#define FM_FD_ERR_PHE 0x00000020
#define FM_FD_ERR_BLE 0x00000008
/**
* FMan driver instance data.
@@ -38,22 +55,61 @@ struct fman_softc {
struct resource *irq_res;
struct resource *err_irq_res;
struct rman rman;
vmem_t *muram_vmem;
int mem_rid;
int irq_rid;
int err_irq_rid;
void *irq_cookie;
int qman_chan_base;
int qman_chan_count;
int fm_id;
t_Handle fm_handle;
t_Handle muram_handle;
int sc_revision_major;
int sc_revision_minor;
uint16_t clock;
bool timestamps;
uint32_t iram_size;
uint32_t dma_thresh_max_commq;
uint32_t dma_thresh_max_buf;
uint32_t dma_cam_num_entries;
uint32_t max_open_dmas;
uint32_t qmi_max_tnums;
uint32_t qmi_def_tnums_thresh;
uint32_t bmi_max_tasks;
uint32_t bmi_max_fifo_size;
uint32_t bmi_fifo_base;
uint32_t port_cgs;
uint32_t rx_ports;
uint32_t total_fifo_size;
uint32_t qman_channel_base;
uint32_t qman_channels;
};
struct fman_port_init_params {
int port_id;
bool is_rx_port;
uint8_t num_tasks;
uint8_t extra_tasks;
uint8_t open_dmas;
uint8_t extra_dmas;
uint32_t fifo_size;
uint32_t extra_fifo_size;
uint8_t deq_pipeline_size;
uint16_t max_frame_length;
uint16_t liodn;
};
/**
* @group QMan bus interface.
* @group FMan bus interface.
* @{
*/
struct resource * fman_alloc_resource(device_t bus, device_t child, int type,
struct resource *fman_alloc_resource(device_t bus, device_t child, int type,
int rid, rman_res_t start, rman_res_t end, rman_res_t count, u_int flags);
int fman_activate_resource(device_t bus, device_t child,
struct resource *res);
@@ -66,11 +122,16 @@ int fman_shutdown(device_t dev);
int fman_read_ivar(device_t dev, device_t child, int index,
uintptr_t *result);
int fman_qman_channel_id(device_t, int);
void fman_get_revision(device_t, int *, int *);
/** @} */
uint32_t fman_get_clock(struct fman_softc *sc);
int fman_get_handle(device_t dev, t_Handle *fmh);
int fman_get_muram_handle(device_t dev, t_Handle *muramh);
int fman_get_bushandle(device_t dev, vm_offset_t *fm_base);
size_t fman_get_bmi_max_fifo_size(device_t);
int fman_reset_mac(device_t, int);
int fman_set_port_params(device_t dev, struct fman_port_init_params *params);
int fman_qman_channel_id(device_t, int);
int fman_set_mac_intr_handler(device_t, int, driver_intr_t, void *);
int fman_set_mac_err_handler(device_t, int, driver_intr_t, void *);
#endif /* FMAN_H_ */
+9 -5
View File
@@ -34,10 +34,8 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <contrib/ncsw/inc/ncsw_ext.h>
#include <contrib/ncsw/inc/enet_ext.h>
#include "fman.h"
#include "fman_if.h"
#define FFMAN_DEVSTR "Freescale Frame Manager"
@@ -56,6 +54,12 @@ static device_method_t fman_methods[] = {
DEVMETHOD(bus_alloc_resource, fman_alloc_resource),
DEVMETHOD(bus_activate_resource, fman_activate_resource),
DEVMETHOD(bus_release_resource, fman_release_resource),
DEVMETHOD(fman_get_revision, fman_get_revision),
DEVMETHOD(fman_reset_mac, fman_reset_mac),
DEVMETHOD(fman_set_port_params, fman_set_port_params),
DEVMETHOD(fman_get_qman_channel_id, fman_qman_channel_id),
DEVMETHOD_END
};
@@ -92,8 +96,8 @@ fman_get_clock(struct fman_softc *sc)
if ((OF_getprop(node, "clock-frequency", &fman_clock,
sizeof(fman_clock)) <= 0) || (fman_clock == 0)) {
device_printf(dev, "could not acquire correct frequency "
"from DTS\n");
device_printf(dev,
"could not acquire correct frequency from DTS\n");
return (0);
}
+38
View File
@@ -0,0 +1,38 @@
#-
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2026 Justin Hibbits
#include <machine/bus.h>
#include <dev/dpaa/fman.h>
/**
* @brief DPAA FMan interface
*
*/
INTERFACE fman;
METHOD void get_revision {
device_t dev;
int *major;
int *minor;
};
METHOD size_t get_bmi_max_fifo_size {
device_t dev;
};
METHOD int get_qman_channel_id {
device_t dev;
int port_id;
};
METHOD int reset_mac {
device_t dev;
int mac_id;
};
METHOD int set_port_params {
device_t dev;
struct fman_port_init_params *params;
};
+5 -11
View File
@@ -46,17 +46,15 @@
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <contrib/ncsw/inc/Peripherals/fm_ext.h>
#include "fman.h"
#include "miibus_if.h"
#define MDIO_LOCK() mtx_lock(&sc->sc_lock)
#define MDIO_UNLOCK() mtx_unlock(&sc->sc_lock)
#define MDIO_WRITE4(sc,r,v) \
bus_space_write_4(&bs_be_tag, sc->sc_handle, sc->sc_offset + r, v)
#define MDIO_WRITE4(sc, r, v) \
bus_write_4(sc->sc_res, r, v)
#define MDIO_READ4(sc, r) \
bus_space_read_4(&bs_be_tag, sc->sc_handle, sc->sc_offset + r)
bus_read_4(sc->sc_res, r)
#define MDIO_MIIMCFG 0x0
#define MDIO_MIIMCOM 0x4
@@ -76,8 +74,7 @@ static int pqmdio_miibus_writereg(device_t dev, int phy, int reg, int value);
struct pqmdio_softc {
struct mtx sc_lock;
bus_space_handle_t sc_handle;
int sc_offset;
struct resource *sc_res;
};
static device_method_t pqmdio_methods[] = {
@@ -123,13 +120,10 @@ static int
pqmdio_fdt_attach(device_t dev)
{
struct pqmdio_softc *sc;
rman_res_t start, count;
sc = device_get_softc(dev);
fman_get_bushandle(device_get_parent(dev), &sc->sc_handle);
bus_get_resource(dev, SYS_RES_MEMORY, 0, &start, &count);
sc->sc_offset = start;
sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0, RF_ACTIVE);
OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
+70
View File
@@ -0,0 +1,70 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#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 <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <machine/bus.h>
#include "opt_platform.h"
#include <powerpc/mpc85xx/mpc85xx.h>
#include "fman.h"
struct fman_muram_softc {
struct resource *sc_mem;
vmem_t sc_vmem;
};
static int
fman_muram_probe(device_t dev)
{
if (!ofw_bus_is_compatible(dev, "fsl,fman-muram"))
return (ENXIO);
device_set_desc(dev, "FMan MURAM");
return (BUS_PROBE_DEFAULT);
}
static int
fman_muram_attach(device_t dev)
{
struct fman_muram_softc *sc = device_get_softc(dev);
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0,
RF_ACTIVE | RF_SHAREABLE);
if (sc->sc_mem == NULL) {
device_printf(dev, "cannot allocate memory\n");
return (ENXIO);
}
sc->sc_vmem = vmem_create("MURAM", rman_get_bushandle(sc->sc_mem),
rman_get_size(sc->sc_mem),
}
static device_method_t muram_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, fman_muram_probe),
DEVMETHOD(device_attach, fman_muram_attach),
DEVMETHOD(device_detach, fman_muram_detach),
DEVMETHOD_END
};
DEFINE_CLASS_0(fman_muram, fman_muram_driver, muram_methods,
sizeof(struct fman_muram_softc));
EARLY_DRIVER_MODULE(fman_muram, fman, fman_muram_driver, 0, 0,
BUS_PASS_SUPPORTDEV);
+680
View File
@@ -0,0 +1,680 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <machine/bus.h>
#include "fman.h"
#include "fman_port.h"
#include "fman_if.h"
#include "fman_port_if.h"
struct fman_port_rsrc {
uint32_t num;
uint32_t extra;
};
#define MAX_BM_POOLS 64
struct fman_port_softc {
device_t sc_dev;
struct resource *sc_mem;
int sc_port_id;
int sc_port_speed;
int sc_port_type;
int sc_revision_major;
int sc_revision_minor;
int sc_max_frame_length;
int sc_bm_max_pools;
int sc_max_port_fifo_size;
int sc_qman_channel_id;
int sc_deq_byte_count;
int sc_deq_high_priority;
int sc_tx_deq_pipeline_depth;
int sc_default_fqid;
int sc_err_fqid;
int pcd_base_fqid;
int pcd_fqs_count;
int sc_max_ext_portals;
int sc_max_sub_portals;
struct fman_port_rsrc sc_open_dmas;
struct fman_port_rsrc sc_tasks;
struct fman_port_rsrc sc_fifo_bufs;
struct fman_port_buffer_pool sc_bpools[FMAN_PORT_MAX_POOLS];
};
#define TX_10G_PORT_BASE 0x30
#define RX_10G_PORT_BASE 0x10
#define FMAN_PORT_TYPE_TX 0
#define FMAN_PORT_TYPE_RX 1
#define PORT_RX 0x01
#define PORT_TX 0x02
#define PORT_V3 0x04
#define FMBM_RCFG 0x000
#define BMI_PORT_CFG_EN 0x80000000
#define FMBM_RST 0x004
#define FMBM_RDA 0x008
#define RDA_WOPT 0x00100000
#define FMBM_RFP 0x00c
#define FMBM_RFED 0x010
#define BMI_RX_FRAME_END_CUT_SHIFT 16
#define FMBM_RICP 0x014
#define FMBM_RIM 0x018
#define FMBM_REBM 0x01c
#define FMBM_RFNE 0x020
#define FMBM_RFCA 0x024
#define RFCA_OR 0x80000000
#define RFCA_COLOR 0x0c000000
#define RFCA_SYNC 0x03000000
#define RFCA_SYNC_REQ 0x02000000
#define RFCA_MR 0x003f0000
#define RFCA_MR_DEF 0x003c0000
#define FMBM_RFPNE 0x028
#define FMBM_RETH 0x038
#define RETH_ETHE 0x80000000 /* Excessive Threshold Enable */
#define FMBM_RFQID 0x060
#define FMBM_REFQID 0x064
#define FMBM_RFSDM 0x068
#define FMBM_RFSEM 0x06c
#define FMBM_RFENE 0x070
#define FMBM_REBMPI(i) (0x100 + (4 * (i)))
#define REBMPI_VAL 0x80000000
#define REBMPI_ACE 0x40000000
#define REBMPI_BPID_S 16
#define FMBM_RSTC 0x0200
#define RSTC_EN 0x80000000
#define FMBM_TCFG 0x000
#define FMBM_TST 0x004
#define FMBM_TDA 0x008
#define FMBM_TFP 0x00c
#define BMI_FIFO_PIPELINE_DEPTH_SHIFT 12
#define FMBM_TFED 0x010
#define FMBM_TFDNE 0x018
#define FMBM_TFCA 0x01c
#define TFCA_MR_DEF 0
#define TFCA_ATTR_ORDER 0x80000000
#define FMBM_TCFQID 0x020
#define FMBM_TEFQID 0x024
#define FMBM_TFENE 0x028
#define FMBM_TFNE 0x070
#define TFNE_EBD 0x80000000
#define FMQM_PNC 0x400
#define PNC_EN 0x80000000
#define PNC_STEN 0x80000000
#define FMQM_PNS 0x404
#define PNS_DEQ_FD_BSY 0x20000000
#define FMQM_PNEN 0x41c
#define FMQM_PNDN 0x42c
#define FMQM_PNDC 0x430
#define QMI_DEQ_CFG_PRI 0x80000000
#define QMI_DEQ_CFG_TYPE1 0x10000000
#define QMI_DEQ_CFG_TYPE2 0x20000000
#define QMI_DEQ_CFG_TYPE3 0x30000000
#define QMI_DEQ_CFG_PREFETCH_PARTIAL 0x01000000
#define QMI_DEQ_CFG_PREFETCH_FULL 0x03000000
#define QMI_DEQ_CFG_SP_MASK 0xf
#define QMI_DEQ_CFG_SP_SHIFT 20
#define HWP_PCAC 0xbf8
#define HWP_PCAC_PSTOP 0x00000001
#define HWP_HXS_PCAC_PSTAT 0x00000100
#define HWP_HXS_SSA(x) (0x800 + x * 2 * sizeof(uint32_t))
#define HWP_HXS_LCV(x) (0x800 + (x * 2 + 1) * sizeof(uint32_t))
#define HWP_HXS_TCP 0xA
#define HWP_HXS_UDP 0xB
#define HXS_SH_PAD_REM 0x80000000
#define HWP_HXS_COUNT 16
#define PORT_MAX_FRAME_LENGTH 9600
#define NIA_ORDER_RESTORE 0x00800000
#define NIA_ENG_BMI 0x00500000
#define NIA_ENG_QMI_DEQ 0x00580000
#define NIA_ENG_QMI_ENQ 0x00540000
#define NIA_ENG_HWP 0x00440000
#define NIA_ENG_HWK 0x00480000
#define NIA_BMI_AC_TX_RELEASE 0x000002c0
#define NIA_BMI_AC_TX 0x00000274
#define NIA_BMI_AC_RELEASE 0x000000c0
#define NIA_BMI_AC_ENQ_FRAME 0x00000002
#define NIA_BMI_AC_FETCH_ALLFRAME 0x0000020c
#define BMI_RX_ERR (FM_FD_ERR_DMA | FM_FD_ERR_FPE | \
FM_FD_ERR_FSE | FM_FD_ERR_DIS | \
FM_FD_ERR_EOF | FM_FD_ERR_NSS | \
FM_FD_ERR_KSO | FM_FD_ERR_IPP | \
FM_FD_ERR_PTE | FM_FD_ERR_PHE | \
FM_FD_ERR_BLE)
/* Default configurations */
#define DEFAULT_RX_CUT_END_BYTES 4
static struct ofw_compat_data compats[] = {
{ "fsl,fman-v2-port-rx", PORT_RX },
{ "fsl,fman-v2-port-tx", PORT_TX },
{ "fsl,fman-v3-port-rx", PORT_V3 | PORT_RX },
{ "fsl,fman-v3-port-tx", PORT_V3 | PORT_TX },
{ NULL, 0 }
};
static int
fman_port_probe(device_t dev)
{
if (ofw_bus_search_compatible(dev, compats)->ocd_str == NULL)
return (ENXIO);
device_set_desc(dev, "FMan port");
return (BUS_PROBE_DEFAULT);
}
static int
fman_port_attach(device_t dev)
{
struct fman_port_softc *sc;
phandle_t node;
pcell_t cell;
uintptr_t compat_data =
ofw_bus_search_compatible(dev, compats)->ocd_data;
int port_speed = 1000;
int port_type;
sc = device_get_softc(dev);
sc->sc_dev = dev;
node = ofw_bus_get_node(dev);
if (OF_getencprop(node, "cell-index", &cell, sizeof(cell)) < 0) {
device_printf(dev, "No cell-index property");
return (ENXIO);
}
sc->sc_port_id = cell;
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0,
RF_ACTIVE | RF_SHAREABLE);
if (sc->sc_mem == NULL) {
device_printf(dev, "failed to allocate MMIO");
return (ENXIO);
}
FMAN_GET_REVISION(device_get_parent(dev),
&sc->sc_revision_major, &sc->sc_revision_minor);
if ((compat_data & PORT_TX) == PORT_TX)
port_type = FMAN_PORT_TYPE_TX;
else
port_type = FMAN_PORT_TYPE_RX;
if ((compat_data & PORT_V3) == PORT_V3) {
if (OF_hasprop(node, "fsl,fman-10g-port"))
port_speed = 10000;
} else {
if ((compat_data & PORT_TX) &&
sc->sc_port_id > TX_10G_PORT_BASE)
port_speed = 10000;
else if ((compat_data & PORT_RX) &&
sc->sc_port_id > RX_10G_PORT_BASE)
port_speed = 10000;
}
if (sc->sc_port_speed == 10000) {
sc->sc_deq_high_priority = true;
sc->sc_deq_byte_count = 0x1400;
} else {
sc->sc_deq_high_priority = false;
sc->sc_deq_byte_count = 0x0400;
}
sc->sc_port_type = port_type;
sc->sc_port_speed = port_speed;
sc->sc_bm_max_pools = MAX_BM_POOLS;
sc->sc_max_frame_length = PORT_MAX_FRAME_LENGTH;
if (port_type == FMAN_PORT_TYPE_TX)
sc->sc_qman_channel_id =
FMAN_GET_QMAN_CHANNEL_ID(device_get_parent(dev),
sc->sc_port_id);
OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
return (0);
}
static int
fman_port_detach(device_t dev)
{
struct fman_port_softc *sc = device_get_softc(dev);
if (sc->sc_mem != NULL)
bus_release_resource(dev, sc->sc_mem);
return (0);
}
static int
fman_port_config(device_t dev, struct fman_port_params *params)
{
struct fman_port_softc *sc = device_get_softc(dev);
sc->sc_default_fqid = params->dflt_fqid;
sc->sc_err_fqid = params->err_fqid;
sc->sc_max_port_fifo_size =
FMAN_GET_BMI_MAX_FIFO_SIZE(device_get_parent(dev));
switch (sc->sc_revision_major) {
case 2:
case 3:
sc->sc_max_ext_portals = 4;
sc->sc_max_sub_portals = 12;
break;
case 6:
sc->sc_max_ext_portals = 8;
sc->sc_max_sub_portals = 16;
break;
}
if (sc->sc_revision_major >= 6 &&
sc->sc_port_type == FMAN_PORT_TYPE_TX && sc->sc_port_speed == 1000)
/* Errata A005127 workaround */
bus_write_4(sc->sc_mem, FMBM_TFP, 0x00001013);
sc->sc_tasks.extra = 0;
switch (sc->sc_port_speed) {
case 10000:
if (sc->sc_revision_major < 6) {
sc->sc_tasks.num = 16;
if (sc->sc_port_type == FMAN_PORT_TYPE_RX)
sc->sc_tasks.extra = 8;
} else
sc->sc_tasks.num = 14;
break;
case 1000:
if (sc->sc_revision_major >= 6)
sc->sc_tasks.num = 4;
else {
sc->sc_tasks.num = 3;
if (sc->sc_port_type == FMAN_PORT_TYPE_RX)
sc->sc_tasks.extra = 2;
}
break;
default:
sc->sc_tasks.num = 0;
break;
}
/* Open DMAs */
if (sc->sc_revision_major >= 6) {
sc->sc_open_dmas.extra = 0;
if (sc->sc_port_speed == 10000) {
if (sc->sc_port_type == FMAN_PORT_TYPE_TX)
sc->sc_open_dmas.num = 12;
else
sc->sc_open_dmas.num = 8;
} else {
if (sc->sc_port_type == FMAN_PORT_TYPE_TX)
sc->sc_open_dmas.num = 3;
else
sc->sc_open_dmas.num = 2;
}
} else {
if (sc->sc_port_speed == 10000) {
sc->sc_open_dmas.num = 8;
sc->sc_open_dmas.num = 8;
} else {
sc->sc_open_dmas.num = 1;
sc->sc_open_dmas.extra = 1;
}
}
/* FIFO bufs */
if (sc->sc_revision_major >= 6) {
if (sc->sc_port_type == FMAN_PORT_TYPE_TX)
if (sc->sc_port_speed == 10000)
sc->sc_fifo_bufs.num = 64;
else
sc->sc_fifo_bufs.num = 50;
else
if (sc->sc_port_speed == 10000)
sc->sc_fifo_bufs.num = 96;
else
sc->sc_fifo_bufs.num = 50;
} else {
if (sc->sc_port_type == FMAN_PORT_TYPE_TX)
if (sc->sc_port_speed == 10000)
sc->sc_fifo_bufs.num = 48;
else
sc->sc_fifo_bufs.num = 44;
else
if (sc->sc_port_speed == 10000)
sc->sc_fifo_bufs.num = 48;
else
sc->sc_fifo_bufs.num = 45;
}
sc->sc_fifo_bufs.extra = 0;
sc->sc_fifo_bufs.num *= FMAN_BMI_FIFO_UNITS;
if (sc->sc_port_type == FMAN_PORT_TYPE_RX)
for (int i = 0; i < params->rx_params.num_pools; i++)
sc->sc_bpools[i] = params->rx_params.bpools[i];
/* TODO: buf_margins? See fman_sp_build_buffer_struct */
return (0);
}
static int
fman_port_init_bmi_rx(struct fman_port_softc *sc)
{
/* TODO: Sort the buffer pool list. */
/* TODO: Backup pools */
/* TODO: Depletion mode */
for (int i = 0; i < FMAN_PORT_MAX_POOLS; i++) {
/* Initialize the external pool info */
if (sc->sc_bpools[i].size != 0) {
bus_write_4(sc->sc_mem, FMBM_REBMPI(i),
REBMPI_VAL | REBMPI_ACE |
(sc->sc_bpools[i].bpid << REBMPI_BPID_S) |
sc->sc_bpools[i].size);
} else
/* Mark invalid if zero */
bus_write_4(sc->sc_mem, FMBM_REBMPI(i), 0);
}
bus_write_4(sc->sc_mem, FMBM_RDA, RDA_WOPT);
bus_write_4(sc->sc_mem, FMBM_RFCA,
RFCA_OR | RFCA_SYNC_REQ | RFCA_MR_DEF);
bus_write_4(sc->sc_mem, FMBM_RFPNE,
NIA_ENG_HWK);
bus_write_4(sc->sc_mem, FMBM_RFENE,
NIA_ENG_QMI_ENQ | NIA_ORDER_RESTORE);
bus_write_4(sc->sc_mem, FMBM_RFQID, sc->sc_default_fqid);
bus_write_4(sc->sc_mem, FMBM_REFQID, sc->sc_err_fqid);
if (sc->sc_revision_major < 6)
bus_write_4(sc->sc_mem, FMBM_RETH, RETH_ETHE);
/* Errata A006320 makes CFED field bad */
if (sc->sc_revision_major == 6 && (sc->sc_revision_minor == 0))
/* These are under errata A006320 */;
else
bus_write_4(sc->sc_mem, FMBM_RFED,
DEFAULT_RX_CUT_END_BYTES << BMI_RX_FRAME_END_CUT_SHIFT);
/* No internal context */
bus_write_4(sc->sc_mem, FMBM_RICP, 0);
/* TODO: Enable HW Parser. */
bus_write_4(sc->sc_mem, FMBM_RFNE, NIA_ENG_BMI | NIA_BMI_AC_ENQ_FRAME);
bus_write_4(sc->sc_mem, FMBM_RFSDM, FM_FD_ERR_DIS);
bus_write_4(sc->sc_mem, FMBM_RFSEM, BMI_RX_ERR & ~FM_FD_ERR_DIS);
return (0);
}
static int
fman_port_init_bmi_tx(struct fman_port_softc *sc)
{
uint32_t reg;
int depth;
bus_write_4(sc->sc_mem, FMBM_TCFG, 0);
bus_write_4(sc->sc_mem, FMBM_TDA, 0);
bus_write_4(sc->sc_mem, FMBM_TFED, 0);
if (sc->sc_port_speed == 10000)
depth = 4;
else if (sc->sc_revision_major >= 6)
depth = 2;
else
depth = 1;
sc->sc_tx_deq_pipeline_depth = depth;
reg = ((depth - 1) << BMI_FIFO_PIPELINE_DEPTH_SHIFT) | 0x13;
bus_write_4(sc->sc_mem, FMBM_TFP, reg);
/* Default color: green */
bus_write_4(sc->sc_mem, FMBM_TFCA,
TFCA_MR_DEF | TFCA_ATTR_ORDER);
bus_write_4(sc->sc_mem, FMBM_TFDNE, NIA_ENG_QMI_DEQ);
bus_write_4(sc->sc_mem, FMBM_TFENE,
NIA_ENG_QMI_ENQ | NIA_ORDER_RESTORE);
if (sc->sc_revision_major >= 6)
bus_write_4(sc->sc_mem, FMBM_TFNE,
(sc->sc_default_fqid == 0 ? TFNE_EBD : 0) |
NIA_BMI_AC_FETCH_ALLFRAME);
bus_write_4(sc->sc_mem, FMBM_TCFQID, sc->sc_default_fqid);
bus_write_4(sc->sc_mem, FMBM_TEFQID, sc->sc_err_fqid);
return (0);
}
static int
fman_port_init_hwp(struct fman_port_softc *sc)
{
int i;
/* TODO: fman_port_init_hwp */
bus_write_4(sc->sc_mem, HWP_PCAC, HWP_PCAC_PSTOP);
for (i = 0; i < 100 &&
(bus_read_4(sc->sc_mem, HWP_PCAC) & HWP_HXS_PCAC_PSTAT) != 0; i++) {
DELAY(10);
}
if (i == 100) {
device_printf(sc->sc_dev, "Timeout stopping HW parser\n");
return (ENXIO);
}
/* set the parser examination config */
for (i = 0; i < HWP_HXS_COUNT; i++) {
bus_write_4(sc->sc_mem, HWP_HXS_SSA(i), 0);
bus_write_4(sc->sc_mem, HWP_HXS_LCV(i), 0xffffffff);
}
bus_write_4(sc->sc_mem, HWP_HXS_SSA(HWP_HXS_TCP), HXS_SH_PAD_REM);
bus_write_4(sc->sc_mem, HWP_HXS_SSA(HWP_HXS_UDP), HXS_SH_PAD_REM);
bus_write_4(sc->sc_mem, HWP_PCAC, 0);
for (i = 100; i > 0 &&
(bus_read_4(sc->sc_mem, HWP_PCAC) & HWP_HXS_PCAC_PSTAT) == 0; i--) {
DELAY(10);
}
if (i == 0) {
device_printf(sc->sc_dev, "Timeout starting HW parser\n");
return (ENXIO);
}
return (0);
}
static int
fman_port_init_qmi(struct fman_port_softc *sc)
{
uint32_t reg;
if (sc->sc_port_type == FMAN_PORT_TYPE_RX) {
bus_write_4(sc->sc_mem, FMQM_PNEN,
NIA_ENG_BMI | NIA_BMI_AC_RELEASE);
return (0);
}
/* TX port */
bus_write_4(sc->sc_mem, FMQM_PNDN,
NIA_ENG_BMI | NIA_BMI_AC_TX);
/* TX port */
bus_write_4(sc->sc_mem, FMQM_PNEN,
NIA_ENG_BMI | NIA_BMI_AC_TX_RELEASE);
reg = 0;
if (sc->sc_deq_high_priority)
reg |= QMI_DEQ_CFG_PRI;
reg |= QMI_DEQ_CFG_TYPE1;
reg |= QMI_DEQ_CFG_PREFETCH_FULL;
reg |= (sc->sc_qman_channel_id & QMI_DEQ_CFG_SP_MASK) << QMI_DEQ_CFG_SP_SHIFT;
reg |= sc->sc_deq_byte_count;
bus_write_4(sc->sc_mem, FMQM_PNDC, reg);
return (0);
}
static int
fman_port_init(device_t dev)
{
struct fman_port_init_params params;
struct fman_port_softc *sc = device_get_softc(dev);
int err;
if (sc->sc_port_type == FMAN_PORT_TYPE_RX) {
/* Set up RX buffers and fifo */
}
params.port_id = sc->sc_port_id;
params.is_rx_port = (sc->sc_port_type == FMAN_PORT_TYPE_RX);
params.num_tasks = sc->sc_tasks.num;
params.extra_tasks = sc->sc_tasks.extra;
params.open_dmas = sc->sc_open_dmas.num;
params.extra_dmas = sc->sc_open_dmas.extra;
params.fifo_size = sc->sc_fifo_bufs.num;
params.extra_fifo_size = sc->sc_fifo_bufs.extra;
params.max_frame_length = sc->sc_max_frame_length;
params.deq_pipeline_size = sc->sc_tx_deq_pipeline_depth;
/* TODO: verify_size_of_fifo() from Linux driver */
err = FMAN_SET_PORT_PARAMS(device_get_parent(dev), &params);
if (err != 0)
return (err);
if (sc->sc_port_type == FMAN_PORT_TYPE_TX)
err = fman_port_init_bmi_tx(sc);
else {
err = fman_port_init_bmi_rx(sc);
if (err == 0)
fman_port_init_hwp(sc);
}
if (err != 0)
return (err);
err = fman_port_init_qmi(sc);
/* TODO: keygen here */
return (err);
}
static int
fman_port_disable(device_t dev)
{
struct fman_port_softc *sc;
uint32_t reg;
int count;
sc = device_get_softc(dev);
switch (sc->sc_port_type) {
case FMAN_PORT_TYPE_TX:
reg = bus_read_4(sc->sc_mem, FMQM_PNC);
bus_write_4(sc->sc_mem, FMQM_PNC, reg & ~PNC_EN);
for (count = 0; count < 100; count++) {
DELAY(10);
reg = bus_read_4(sc->sc_mem, FMQM_PNS);
if (!(reg & PNS_DEQ_FD_BSY))
break;
}
if (count == 100)
device_printf(sc->sc_dev, "Timeout stopping QMI\n");
reg = bus_read_4(sc->sc_mem, FMBM_TCFG);
bus_write_4(sc->sc_mem, FMBM_TCFG, reg & ~BMI_PORT_CFG_EN);
for (count = 0; count < 100; count++) {
DELAY(10);
reg = bus_read_4(sc->sc_mem, FMBM_TST);
if (!(reg & PNS_DEQ_FD_BSY))
break;
}
if (count == 100)
device_printf(sc->sc_dev, "Timeout stopping BMI");
break;
case FMAN_PORT_TYPE_RX:
reg = bus_read_4(sc->sc_mem, FMBM_RCFG);
bus_write_4(sc->sc_mem, FMBM_RCFG, reg & ~BMI_PORT_CFG_EN);
for (count = 0; count < 100; count++) {
DELAY(10);
reg = bus_read_4(sc->sc_mem, FMBM_RST);
if (!(reg & PNS_DEQ_FD_BSY))
break;
}
if (count == 100)
device_printf(sc->sc_dev, "Timeout stopping BMI");
break;
}
return (0);
}
static int
fman_port_enable(device_t dev)
{
struct fman_port_softc *sc;
uint32_t reg;
sc = device_get_softc(dev);
switch (sc->sc_port_type) {
case FMAN_PORT_TYPE_TX:
reg = bus_read_4(sc->sc_mem, FMQM_PNC);
bus_write_4(sc->sc_mem, FMQM_PNC, reg | PNC_EN | PNC_STEN);
reg = bus_read_4(sc->sc_mem, FMBM_TCFG);
bus_write_4(sc->sc_mem, FMBM_TCFG, reg | BMI_PORT_CFG_EN);
break;
case FMAN_PORT_TYPE_RX:
reg = bus_read_4(sc->sc_mem, FMBM_RCFG);
bus_write_4(sc->sc_mem, FMQM_PNC, reg | PNC_EN | PNC_STEN);
bus_write_4(sc->sc_mem, FMBM_RCFG, reg | BMI_PORT_CFG_EN);
bus_write_4(sc->sc_mem, FMBM_RSTC, RSTC_EN);
break;
}
return (0);
}
static device_method_t fman_port_methods[] = {
DEVMETHOD(device_probe, fman_port_probe),
DEVMETHOD(device_attach, fman_port_attach),
DEVMETHOD(device_detach, fman_port_detach),
DEVMETHOD(fman_port_config, fman_port_config),
DEVMETHOD(fman_port_init, fman_port_init),
DEVMETHOD(fman_port_enable, fman_port_enable),
DEVMETHOD(fman_port_disable, fman_port_disable),
DEVMETHOD_END
};
DEFINE_CLASS_0(fman_port, fman_port_driver, fman_port_methods,
sizeof(struct fman_port_softc));
EARLY_DRIVER_MODULE(fman_port, fman, fman_port_driver, 0, 0,
BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+29
View File
@@ -0,0 +1,29 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#ifndef DPAA_FMAN_PORT_H
#define DPAA_FMAN_PORT_H
#define FMAN_PORT_MAX_POOLS 4
struct fman_port_buffer_pool {
uint8_t bpid;
uint16_t size;
};
struct fman_port_params {
uint32_t dflt_fqid; /* Must not be 0 */
uint32_t err_fqid;
union {
struct {
int num_pools;
struct fman_port_buffer_pool bpools[FMAN_PORT_MAX_POOLS];
} rx_params;
struct {
} tx_params;
};
};
#endif
+55
View File
@@ -0,0 +1,55 @@
#-
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2026 Justin Hibbits
#
# 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/dpaa/fman_port.h>
/**
* @brief DPAA FMan Port interface
*
*/
INTERFACE fman_port;
/**
* @brief Configure the port for a specific purpose
*/
METHOD int config {
device_t dev;
struct fman_port_params *params;
};
METHOD int init {
device_t dev;
};
METHOD int disable {
device_t dev;
};
METHOD int enable {
device_t dev;
};
+284
View File
@@ -0,0 +1,284 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/resource.h>
#include <sys/socket.h>
#include <machine/bus.h>
#include <net/if.h>
#include <net/if_media.h>
#include <net/if_types.h>
#include <net/if_var.h>
#include <dev/mdio/mdio.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include "fman.h"
#include "miibus_if.h"
#include "mdio_if.h"
#define MDIO_LOCK() mtx_lock(&sc->sc_lock)
#define MDIO_UNLOCK() mtx_unlock(&sc->sc_lock)
#define MDIO_WRITE4(sc, r, v) \
bus_write_4(sc->sc_res, r, v)
#define MDIO_READ4(sc, r) \
bus_read_4(sc->sc_res, r)
#define MDIO_CFG 0x30
#define CFG_ENC45 0x00000040
#define MDIO_STAT 0x30
#define STAT_BUSY 0x80000000
#define STAT_MDIO_RD_ER 0x00000002
#define MDIO_CTL 0x34
#define CTL_READ 0x00008000
#define MDIO_DATA 0x38
#define MDIO_ADDR 0x3c
static int xmdio_fdt_probe(device_t dev);
static int xmdio_fdt_attach(device_t dev);
static int xmdio_detach(device_t dev);
static int xmdio_miibus_readreg(device_t dev, int phy, int reg);
static int xmdio_miibus_writereg(device_t dev, int phy, int reg, int value);
static int xmdio_mdio_readextreg(device_t dev, int phy, int devad, int reg);
static int xmdio_mdio_writeextreg(device_t dev, int phy, int devad, int reg,
int val);
struct xmdio_softc {
struct mtx sc_lock;
struct resource *sc_res;
};
static struct ofw_compat_data mdio_compat_data[] = {
{"fsl,fman-memac-mdio", 0},
{"fsl,fman-xmdio", 0},
{NULL, 0}
};
static device_method_t xmdio_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, xmdio_fdt_probe),
DEVMETHOD(device_attach, xmdio_fdt_attach),
DEVMETHOD(device_detach, xmdio_detach),
DEVMETHOD(bus_add_child, bus_generic_add_child),
/* MII interface */
DEVMETHOD(miibus_readreg, xmdio_miibus_readreg),
DEVMETHOD(miibus_writereg, xmdio_miibus_writereg),
/* MDIO interface */
DEVMETHOD(mdio_readreg, xmdio_miibus_readreg),
DEVMETHOD(mdio_writereg, xmdio_miibus_writereg),
DEVMETHOD(mdio_readextreg, xmdio_mdio_readextreg),
DEVMETHOD(mdio_writeextreg, xmdio_mdio_writeextreg),
DEVMETHOD_END
};
static driver_t xmdio_driver = {
"xmdio",
xmdio_methods,
sizeof(struct xmdio_softc),
};
EARLY_DRIVER_MODULE(xmdio, fman, xmdio_driver, 0, 0,
BUS_PASS_SUPPORTDEV);
DRIVER_MODULE(miibus, xmdio, miibus_driver, 0, 0);
DRIVER_MODULE(mdio, xmdio, mdio_driver, 0, 0);
MODULE_DEPEND(xmdio, miibus, 1, 1, 1);
static int
xmdio_fdt_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_search_compatible(dev, mdio_compat_data)->ocd_str)
return (ENXIO);
device_set_desc(dev, "Freescale XGMAC MDIO");
return (BUS_PROBE_DEFAULT);
}
static int
xmdio_fdt_attach(device_t dev)
{
struct xmdio_softc *sc;
sc = device_get_softc(dev);
sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0, RF_ACTIVE);
OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev);
mtx_init(&sc->sc_lock, device_get_nameunit(dev), "XMDIO lock",
MTX_DEF);
return (0);
}
static int
xmdio_detach(device_t dev)
{
struct xmdio_softc *sc;
sc = device_get_softc(dev);
mtx_destroy(&sc->sc_lock);
return (0);
}
static void
set_clause45(struct xmdio_softc *sc)
{
uint32_t reg;
reg = MDIO_READ4(sc, MDIO_CFG);
MDIO_WRITE4(sc, MDIO_CFG, reg | CFG_ENC45);
}
static void
set_clause22(struct xmdio_softc *sc)
{
uint32_t reg;
reg = MDIO_READ4(sc, MDIO_CFG);
MDIO_WRITE4(sc, MDIO_CFG, reg & ~CFG_ENC45);
}
static int
xmdio_wait_no_busy(struct xmdio_softc *sc)
{
uint32_t count, val;
for (count = 1000; count > 0; count--) {
val = MDIO_READ4(sc, MDIO_CFG);
if ((val & STAT_BUSY) == 0)
break;
DELAY(1);
}
if (count == 0)
return (0xffff);
return (0);
}
int
xmdio_miibus_readreg(device_t dev, int phy, int reg)
{
struct xmdio_softc *sc;
int rv;
uint32_t ctl;
sc = device_get_softc(dev);
MDIO_LOCK();
set_clause22(sc);
ctl = (phy << 5) | reg;
MDIO_WRITE4(sc, MDIO_CTL, ctl | CTL_READ);
MDIO_READ4(sc, MDIO_CTL);
if (xmdio_wait_no_busy(sc))
rv = 0xffff;
else
rv = MDIO_READ4(sc, MDIO_DATA);
MDIO_WRITE4(sc, MDIO_CTL, 0);
MDIO_UNLOCK();
return (rv);
}
int
xmdio_miibus_writereg(device_t dev, int phy, int reg, int value)
{
struct xmdio_softc *sc;
sc = device_get_softc(dev);
MDIO_LOCK();
set_clause22(sc);
/* Stop the MII management read cycle */
MDIO_WRITE4(sc, MDIO_CTL, (phy << 5) | reg);
MDIO_WRITE4(sc, MDIO_DATA, value);
/* Wait till MII management write is complete */
xmdio_wait_no_busy(sc);
MDIO_UNLOCK();
return (0);
}
static int
xmdio_mdio_readextreg(device_t dev, int phy, int devad, int reg)
{
struct xmdio_softc *sc;
int rv;
uint32_t ctl;
sc = device_get_softc(dev);
MDIO_LOCK();
set_clause45(sc);
ctl = (phy << 5) | devad;
MDIO_WRITE4(sc, MDIO_CTL, ctl);
MDIO_WRITE4(sc, MDIO_ADDR, reg);
xmdio_wait_no_busy(sc);
MDIO_WRITE4(sc, MDIO_CTL, ctl | CTL_READ);
MDIO_READ4(sc, MDIO_CTL);
xmdio_wait_no_busy(sc);
if (MDIO_READ4(sc, MDIO_STAT) & STAT_MDIO_RD_ER)
rv = 0xffff;
else
rv = MDIO_READ4(sc, MDIO_DATA);
MDIO_WRITE4(sc, MDIO_CTL, 0);
MDIO_UNLOCK();
return (rv);
}
static int
xmdio_mdio_writeextreg(device_t dev, int phy, int devad, int reg, int val)
{
struct xmdio_softc *sc;
sc = device_get_softc(dev);
MDIO_LOCK();
set_clause45(sc);
/* Stop the MII management read cycle */
MDIO_WRITE4(sc, MDIO_CTL, (phy << 5) | devad);
MDIO_WRITE4(sc, MDIO_DATA, val);
/* Wait till MII management write is complete */
xmdio_wait_no_busy(sc);
MDIO_UNLOCK();
return (0);
}
+201 -413
View File
@@ -54,271 +54,60 @@
#include "miibus_if.h"
#include <contrib/ncsw/inc/integrations/dpaa_integration_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h>
#include <contrib/ncsw/inc/flib/fsl_fman_dtsec.h>
#include <contrib/ncsw/inc/xx_ext.h>
#include "dpaa_eth.h"
#include "fman.h"
#include "fman_port.h"
#include "if_dtsec.h"
#include "if_dtsec_im.h"
#include "if_dtsec_rm.h"
#include "fman_if.h"
#include "fman_port_if.h"
#define DTSEC_MIN_FRAME_SIZE 64
#define DTSEC_MAX_FRAME_SIZE 9600
#define DTSEC_REG_MAXFRM 0x110
#define DTSEC_REG_GADDR(i) (0x0a0 + 4*(i))
#define DTSEC_REG_IGADDR(i) (0x080 + 4 * (i))
#define DTSEC_REG_GADDR(i) (0x0a0 + 4 * (i))
/**
* @group dTSEC private defines.
* @{
*/
/**
* dTSEC FMan MAC exceptions info struct.
*/
struct dtsec_fm_mac_ex_str {
const int num;
const char *str;
};
/** @} */
#define DTSEC_ECNTRL 0x014
#define ECNTRL_R100M 0x00000008
#define DTSEC_TCTRL 0x040
#define TCTRL_GTS 0x00000020
#define DTSEC_RCTRL 0x050
#define RCTRL_CFA 0x00008000
#define RCTRL_GHTX 0x00000400
#define RCTRL_GRS 0x00000020
#define RCTRL_MPROM 0x00000008
#define DTSEC_MACCFG1 0x100
#define DTSEC_MACCFG2 0x104
#define MACCFG_IF_M 0x00000300
#define MACCFG_IF_10_100 0x00000100
#define MACCFG_IF_1G 0x00000200
#define MACCFG_FULLDUPLEX 0x00000001
#define DTSEC_MACSTNADDR1 0x140
#define DTSEC_MACSTNADDR2 0x144
static void dtsec_if_init_locked(struct dtsec_softc *sc);
/**
* @group FMan MAC routines.
* @{
*/
#define DTSEC_MAC_EXCEPTIONS_END (-1)
/**
* FMan MAC exceptions.
*/
static const struct dtsec_fm_mac_ex_str dtsec_fm_mac_exceptions[] = {
{ e_FM_MAC_EX_10G_MDIO_SCAN_EVENTMDIO, "MDIO scan event" },
{ e_FM_MAC_EX_10G_MDIO_CMD_CMPL, "MDIO command completion" },
{ e_FM_MAC_EX_10G_REM_FAULT, "Remote fault" },
{ e_FM_MAC_EX_10G_LOC_FAULT, "Local fault" },
{ e_FM_MAC_EX_10G_1TX_ECC_ER, "Transmit frame ECC error" },
{ e_FM_MAC_EX_10G_TX_FIFO_UNFL, "Transmit FIFO underflow" },
{ e_FM_MAC_EX_10G_TX_FIFO_OVFL, "Receive FIFO overflow" },
{ e_FM_MAC_EX_10G_TX_ER, "Transmit frame error" },
{ e_FM_MAC_EX_10G_RX_FIFO_OVFL, "Receive FIFO overflow" },
{ e_FM_MAC_EX_10G_RX_ECC_ER, "Receive frame ECC error" },
{ e_FM_MAC_EX_10G_RX_JAB_FRM, "Receive jabber frame" },
{ e_FM_MAC_EX_10G_RX_OVRSZ_FRM, "Receive oversized frame" },
{ e_FM_MAC_EX_10G_RX_RUNT_FRM, "Receive runt frame" },
{ e_FM_MAC_EX_10G_RX_FRAG_FRM, "Receive fragment frame" },
{ e_FM_MAC_EX_10G_RX_LEN_ER, "Receive payload length error" },
{ e_FM_MAC_EX_10G_RX_CRC_ER, "Receive CRC error" },
{ e_FM_MAC_EX_10G_RX_ALIGN_ER, "Receive alignment error" },
{ e_FM_MAC_EX_1G_BAB_RX, "Babbling receive error" },
{ e_FM_MAC_EX_1G_RX_CTL, "Receive control (pause frame) interrupt" },
{ e_FM_MAC_EX_1G_GRATEFUL_TX_STP_COMPLET, "Graceful transmit stop "
"complete" },
{ e_FM_MAC_EX_1G_BAB_TX, "Babbling transmit error" },
{ e_FM_MAC_EX_1G_TX_CTL, "Transmit control (pause frame) interrupt" },
{ e_FM_MAC_EX_1G_TX_ERR, "Transmit error" },
{ e_FM_MAC_EX_1G_LATE_COL, "Late collision" },
{ e_FM_MAC_EX_1G_COL_RET_LMT, "Collision retry limit" },
{ e_FM_MAC_EX_1G_TX_FIFO_UNDRN, "Transmit FIFO underrun" },
{ e_FM_MAC_EX_1G_MAG_PCKT, "Magic Packet detected when dTSEC is in "
"Magic Packet detection mode" },
{ e_FM_MAC_EX_1G_MII_MNG_RD_COMPLET, "MII management read completion" },
{ e_FM_MAC_EX_1G_MII_MNG_WR_COMPLET, "MII management write completion" },
{ e_FM_MAC_EX_1G_GRATEFUL_RX_STP_COMPLET, "Graceful receive stop "
"complete" },
{ e_FM_MAC_EX_1G_TX_DATA_ERR, "Internal data error on transmit" },
{ e_FM_MAC_EX_1G_RX_DATA_ERR, "Internal data error on receive" },
{ e_FM_MAC_EX_1G_1588_TS_RX_ERR, "Time-Stamp Receive Error" },
{ e_FM_MAC_EX_1G_RX_MIB_CNT_OVFL, "MIB counter overflow" },
{ DTSEC_MAC_EXCEPTIONS_END, "" }
};
static const char *
dtsec_fm_mac_ex_to_str(e_FmMacExceptions exception)
{
int i;
for (i = 0; dtsec_fm_mac_exceptions[i].num != exception &&
dtsec_fm_mac_exceptions[i].num != DTSEC_MAC_EXCEPTIONS_END; ++i)
;
if (dtsec_fm_mac_exceptions[i].num == DTSEC_MAC_EXCEPTIONS_END)
return ("<Unknown Exception>");
return (dtsec_fm_mac_exceptions[i].str);
}
static void
dtsec_fm_mac_mdio_event_callback(t_Handle h_App,
e_FmMacExceptions exception)
{
struct dtsec_softc *sc;
sc = h_App;
device_printf(sc->sc_dev, "MDIO event %i: %s.\n", exception,
dtsec_fm_mac_ex_to_str(exception));
}
static void
dtsec_fm_mac_exception_callback(t_Handle app, e_FmMacExceptions exception)
{
struct dtsec_softc *sc;
sc = app;
device_printf(sc->sc_dev, "MAC exception %i: %s.\n", exception,
dtsec_fm_mac_ex_to_str(exception));
}
static void
dtsec_fm_mac_free(struct dtsec_softc *sc)
{
if (sc->sc_mach == NULL)
return;
FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
FM_MAC_Free(sc->sc_mach);
sc->sc_mach = NULL;
}
static int
dtsec_fm_mac_init(struct dtsec_softc *sc, uint8_t *mac)
{
t_FmMacParams params;
t_Error error;
FMAN_GET_REVISION(device_get_parent(sc->sc_base.sc_dev), &sc->sc_base.sc_rev_major,
&sc->sc_base.sc_rev_minor);
memset(&params, 0, sizeof(params));
memcpy(&params.addr, mac, sizeof(params.addr));
params.baseAddr = rman_get_bushandle(sc->sc_mem);
params.enetMode = sc->sc_mac_enet_mode;
params.macId = sc->sc_eth_id;
params.mdioIrq = sc->sc_mac_mdio_irq;
params.f_Event = dtsec_fm_mac_mdio_event_callback;
params.f_Exception = dtsec_fm_mac_exception_callback;
params.h_App = sc;
params.h_Fm = sc->sc_fmh;
sc->sc_mach = FM_MAC_Config(&params);
if (sc->sc_mach == NULL) {
device_printf(sc->sc_dev, "couldn't configure FM_MAC module.\n"
);
if (FMAN_RESET_MAC(device_get_parent(sc->sc_base.sc_dev), sc->sc_base.sc_eth_id) != 0)
return (ENXIO);
}
error = FM_MAC_ConfigResetOnInit(sc->sc_mach, TRUE);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't enable reset on init "
"feature.\n");
dtsec_fm_mac_free(sc);
return (ENXIO);
}
/* Do not inform about pause frames */
error = FM_MAC_ConfigException(sc->sc_mach, e_FM_MAC_EX_1G_RX_CTL,
FALSE);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't disable pause frames "
"exception.\n");
dtsec_fm_mac_free(sc);
return (ENXIO);
}
error = FM_MAC_Init(sc->sc_mach);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't initialize FM_MAC module."
"\n");
dtsec_fm_mac_free(sc);
return (ENXIO);
}
return (0);
}
/** @} */
/**
* @group FMan PORT routines.
* @{
*/
static const char *
dtsec_fm_port_ex_to_str(e_FmPortExceptions exception)
{
switch (exception) {
case e_FM_PORT_EXCEPTION_IM_BUSY:
return ("IM: RX busy");
default:
return ("<Unknown Exception>");
}
}
void
dtsec_fm_port_rx_exception_callback(t_Handle app,
e_FmPortExceptions exception)
{
struct dtsec_softc *sc;
sc = app;
device_printf(sc->sc_dev, "RX exception: %i: %s.\n", exception,
dtsec_fm_port_ex_to_str(exception));
}
void
dtsec_fm_port_tx_exception_callback(t_Handle app,
e_FmPortExceptions exception)
{
struct dtsec_softc *sc;
sc = app;
device_printf(sc->sc_dev, "TX exception: %i: %s.\n", exception,
dtsec_fm_port_ex_to_str(exception));
}
e_FmPortType
dtsec_fm_port_rx_type(enum eth_dev_type type)
{
switch (type) {
case ETH_DTSEC:
return (e_FM_PORT_TYPE_RX);
case ETH_10GSEC:
return (e_FM_PORT_TYPE_RX_10G);
default:
return (e_FM_PORT_TYPE_DUMMY);
}
}
e_FmPortType
dtsec_fm_port_tx_type(enum eth_dev_type type)
{
switch (type) {
case ETH_DTSEC:
return (e_FM_PORT_TYPE_TX);
case ETH_10GSEC:
return (e_FM_PORT_TYPE_TX_10G);
default:
return (e_FM_PORT_TYPE_DUMMY);
}
}
static void
dtsec_fm_port_free_both(struct dtsec_softc *sc)
{
if (sc->sc_rxph) {
FM_PORT_Free(sc->sc_rxph);
sc->sc_rxph = NULL;
}
if (sc->sc_txph) {
FM_PORT_Free(sc->sc_txph);
sc->sc_txph = NULL;
}
}
/** @} */
/**
* @group IFnet routines.
* @{
@@ -332,7 +121,7 @@ dtsec_set_mtu(struct dtsec_softc *sc, unsigned int mtu)
DTSEC_LOCK_ASSERT(sc);
if (mtu >= DTSEC_MIN_FRAME_SIZE && mtu <= DTSEC_MAX_FRAME_SIZE) {
bus_write_4(sc->sc_mem, DTSEC_REG_MAXFRM, mtu);
bus_write_4(sc->sc_base.sc_mem, DTSEC_REG_MAXFRM, mtu);
return (mtu);
}
@@ -342,9 +131,10 @@ dtsec_set_mtu(struct dtsec_softc *sc, unsigned int mtu)
static u_int
dtsec_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct dtsec_softc *sc = arg;
uint32_t h, *hashtable = arg;
FM_MAC_AddHashMacAddr(sc->sc_mach, (t_EnetAddr *)LLADDR(sdl));
h = (ether_crc32_be(LLADDR(sdl), ETHER_ADDR_LEN) >> 24) & 0xFF;
hashtable[(h >> 5)] |= 1 << (0x1F - (h & 0x1F));
return (1);
}
@@ -352,18 +142,52 @@ dtsec_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
static void
dtsec_setup_multicast(struct dtsec_softc *sc)
{
uint32_t hashtable[8] = {};
int i;
if (if_getflags(sc->sc_ifnet) & IFF_ALLMULTI) {
if (if_getflags(sc->sc_base.sc_ifnet) & IFF_ALLMULTI) {
for (i = 0; i < 8; i++)
bus_write_4(sc->sc_mem, DTSEC_REG_GADDR(i), 0xFFFFFFFF);
bus_write_4(sc->sc_base.sc_mem, DTSEC_REG_GADDR(i), 0xFFFFFFFF);
bus_write_4(sc->sc_base.sc_mem, DTSEC_RCTRL,
bus_read_4(sc->sc_base.sc_mem, DTSEC_RCTRL) | RCTRL_MPROM);
return;
}
bus_write_4(sc->sc_base.sc_mem, DTSEC_RCTRL,
bus_read_4(sc->sc_base.sc_mem, DTSEC_RCTRL) & ~RCTRL_MPROM);
fman_dtsec_reset_filter_table(rman_get_virtual(sc->sc_mem),
true, false);
if_foreach_llmaddr(sc->sc_ifnet, dtsec_hash_maddr, sc);
if_foreach_llmaddr(sc->sc_base.sc_ifnet, dtsec_hash_maddr, hashtable);
for (i = 0; i < 8; i++)
bus_write_4(sc->sc_base.sc_mem, DTSEC_REG_GADDR(i),
hashtable[i]);
}
static void
dtsec_if_graceful_stop(struct dtsec_softc *sc)
{
bus_write_4(sc->sc_base.sc_mem, DTSEC_RCTRL,
bus_read_4(sc->sc_base.sc_mem, DTSEC_RCTRL) | RCTRL_GRS);
if (sc->sc_base.sc_rev_major == 2)
DELAY(100);
else
DELAY(10);
bus_write_4(sc->sc_base.sc_mem, DTSEC_TCTRL,
bus_read_4(sc->sc_base.sc_mem, DTSEC_TCTRL) | TCTRL_GTS);
}
static void
dtsec_if_graceful_start(struct dtsec_softc *sc)
{
bus_write_4(sc->sc_base.sc_mem, DTSEC_RCTRL,
bus_read_4(sc->sc_base.sc_mem, DTSEC_RCTRL) & ~RCTRL_GRS);
if (sc->sc_base.sc_rev_major == 2)
DELAY(100);
else
DELAY(10);
bus_write_4(sc->sc_base.sc_mem, DTSEC_TCTRL,
bus_read_4(sc->sc_base.sc_mem, DTSEC_TCTRL) & ~TCTRL_GTS);
}
static int
@@ -373,24 +197,22 @@ dtsec_if_enable_locked(struct dtsec_softc *sc)
DTSEC_LOCK_ASSERT(sc);
error = FM_MAC_Enable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
if (error != E_OK)
dtsec_if_graceful_start(sc);
error = FMAN_PORT_ENABLE(sc->sc_base.sc_rx_port);
if (error != 0)
return (EIO);
error = FM_PORT_Enable(sc->sc_rxph);
if (error != E_OK)
return (EIO);
error = FM_PORT_Enable(sc->sc_txph);
if (error != E_OK)
error = FMAN_PORT_ENABLE(sc->sc_base.sc_tx_port);
if (error != 0)
return (EIO);
dtsec_setup_multicast(sc);
if_setdrvflagbits(sc->sc_ifnet, IFF_DRV_RUNNING, 0);
if_setdrvflagbits(sc->sc_base.sc_ifnet, IFF_DRV_RUNNING, 0);
/* Refresh link state */
dtsec_miibus_statchg(sc->sc_dev);
dtsec_miibus_statchg(sc->sc_base.sc_dev);
return (0);
}
@@ -402,19 +224,17 @@ dtsec_if_disable_locked(struct dtsec_softc *sc)
DTSEC_LOCK_ASSERT(sc);
error = FM_MAC_Disable(sc->sc_mach, e_COMM_MODE_RX_AND_TX);
if (error != E_OK)
dtsec_if_graceful_stop(sc);
error = FMAN_PORT_DISABLE(sc->sc_base.sc_rx_port);
if (error != 0)
return (EIO);
error = FM_PORT_Disable(sc->sc_rxph);
if (error != E_OK)
error = FMAN_PORT_DISABLE(sc->sc_base.sc_tx_port);
if (error != 0)
return (EIO);
error = FM_PORT_Disable(sc->sc_txph);
if (error != E_OK)
return (EIO);
if_setdrvflagbits(sc->sc_ifnet, 0, IFF_DRV_RUNNING);
if_setdrvflagbits(sc->sc_base.sc_ifnet, 0, IFF_DRV_RUNNING);
return (0);
}
@@ -443,9 +263,10 @@ dtsec_if_ioctl(if_t ifp, u_long command, caddr_t data)
case SIOCSIFFLAGS:
DTSEC_LOCK(sc);
if (if_getflags(sc->sc_ifnet) & IFF_UP)
error = dtsec_if_enable_locked(sc);
else
if (if_getflags(ifp) & IFF_UP) {
if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
dtsec_if_init_locked(sc);
} else
error = dtsec_if_disable_locked(sc);
DTSEC_UNLOCK(sc);
@@ -453,7 +274,7 @@ dtsec_if_ioctl(if_t ifp, u_long command, caddr_t data)
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii->mii_media,
error = ifmedia_ioctl(ifp, ifr, &sc->sc_base.sc_mii->mii_media,
command);
break;
@@ -474,8 +295,8 @@ dtsec_if_tick(void *arg)
/* TODO */
DTSEC_LOCK(sc);
mii_tick(sc->sc_mii);
callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc);
mii_tick(sc->sc_base.sc_mii);
callout_reset(&sc->sc_base.sc_tick_callout, hz, dtsec_if_tick, sc);
DTSEC_UNLOCK(sc);
}
@@ -487,30 +308,38 @@ dtsec_if_deinit_locked(struct dtsec_softc *sc)
DTSEC_LOCK_ASSERT(sc);
DTSEC_UNLOCK(sc);
callout_drain(&sc->sc_tick_callout);
callout_drain(&sc->sc_base.sc_tick_callout);
DTSEC_LOCK(sc);
}
static void
dtsec_if_set_macaddr(struct dtsec_softc *sc, const char *addr)
{
uint32_t reg;
reg = (addr[5] << 24) | (addr[4] << 16) | (addr[3] << 8) | addr[2];
bus_write_4(sc->sc_base.sc_mem, DTSEC_MACSTNADDR1, reg);
reg = (addr[1] << 24) | (addr[0] << 16);
bus_write_4(sc->sc_base.sc_mem, DTSEC_MACSTNADDR2, reg);
}
static void
dtsec_if_init_locked(struct dtsec_softc *sc)
{
int error;
const char *macaddr;
DTSEC_LOCK_ASSERT(sc);
/* Set MAC address */
error = FM_MAC_ModifyMacAddr(sc->sc_mach,
(t_EnetAddr *)if_getlladdr(sc->sc_ifnet));
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't set MAC address.\n");
goto err;
}
macaddr = if_getlladdr(sc->sc_base.sc_ifnet);
dtsec_if_set_macaddr(sc, macaddr);
/* Start MII polling */
if (sc->sc_mii)
callout_reset(&sc->sc_tick_callout, hz, dtsec_if_tick, sc);
if (sc->sc_base.sc_mii)
callout_reset(&sc->sc_base.sc_tick_callout, hz,
dtsec_if_tick, sc);
if (if_getflags(sc->sc_ifnet) & IFF_UP) {
if (if_getflags(sc->sc_base.sc_ifnet) & IFF_UP) {
error = dtsec_if_enable_locked(sc);
if (error != 0)
goto err;
@@ -524,7 +353,7 @@ dtsec_if_init_locked(struct dtsec_softc *sc)
err:
dtsec_if_deinit_locked(sc);
device_printf(sc->sc_dev, "initialization error.\n");
device_printf(sc->sc_base.sc_dev, "initialization error.\n");
return;
}
@@ -547,7 +376,7 @@ dtsec_if_start(if_t ifp)
sc = if_getsoftc(ifp);
DTSEC_LOCK(sc);
sc->sc_start_locked(sc);
dpaa_eth_if_start_locked(&sc->sc_base);
DTSEC_UNLOCK(sc);
}
@@ -569,7 +398,7 @@ dtsec_ifmedia_upd(if_t ifp)
struct dtsec_softc *sc = if_getsoftc(ifp);
DTSEC_LOCK(sc);
mii_mediachg(sc->sc_mii);
mii_mediachg(sc->sc_base.sc_mii);
DTSEC_UNLOCK(sc);
return (0);
@@ -582,10 +411,10 @@ dtsec_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
DTSEC_LOCK(sc);
mii_pollstat(sc->sc_mii);
mii_pollstat(sc->sc_base.sc_mii);
ifmr->ifm_active = sc->sc_mii->mii_media_active;
ifmr->ifm_status = sc->sc_mii->mii_media_status;
ifmr->ifm_active = sc->sc_base.sc_mii->mii_media_active;
ifmr->ifm_status = sc->sc_base.sc_mii->mii_media_status;
DTSEC_UNLOCK(sc);
}
@@ -596,118 +425,75 @@ dtsec_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
* @group dTSEC bus interface.
* @{
*/
static void
dtsec_configure_mode(struct dtsec_softc *sc)
{
char tunable[64];
snprintf(tunable, sizeof(tunable), "%s.independent_mode",
device_get_nameunit(sc->sc_dev));
sc->sc_mode = DTSEC_MODE_REGULAR;
TUNABLE_INT_FETCH(tunable, &sc->sc_mode);
if (sc->sc_mode == DTSEC_MODE_REGULAR) {
sc->sc_port_rx_init = dtsec_rm_fm_port_rx_init;
sc->sc_port_tx_init = dtsec_rm_fm_port_tx_init;
sc->sc_start_locked = dtsec_rm_if_start_locked;
} else {
sc->sc_port_rx_init = dtsec_im_fm_port_rx_init;
sc->sc_port_tx_init = dtsec_im_fm_port_tx_init;
sc->sc_start_locked = dtsec_im_if_start_locked;
}
device_printf(sc->sc_dev, "Configured for %s mode.\n",
(sc->sc_mode == DTSEC_MODE_REGULAR) ? "regular" : "independent");
}
int
dtsec_attach(device_t dev)
{
struct dtsec_softc *sc;
device_t parent;
cell_t ports[2];
phandle_t node;
int error;
if_t ifp;
sc = device_get_softc(dev);
parent = device_get_parent(dev);
sc->sc_dev = dev;
sc->sc_mac_mdio_irq = NO_IRQ;
/* Check if MallocSmart allocator is ready */
if (XX_MallocSmartInit() != E_OK)
return (ENXIO);
sc->sc_base.sc_dev = dev;
node = ofw_bus_get_node(dev);
/* Init locks */
mtx_init(&sc->sc_lock, device_get_nameunit(dev),
mtx_init(&sc->sc_base.sc_lock, device_get_nameunit(dev),
"DTSEC Global Lock", MTX_DEF);
mtx_init(&sc->sc_mii_lock, device_get_nameunit(dev),
mtx_init(&sc->sc_base.sc_mii_lock, device_get_nameunit(dev),
"DTSEC MII Lock", MTX_DEF);
/* Init callouts */
callout_init(&sc->sc_tick_callout, CALLOUT_MPSAFE);
callout_init(&sc->sc_base.sc_tick_callout, CALLOUT_MPSAFE);
/* Read configuraton */
if ((error = fman_get_handle(parent, &sc->sc_fmh)) != 0)
return (error);
/* Create RX buffer pool */
error = dpaa_eth_pool_rx_init(&sc->sc_base);
if (error != 0)
return (EIO);
if ((error = fman_get_muram_handle(parent, &sc->sc_muramh)) != 0)
return (error);
/* Create RX frame queue range */
error = dpaa_eth_fq_rx_init(&sc->sc_base);
if (error != 0)
return (EIO);
if ((error = fman_get_bushandle(parent, &sc->sc_fm_base)) != 0)
return (error);
/* Create frame info pool */
error = dpaa_eth_fi_pool_init(&sc->sc_base);
if (error != 0)
return (EIO);
/* Configure working mode */
dtsec_configure_mode(sc);
/* Create TX frame queue range */
error = dpaa_eth_fq_tx_init(&sc->sc_base);
if (error != 0)
return (EIO);
/* If we are working in regular mode configure BMAN and QMAN */
if (sc->sc_mode == DTSEC_MODE_REGULAR) {
/* Create RX buffer pool */
error = dtsec_rm_pool_rx_init(sc);
if (error != 0)
return (EIO);
/* Create RX frame queue range */
error = dtsec_rm_fqr_rx_init(sc);
if (error != 0)
return (EIO);
/* Create frame info pool */
error = dtsec_rm_fi_pool_init(sc);
if (error != 0)
return (EIO);
/* Create TX frame queue range */
error = dtsec_rm_fqr_tx_init(sc);
if (error != 0)
return (EIO);
if (OF_getencprop(node, "fsl,fman-ports", ports, sizeof(ports)) < 0) {
device_printf(dev, "missing ports in device tree\n");
return (ENXIO);
}
/* Init FMan MAC module. */
error = dtsec_fm_mac_init(sc, sc->sc_mac_addr);
error = dtsec_fm_mac_init(sc, sc->sc_base.sc_mac_addr);
if (error != 0) {
dtsec_detach(dev);
return (ENXIO);
}
/* Init FMan TX port */
error = sc->sc_port_tx_init(sc, device_get_unit(sc->sc_dev));
if (error != 0) {
dtsec_detach(dev);
return (ENXIO);
}
sc->sc_base.sc_rx_port = OF_device_from_xref(ports[0]);
sc->sc_base.sc_tx_port = OF_device_from_xref(ports[1]);
dpaa_eth_fm_port_rx_init(&sc->sc_base);
dpaa_eth_fm_port_tx_init(&sc->sc_base);
/* Init FMan RX port */
error = sc->sc_port_rx_init(sc, device_get_unit(sc->sc_dev));
if (error != 0) {
if (sc->sc_base.sc_rx_port == NULL || sc->sc_base.sc_tx_port == NULL) {
device_printf(dev, "invalid ports");
dtsec_detach(dev);
return (ENXIO);
}
/* Create network interface for upper layers */
ifp = sc->sc_ifnet = if_alloc(IFT_ETHER);
ifp = sc->sc_base.sc_ifnet = if_alloc(IFT_ETHER);
if_setsoftc(ifp, sc);
if_setflags(ifp, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST);
@@ -716,11 +502,12 @@ dtsec_attach(device_t dev)
if_setioctlfn(ifp, dtsec_if_ioctl);
if_setsendqlen(ifp, IFQ_MAXLEN);
if (sc->sc_phy_addr >= 0)
if_initname(ifp, device_get_name(sc->sc_dev),
device_get_unit(sc->sc_dev));
if (sc->sc_base.sc_phy_addr >= 0)
if_initname(ifp, device_get_name(sc->sc_base.sc_dev),
device_get_unit(sc->sc_base.sc_dev));
else
if_initname(ifp, "dtsec_phy", device_get_unit(sc->sc_dev));
if_initname(ifp, "dtsec_phy",
device_get_unit(sc->sc_base.sc_dev));
/* TODO */
#if 0
@@ -732,18 +519,18 @@ dtsec_attach(device_t dev)
if_setcapenable(ifp, if_getcapabilities(ifp));
/* Attach PHY(s) */
error = mii_attach(sc->sc_dev, &sc->sc_mii_dev, ifp, dtsec_ifmedia_upd,
dtsec_ifmedia_sts, BMSR_DEFCAPMASK, sc->sc_phy_addr,
MII_OFFSET_ANY, 0);
error = mii_attach(sc->sc_base.sc_dev, &sc->sc_base.sc_mii_dev,
ifp, dtsec_ifmedia_upd, dtsec_ifmedia_sts, BMSR_DEFCAPMASK,
sc->sc_base.sc_phy_addr, MII_OFFSET_ANY, 0);
if (error) {
device_printf(sc->sc_dev, "attaching PHYs failed: %d\n", error);
dtsec_detach(sc->sc_dev);
device_printf(sc->sc_base.sc_dev,
"attaching PHYs failed: %d\n", error);
dtsec_detach(sc->sc_base.sc_dev);
return (error);
}
sc->sc_mii = device_get_softc(sc->sc_mii_dev);
/* Attach to stack */
ether_ifattach(ifp, sc->sc_mac_addr);
ether_ifattach(ifp, sc->sc_base.sc_mac_addr);
return (0);
}
@@ -755,7 +542,7 @@ dtsec_detach(device_t dev)
if_t ifp;
sc = device_get_softc(dev);
ifp = sc->sc_ifnet;
ifp = sc->sc_base.sc_ifnet;
if (device_is_attached(dev)) {
ether_ifdetach(ifp);
@@ -765,28 +552,23 @@ dtsec_detach(device_t dev)
DTSEC_UNLOCK(sc);
}
if (sc->sc_ifnet) {
if_free(sc->sc_ifnet);
sc->sc_ifnet = NULL;
if (sc->sc_base.sc_ifnet) {
if_free(sc->sc_base.sc_ifnet);
sc->sc_base.sc_ifnet = NULL;
}
if (sc->sc_mode == DTSEC_MODE_REGULAR) {
/* Free RX/TX FQRs */
dtsec_rm_fqr_rx_free(sc);
dtsec_rm_fqr_tx_free(sc);
/* Free RX/TX FQRs */
dpaa_eth_fq_rx_free(&sc->sc_base);
dpaa_eth_fq_tx_free(&sc->sc_base);
/* Free frame info pool */
dtsec_rm_fi_pool_free(sc);
/* Free frame info pool */
dpaa_eth_fi_pool_free(&sc->sc_base);
/* Free RX buffer pool */
dtsec_rm_pool_rx_free(sc);
}
dtsec_fm_mac_free(sc);
dtsec_fm_port_free_both(sc);
/* Free RX buffer pool */
dpaa_eth_pool_rx_free(&sc->sc_base);
/* Destroy lock */
mtx_destroy(&sc->sc_lock);
mtx_destroy(&sc->sc_base.sc_lock);
return (0);
}
@@ -825,7 +607,7 @@ dtsec_miibus_readreg(device_t dev, int phy, int reg)
sc = device_get_softc(dev);
return (MIIBUS_READREG(sc->sc_mdio, phy, reg));
return (MIIBUS_READREG(sc->sc_base.sc_mdio, phy, reg));
}
int
@@ -836,43 +618,49 @@ dtsec_miibus_writereg(device_t dev, int phy, int reg, int value)
sc = device_get_softc(dev);
return (MIIBUS_WRITEREG(sc->sc_mdio, phy, reg, value));
return (MIIBUS_WRITEREG(sc->sc_base.sc_mdio, phy, reg, value));
}
void
dtsec_miibus_statchg(device_t dev)
{
struct dtsec_softc *sc;
e_EnetSpeed speed;
uint32_t reg;
bool duplex;
int error;
int speed;
sc = device_get_softc(dev);
DTSEC_LOCK_ASSERT(sc);
duplex = ((sc->sc_mii->mii_media_active & IFM_GMASK) == IFM_FDX);
duplex = ((sc->sc_base.sc_mii->mii_media_active & IFM_GMASK) == IFM_FDX);
switch (IFM_SUBTYPE(sc->sc_mii->mii_media_active)) {
switch (IFM_SUBTYPE(sc->sc_base.sc_mii->mii_media_active)) {
case IFM_1000_T:
case IFM_1000_SX:
speed = e_ENET_SPEED_1000;
break;
case IFM_100_TX:
speed = e_ENET_SPEED_100;
break;
case IFM_10_T:
speed = e_ENET_SPEED_10;
if (!duplex) {
device_printf(sc->sc_base.sc_dev,
"Only full-duplex supported for 1Gbps speeds");
return;
}
speed = MACCFG_IF_1G;
break;
default:
speed = e_ENET_SPEED_10;
speed = MACCFG_IF_10_100;
}
error = FM_MAC_AdjustLink(sc->sc_mach, speed, duplex);
if (error != E_OK)
device_printf(sc->sc_dev, "error while adjusting MAC speed.\n");
reg = bus_read_4(sc->sc_base.sc_mem, DTSEC_MACCFG2);
reg &= ~(MACCFG_IF_M | MACCFG_FULLDUPLEX);
if (duplex)
reg |= MACCFG_FULLDUPLEX;
reg |= speed;
bus_write_4(sc->sc_base.sc_mem, DTSEC_MACCFG2, reg);
reg = bus_read_4(sc->sc_base.sc_mem, DTSEC_ECNTRL) & ~ECNTRL_R100M;
if (IFM_SUBTYPE(sc->sc_base.sc_mii->mii_media_active) == IFM_100_TX)
reg |= ECNTRL_R100M;
bus_write_4(sc->sc_base.sc_mem, DTSEC_ECNTRL, reg);
}
/** @} */
+6 -90
View File
@@ -34,11 +34,11 @@
#define DTSEC_MODE_REGULAR 0
#define DTSEC_MODE_INDEPENDENT 1
#define DTSEC_LOCK(sc) mtx_lock(&(sc)->sc_lock)
#define DTSEC_UNLOCK(sc) mtx_unlock(&(sc)->sc_lock)
#define DTSEC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_lock, MA_OWNED)
#define DTSEC_MII_LOCK(sc) mtx_lock(&(sc)->sc_mii_lock)
#define DTSEC_MII_UNLOCK(sc) mtx_unlock(&(sc)->sc_mii_lock)
#define DTSEC_LOCK(sc) mtx_lock(&(sc)->sc_base.sc_lock)
#define DTSEC_UNLOCK(sc) mtx_unlock(&(sc)->sc_base.sc_lock)
#define DTSEC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_base.sc_lock, MA_OWNED)
#define DTSEC_MII_LOCK(sc) mtx_lock(&(sc)->sc_base.sc_mii_lock)
#define DTSEC_MII_UNLOCK(sc) mtx_unlock(&(sc)->sc_base.sc_mii_lock)
enum eth_dev_type {
ETH_DTSEC = 0x1,
@@ -46,96 +46,12 @@ enum eth_dev_type {
};
struct dtsec_softc {
/* XXX MII bus requires that struct ifnet is first!!! */
if_t sc_ifnet;
device_t sc_dev;
struct resource *sc_mem;
struct mtx sc_lock;
int sc_mode;
/* Methods */
int (*sc_port_rx_init)
(struct dtsec_softc *sc, int unit);
int (*sc_port_tx_init)
(struct dtsec_softc *sc, int unit);
void (*sc_start_locked)
(struct dtsec_softc *sc);
/* dTSEC data */
struct dpaa_eth_softc sc_base;
enum eth_dev_type sc_eth_dev_type;
uint8_t sc_eth_id; /* Ethernet ID within its frame manager */
uintptr_t sc_mac_mem_offset;
e_EnetMode sc_mac_enet_mode;
int sc_mac_mdio_irq;
uint8_t sc_mac_addr[6];
int sc_port_rx_hw_id;
int sc_port_tx_hw_id;
uint32_t sc_port_tx_qman_chan;
int sc_phy_addr;
bool sc_hidden;
device_t sc_mdio;
/* Params from fman_bus driver */
vm_offset_t sc_fm_base;
t_Handle sc_fmh;
t_Handle sc_muramh;
t_Handle sc_mach;
t_Handle sc_rxph;
t_Handle sc_txph;
/* MII data */
struct mii_data *sc_mii;
device_t sc_mii_dev;
struct mtx sc_mii_lock;
struct callout sc_tick_callout;
/* RX Pool */
t_Handle sc_rx_pool;
uint8_t sc_rx_bpid;
uma_zone_t sc_rx_zone;
char sc_rx_zname[64];
/* RX Frame Queue */
t_Handle sc_rx_fqr;
uint32_t sc_rx_fqid;
/* TX Frame Queue */
t_Handle sc_tx_fqr;
bool sc_tx_fqr_full;
t_Handle sc_tx_conf_fqr;
uint32_t sc_tx_conf_fqid;
/* Frame Info Zone */
uma_zone_t sc_fi_zone;
char sc_fi_zname[64];
};
/** @} */
/**
* @group dTSEC FMan PORT API.
* @{
*/
enum dtsec_fm_port_params {
FM_PORT_LIODN_BASE = 0,
FM_PORT_LIODN_OFFSET = 0,
FM_PORT_MEM_ID = 0,
FM_PORT_MEM_ATTR = MEMORY_ATTR_CACHEABLE,
FM_PORT_BUFFER_SIZE = MCLBYTES,
};
e_FmPortType dtsec_fm_port_rx_type(enum eth_dev_type type);
void dtsec_fm_port_rx_exception_callback(t_Handle app,
e_FmPortExceptions exception);
void dtsec_fm_port_tx_exception_callback(t_Handle app,
e_FmPortExceptions exception);
e_FmPortType dtsec_fm_port_tx_type(enum eth_dev_type type);
/** @} */
/**
* @group dTSEC bus interface.
* @{
+14 -41
View File
@@ -48,9 +48,7 @@
#include "miibus_if.h"
#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h>
#include <contrib/ncsw/inc/xx_ext.h>
#include "dpaa_eth.h"
#include "if_dtsec.h"
#include "fman.h"
@@ -123,7 +121,7 @@ dtsec_fdt_attach(device_t dev)
enet_node = ofw_bus_get_node(dev);
if (OF_getprop(enet_node, "local-mac-address",
(void *)sc->sc_mac_addr, 6) == -1) {
(void *)sc->sc_base.sc_mac_addr, 6) == -1) {
device_printf(dev,
"Could not load local-mac-addr property from DTS\n");
return (ENXIO);
@@ -135,17 +133,17 @@ dtsec_fdt_attach(device_t dev)
else if (ofw_bus_is_compatible(dev, "fsl,fman-xgec") != 0)
sc->sc_eth_dev_type = ETH_10GSEC;
else
return(ENXIO);
return (ENXIO);
/* Get PHY address */
if (OF_getprop(enet_node, "phy-handle", (void *)&phy_node,
if (OF_getprop(enet_node, "tbi-handle", (void *)&phy_node,
sizeof(phy_node)) <= 0)
return (ENXIO);
phy_node = OF_node_from_xref(phy_node);
if (OF_getprop(phy_node, "reg", (void *)&sc->sc_phy_addr,
sizeof(sc->sc_phy_addr)) <= 0)
if (OF_getprop(phy_node, "reg", (void *)&sc->sc_base.sc_phy_addr,
sizeof(sc->sc_base.sc_phy_addr)) <= 0)
return (ENXIO);
phy_dev = OF_device_from_xref(OF_parent(phy_node));
@@ -155,12 +153,12 @@ dtsec_fdt_attach(device_t dev)
return (ENXIO);
}
sc->sc_mdio = phy_dev;
sc->sc_base.sc_mdio = phy_dev;
/* Get MAC memory offset in SoC */
rid = 0;
sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
if (sc->sc_mem == NULL)
sc->sc_base.sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE);
if (sc->sc_base.sc_mem == NULL)
return (ENXIO);
/* Get PHY connection type */
@@ -168,20 +166,10 @@ dtsec_fdt_attach(device_t dev)
sizeof(phy_type)) <= 0)
return (ENXIO);
if (!strcmp(phy_type, "sgmii"))
sc->sc_mac_enet_mode = e_ENET_MODE_SGMII_1000;
else if (!strcmp(phy_type, "rgmii"))
sc->sc_mac_enet_mode = e_ENET_MODE_RGMII_1000;
else if (!strcmp(phy_type, "xgmii"))
/* We set 10 Gigabit mode flag however we don't support it */
sc->sc_mac_enet_mode = e_ENET_MODE_XGMII_10000;
else
return (ENXIO);
if (OF_getencprop(enet_node, "cell-index",
(void *)&mac_id, sizeof(mac_id)) <= 0)
return (ENXIO);
sc->sc_eth_id = mac_id;
sc->sc_base.sc_eth_id = mac_id;
/* Get RX/TX port handles */
if (OF_getprop(enet_node, "fsl,fman-ports", (void *)fman_rxtx_node,
@@ -194,32 +182,17 @@ dtsec_fdt_attach(device_t dev)
if (fman_rxtx_node[1] == 0)
return (ENXIO);
fman_rxtx_node[0] = OF_instance_to_package(fman_rxtx_node[0]);
fman_rxtx_node[1] = OF_instance_to_package(fman_rxtx_node[1]);
sc->sc_base.sc_rx_port = OF_device_from_xref(fman_rxtx_node[0]);
sc->sc_base.sc_tx_port = OF_device_from_xref(fman_rxtx_node[1]);
if (ofw_bus_node_is_compatible(fman_rxtx_node[0],
"fsl,fman-v2-port-rx") == 0)
return (ENXIO);
if (ofw_bus_node_is_compatible(fman_rxtx_node[1],
"fsl,fman-v2-port-tx") == 0)
return (ENXIO);
/* Get RX port HW id */
if (OF_getprop(fman_rxtx_node[0], "reg", (void *)&sc->sc_port_rx_hw_id,
sizeof(sc->sc_port_rx_hw_id)) <= 0)
return (ENXIO);
/* Get TX port HW id */
if (OF_getprop(fman_rxtx_node[1], "reg", (void *)&sc->sc_port_tx_hw_id,
sizeof(sc->sc_port_tx_hw_id)) <= 0)
if (sc->sc_base.sc_rx_port == NULL || sc->sc_base.sc_tx_port == NULL)
return (ENXIO);
if (OF_getprop(fman_rxtx_node[1], "cell-index", &fman_tx_cell,
sizeof(fman_tx_cell)) <= 0)
return (ENXIO);
/* Get QMan channel */
sc->sc_port_tx_qman_chan = fman_qman_channel_id(device_get_parent(dev),
sc->sc_base.sc_port_tx_qman_chan = fman_qman_channel_id(device_get_parent(dev),
fman_tx_cell);
return (dtsec_attach(dev));
-260
View File
@@ -1,260 +0,0 @@
/*-
* 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/socket.h>
#include <sys/sysctl.h>
#include <sys/sockio.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 <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include "miibus_if.h"
#include <contrib/ncsw/inc/integrations/dpaa_integration_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h>
#include <contrib/ncsw/inc/xx_ext.h>
#include "fman.h"
#include "if_dtsec.h"
#include "if_dtsec_im.h"
/**
* @group dTSEC FMan PORT routines.
* @{
*/
static e_RxStoreResponse
dtsec_im_fm_port_rx_callback(t_Handle app, uint8_t *data, uint16_t length,
uint16_t status, uint8_t position, t_Handle buf_context)
{
struct dtsec_softc *sc;
struct mbuf *m;
/* TODO STATUS / Position checking */
sc = app;
m = m_devget(data, length, 0, sc->sc_ifnet, NULL);
if (m)
if_input(sc->sc_ifnet, m);
XX_FreeSmart(data);
return (e_RX_STORE_RESPONSE_CONTINUE);
}
static void
dtsec_im_fm_port_tx_conf_callback(t_Handle app, uint8_t *data, uint16_t status,
t_Handle buf_context)
{
/* TODO: Check status */
XX_FreeSmart(data);
}
static uint8_t *
dtsec_im_fm_port_rx_get_buf(t_Handle buffer_pool, t_Handle *buf_context_handle)
{
struct dtsec_softc *sc;
uint8_t *buffer;
sc = buffer_pool;
buffer = XX_MallocSmart(FM_PORT_BUFFER_SIZE, 0, sizeof(void *));
if (!buffer)
device_printf(sc->sc_dev, "couldn't allocate RX buffer.\n");
return (buffer);
}
static t_Error
dtsec_im_fm_port_rx_put_buf(t_Handle buffer_pool, uint8_t *buffer,
t_Handle buf_context)
{
XX_FreeSmart(buffer);
return (E_OK);
}
int
dtsec_im_fm_port_rx_init(struct dtsec_softc *sc, int unit)
{
t_FmPortParams params;
t_BufferPoolInfo *pool_params;
t_FmPortImRxTxParams *im_params;
t_Error error;
memset(&params, 0, sizeof(params));
params.baseAddr = sc->sc_fm_base + sc->sc_port_rx_hw_id;
params.h_Fm = sc->sc_fmh;
params.portType = dtsec_fm_port_rx_type(sc->sc_eth_dev_type);
params.portId = sc->sc_eth_id;
params.independentModeEnable = TRUE;
params.liodnBase = FM_PORT_LIODN_BASE;
params.f_Exception = dtsec_fm_port_rx_exception_callback;
params.h_App = sc;
im_params = &params.specificParams.imRxTxParams;
im_params->h_FmMuram = sc->sc_muramh;
im_params->liodnOffset = FM_PORT_LIODN_OFFSET;
im_params->dataMemId = FM_PORT_MEM_ID;
im_params->dataMemAttributes = FM_PORT_MEM_ATTR;
im_params->f_RxStore = dtsec_im_fm_port_rx_callback;
pool_params = &params.specificParams.imRxTxParams.rxPoolParams;
pool_params->h_BufferPool = sc;
pool_params->f_GetBuf = dtsec_im_fm_port_rx_get_buf;
pool_params->f_PutBuf = dtsec_im_fm_port_rx_put_buf;
pool_params->bufferSize = FM_PORT_BUFFER_SIZE;
sc->sc_rxph = FM_PORT_Config(&params);
if (sc->sc_rxph == NULL) {
device_printf(sc->sc_dev, "couldn't configure FM Port RX.\n");
return (ENXIO);
}
error = FM_PORT_Init(sc->sc_rxph);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't initialize FM Port RX.\n");
FM_PORT_Free(sc->sc_rxph);
return (ENXIO);
}
if (bootverbose)
device_printf(sc->sc_dev, "RX hw port 0x%02x initialized.\n",
sc->sc_port_rx_hw_id);
return (0);
}
int
dtsec_im_fm_port_tx_init(struct dtsec_softc *sc, int unit)
{
t_FmPortParams params;
t_FmPortImRxTxParams *im_params;
t_Error error;
memset(&params, 0, sizeof(params));
params.baseAddr = sc->sc_fm_base + sc->sc_port_tx_hw_id;
params.h_Fm = sc->sc_fmh;
params.portType = dtsec_fm_port_tx_type(sc->sc_eth_dev_type);
params.portId = unit;
params.independentModeEnable = TRUE;
params.liodnBase = FM_PORT_LIODN_BASE;
params.f_Exception = dtsec_fm_port_tx_exception_callback;
params.h_App = sc;
im_params = &params.specificParams.imRxTxParams;
im_params->h_FmMuram = sc->sc_muramh;
im_params->liodnOffset = FM_PORT_LIODN_OFFSET;
im_params->dataMemId = FM_PORT_MEM_ID;
im_params->dataMemAttributes = FM_PORT_MEM_ATTR;
im_params->f_TxConf = dtsec_im_fm_port_tx_conf_callback;
sc->sc_txph = FM_PORT_Config(&params);
if (sc->sc_txph == NULL) {
device_printf(sc->sc_dev, "couldn't configure FM Port TX.\n");
return (ENXIO);
}
error = FM_PORT_Init(sc->sc_txph);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't initialize FM Port TX.\n");
FM_PORT_Free(sc->sc_txph);
return (ENXIO);
}
if (bootverbose)
device_printf(sc->sc_dev, "TX hw port 0x%02x initialized.\n",
sc->sc_port_tx_hw_id);
return (0);
}
/** @} */
/**
* @group dTSEC IFnet routines.
* @{
*/
void
dtsec_im_if_start_locked(struct dtsec_softc *sc)
{
uint8_t *buffer;
uint16_t length;
struct mbuf *m;
int error;
DTSEC_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)) {
m = if_dequeue(sc->sc_ifnet);
if (m == NULL)
break;
length = m_length(m, NULL);
buffer = XX_MallocSmart(length, 0, sizeof(void *));
if (!buffer) {
m_freem(m);
break;
}
m_copydata(m, 0, length, buffer);
m_freem(m);
error = FM_PORT_ImTx(sc->sc_txph, buffer, length, TRUE, buffer);
if (error != E_OK) {
/* TODO: Ring full */
XX_FreeSmart(buffer);
break;
}
}
}
/** @} */
-39
View File
@@ -1,39 +0,0 @@
/*-
* 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.
*/
#ifndef IF_DTSEC_IM_H_
#define IF_DTSEC_IM_H_
/**
* @group dTSEC Independent Mode API.
* @{
*/
int dtsec_im_fm_port_tx_init(struct dtsec_softc *sc, int unit);
int dtsec_im_fm_port_rx_init(struct dtsec_softc *sc, int unit);
void dtsec_im_if_start_locked(struct dtsec_softc *sc);
/** @} */
#endif /* IF_DTSEC_IM_H_ */
-651
View File
@@ -1,651 +0,0 @@
/*-
* 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/socket.h>
#include <sys/sysctl.h>
#include <sys/sockio.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 <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include "miibus_if.h"
#include <contrib/ncsw/inc/integrations/dpaa_integration_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_mac_ext.h>
#include <contrib/ncsw/inc/Peripherals/fm_port_ext.h>
#include <contrib/ncsw/inc/xx_ext.h>
#include "fman.h"
#include "bman.h"
#include "qman.h"
#include "if_dtsec.h"
#include "if_dtsec_rm.h"
/**
* @group dTSEC RM private defines.
* @{
*/
#define DTSEC_BPOOLS_USED (1)
#define DTSEC_MAX_TX_QUEUE_LEN 256
struct dtsec_rm_frame_info {
struct mbuf *fi_mbuf;
t_DpaaSGTE fi_sgt[DPAA_NUM_OF_SG_TABLE_ENTRY];
};
enum dtsec_rm_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 e_QM_FQ_CHANNEL_POOL1
#define DTSEC_RM_FQR_TX_CONF_CHANNEL e_QM_FQ_CHANNEL_SWPORTAL0
enum dtsec_rm_fqr_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
dtsec_rm_fi_pool_free(struct dtsec_softc *sc)
{
if (sc->sc_fi_zone != NULL)
uma_zdestroy(sc->sc_fi_zone);
}
int
dtsec_rm_fi_pool_init(struct dtsec_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 dtsec_rm_frame_info), NULL, NULL, NULL, NULL,
UMA_ALIGN_PTR, 0);
return (0);
}
static struct dtsec_rm_frame_info *
dtsec_rm_fi_alloc(struct dtsec_softc *sc)
{
struct dtsec_rm_frame_info *fi;
fi = uma_zalloc(sc->sc_fi_zone, M_NOWAIT);
return (fi);
}
static void
dtsec_rm_fi_free(struct dtsec_softc *sc, struct dtsec_rm_frame_info *fi)
{
uma_zfree(sc->sc_fi_zone, fi);
}
/** @} */
/**
* @group dTSEC FMan PORT routines.
* @{
*/
int
dtsec_rm_fm_port_rx_init(struct dtsec_softc *sc, int unit)
{
t_FmPortParams params;
t_FmPortRxParams *rx_params;
t_FmExtPools *pool_params;
t_Error error;
memset(&params, 0, sizeof(params));
params.baseAddr = sc->sc_fm_base + sc->sc_port_rx_hw_id;
params.h_Fm = sc->sc_fmh;
params.portType = dtsec_fm_port_rx_type(sc->sc_eth_dev_type);
params.portId = sc->sc_eth_id;
params.independentModeEnable = false;
params.liodnBase = FM_PORT_LIODN_BASE;
params.f_Exception = dtsec_fm_port_rx_exception_callback;
params.h_App = sc;
rx_params = &params.specificParams.rxParams;
rx_params->errFqid = sc->sc_rx_fqid;
rx_params->dfltFqid = sc->sc_rx_fqid;
rx_params->liodnOffset = 0;
pool_params = &rx_params->extBufPools;
pool_params->numOfPoolsUsed = DTSEC_BPOOLS_USED;
pool_params->extBufPool->id = sc->sc_rx_bpid;
pool_params->extBufPool->size = FM_PORT_BUFFER_SIZE;
sc->sc_rxph = FM_PORT_Config(&params);
if (sc->sc_rxph == NULL) {
device_printf(sc->sc_dev, "couldn't configure FM Port RX.\n");
return (ENXIO);
}
error = FM_PORT_Init(sc->sc_rxph);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't initialize FM Port RX.\n");
FM_PORT_Free(sc->sc_rxph);
return (ENXIO);
}
if (bootverbose)
device_printf(sc->sc_dev, "RX hw port 0x%02x initialized.\n",
sc->sc_port_rx_hw_id);
return (0);
}
int
dtsec_rm_fm_port_tx_init(struct dtsec_softc *sc, int unit)
{
t_FmPortParams params;
t_FmPortNonRxParams *tx_params;
t_Error error;
memset(&params, 0, sizeof(params));
params.baseAddr = sc->sc_fm_base + sc->sc_port_tx_hw_id;
params.h_Fm = sc->sc_fmh;
params.portType = dtsec_fm_port_tx_type(sc->sc_eth_dev_type);
params.portId = sc->sc_eth_id;
params.independentModeEnable = false;
params.liodnBase = FM_PORT_LIODN_BASE;
params.f_Exception = dtsec_fm_port_tx_exception_callback;
params.h_App = sc;
tx_params = &params.specificParams.nonRxParams;
tx_params->errFqid = sc->sc_tx_conf_fqid;
tx_params->dfltFqid = sc->sc_tx_conf_fqid;
tx_params->qmChannel = sc->sc_port_tx_qman_chan;
#ifdef FM_OP_PARTITION_ERRATA_FMANx8
tx_params->opLiodnOffset = 0;
#endif
sc->sc_txph = FM_PORT_Config(&params);
if (sc->sc_txph == NULL) {
device_printf(sc->sc_dev, "couldn't configure FM Port TX.\n");
return (ENXIO);
}
error = FM_PORT_Init(sc->sc_txph);
if (error != E_OK) {
device_printf(sc->sc_dev, "couldn't initialize FM Port TX.\n");
FM_PORT_Free(sc->sc_txph);
return (ENXIO);
}
if (bootverbose)
device_printf(sc->sc_dev, "TX hw port 0x%02x initialized.\n",
sc->sc_port_tx_hw_id);
return (0);
}
/** @} */
/**
* @group dTSEC buffer pools routines.
* @{
*/
static t_Error
dtsec_rm_pool_rx_put_buffer(t_Handle h_BufferPool, uint8_t *buffer,
t_Handle context)
{
struct dtsec_softc *sc;
sc = h_BufferPool;
uma_zfree(sc->sc_rx_zone, buffer);
return (E_OK);
}
static uint8_t *
dtsec_rm_pool_rx_get_buffer(t_Handle h_BufferPool, t_Handle *context)
{
struct dtsec_softc *sc;
uint8_t *buffer;
sc = h_BufferPool;
buffer = uma_zalloc(sc->sc_rx_zone, M_NOWAIT);
return (buffer);
}
static void
dtsec_rm_pool_rx_depleted(t_Handle h_App, bool in)
{
struct dtsec_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;
bman_pool_fill(sc->sc_rx_pool, DTSEC_RM_POOL_RX_HIGH_MARK);
}
}
void
dtsec_rm_pool_rx_free(struct dtsec_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
dtsec_rm_pool_rx_init(struct dtsec_softc *sc)
{
/* FM_PORT_BUFFER_SIZE must be less than PAGE_SIZE */
CTASSERT(FM_PORT_BUFFER_SIZE < 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, FM_PORT_BUFFER_SIZE, NULL,
NULL, NULL, NULL, FM_PORT_BUFFER_SIZE - 1, 0);
sc->sc_rx_pool = bman_pool_create(&sc->sc_rx_bpid, FM_PORT_BUFFER_SIZE,
0, 0, DTSEC_RM_POOL_RX_MAX_SIZE, dtsec_rm_pool_rx_get_buffer,
dtsec_rm_pool_rx_put_buffer, DTSEC_RM_POOL_RX_LOW_MARK,
DTSEC_RM_POOL_RX_HIGH_MARK, 0, 0, dtsec_rm_pool_rx_depleted, sc, NULL,
NULL);
if (sc->sc_rx_pool == NULL) {
device_printf(sc->sc_dev, "NULL rx pool somehow\n");
dtsec_rm_pool_rx_free(sc);
return (EIO);
}
return (0);
}
/** @} */
/**
* @group dTSEC Frame Queue Range routines.
* @{
*/
static void
dtsec_rm_fqr_mext_free(struct mbuf *m)
{
struct dtsec_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, buffer);
else
dtsec_rm_pool_rx_put_buffer(sc, buffer, NULL);
}
static e_RxStoreResponse
dtsec_rm_fqr_rx_callback(t_Handle app, t_Handle fqr, t_Handle portal,
uint32_t fqid_off, t_DpaaFD *frame)
{
struct dtsec_softc *sc;
struct mbuf *m;
void *frame_va;
m = NULL;
sc = app;
frame_va = DPAA_FD_GET_ADDR(frame);
KASSERT(DPAA_FD_GET_FORMAT(frame) == e_DPAA_FD_FORMAT_TYPE_SHORT_SBSF,
("%s(): Got unsupported frame format 0x%02X!", __func__,
DPAA_FD_GET_FORMAT(frame)));
KASSERT(DPAA_FD_GET_OFFSET(frame) == 0,
("%s(): Only offset 0 is supported!", __func__));
if (DPAA_FD_GET_STATUS(frame) != 0) {
device_printf(sc->sc_dev, "RX error: 0x%08X\n",
DPAA_FD_GET_STATUS(frame));
goto err;
}
m = m_gethdr(M_NOWAIT, MT_HEADER);
if (m == NULL)
goto err;
m_extadd(m, frame_va, FM_PORT_BUFFER_SIZE,
dtsec_rm_fqr_mext_free, frame_va, sc, 0,
EXT_NET_DRV);
m->m_pkthdr.rcvif = sc->sc_ifnet;
m->m_len = DPAA_FD_GET_LENGTH(frame);
m_fixhdr(m);
if_input(sc->sc_ifnet, m);
return (e_RX_STORE_RESPONSE_CONTINUE);
err:
bman_put_buffer(sc->sc_rx_pool, frame_va);
if (m != NULL)
m_freem(m);
return (e_RX_STORE_RESPONSE_CONTINUE);
}
static e_RxStoreResponse
dtsec_rm_fqr_tx_confirm_callback(t_Handle app, t_Handle fqr, t_Handle portal,
uint32_t fqid_off, t_DpaaFD *frame)
{
struct dtsec_rm_frame_info *fi;
struct dtsec_softc *sc;
unsigned int qlen;
t_DpaaSGTE *sgt0;
sc = app;
if (DPAA_FD_GET_STATUS(frame) != 0)
device_printf(sc->sc_dev, "TX error: 0x%08X\n",
DPAA_FD_GET_STATUS(frame));
/*
* We are storing struct dtsec_rm_frame_info in first entry
* of scatter-gather table.
*/
sgt0 = DPAA_FD_GET_ADDR(frame);
fi = DPAA_SGTE_GET_ADDR(sgt0);
/* Free transmitted frame */
m_freem(fi->fi_mbuf);
dtsec_rm_fi_free(sc, fi);
qlen = qman_fqr_get_counter(sc->sc_tx_conf_fqr, 0,
e_QM_FQR_COUNTERS_FRAME);
if (qlen == 0) {
DTSEC_LOCK(sc);
if (sc->sc_tx_fqr_full) {
sc->sc_tx_fqr_full = 0;
dtsec_rm_if_start_locked(sc);
}
DTSEC_UNLOCK(sc);
}
return (e_RX_STORE_RESPONSE_CONTINUE);
}
void
dtsec_rm_fqr_rx_free(struct dtsec_softc *sc)
{
if (sc->sc_rx_fqr)
qman_fqr_free(sc->sc_rx_fqr);
}
int
dtsec_rm_fqr_rx_init(struct dtsec_softc *sc)
{
t_Error error;
t_Handle fqr;
/* Default Frame Queue */
fqr = qman_fqr_create(1, DTSEC_RM_FQR_RX_CHANNEL, DTSEC_RM_FQR_RX_WQ,
false, 0, false, false, true, false, 0, 0, 0);
if (fqr == NULL) {
device_printf(sc->sc_dev, "could not create default RX queue"
"\n");
return (EIO);
}
sc->sc_rx_fqr = fqr;
sc->sc_rx_fqid = qman_fqr_get_base_fqid(fqr);
error = qman_fqr_register_cb(fqr, dtsec_rm_fqr_rx_callback, sc);
if (error != E_OK) {
device_printf(sc->sc_dev, "could not register RX callback\n");
dtsec_rm_fqr_rx_free(sc);
return (EIO);
}
return (0);
}
void
dtsec_rm_fqr_tx_free(struct dtsec_softc *sc)
{
if (sc->sc_tx_fqr)
qman_fqr_free(sc->sc_tx_fqr);
if (sc->sc_tx_conf_fqr)
qman_fqr_free(sc->sc_tx_conf_fqr);
}
int
dtsec_rm_fqr_tx_init(struct dtsec_softc *sc)
{
t_Error error;
t_Handle fqr;
/* TX Frame Queue */
fqr = qman_fqr_create(1, sc->sc_port_tx_qman_chan,
DTSEC_RM_FQR_TX_WQ, false, 0, false, false, true, false, 0, 0, 0);
if (fqr == NULL) {
device_printf(sc->sc_dev, "could not create default TX queue"
"\n");
return (EIO);
}
sc->sc_tx_fqr = fqr;
/* TX Confirmation Frame Queue */
fqr = qman_fqr_create(1, DTSEC_RM_FQR_TX_CONF_CHANNEL,
DTSEC_RM_FQR_TX_CONF_WQ, false, 0, false, false, true, false, 0, 0,
0);
if (fqr == NULL) {
device_printf(sc->sc_dev, "could not create TX confirmation "
"queue\n");
dtsec_rm_fqr_tx_free(sc);
return (EIO);
}
sc->sc_tx_conf_fqr = fqr;
sc->sc_tx_conf_fqid = qman_fqr_get_base_fqid(fqr);
error = qman_fqr_register_cb(fqr, dtsec_rm_fqr_tx_confirm_callback, sc);
if (error != E_OK) {
device_printf(sc->sc_dev, "could not register TX confirmation "
"callback\n");
dtsec_rm_fqr_tx_free(sc);
return (EIO);
}
return (0);
}
/** @} */
/**
* @group dTSEC IFnet routines.
* @{
*/
void
dtsec_rm_if_start_locked(struct dtsec_softc *sc)
{
vm_size_t dsize, psize, ssize;
struct dtsec_rm_frame_info *fi;
unsigned int qlen, i;
struct mbuf *m0, *m;
vm_offset_t vaddr;
t_DpaaFD fd;
DTSEC_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_fqr_get_counter(sc->sc_tx_fqr, 0,
e_QM_FQR_COUNTERS_FRAME);
if (qlen >= DTSEC_MAX_TX_QUEUE_LEN) {
sc->sc_tx_fqr_full = 1;
return;
}
fi = dtsec_rm_fi_alloc(sc);
if (fi == NULL)
return;
m0 = if_dequeue(sc->sc_ifnet);
if (m0 == NULL) {
dtsec_rm_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.
*/
DPAA_SGTE_SET_ADDR(&fi->fi_sgt[i], (void *)fi);
DPAA_SGTE_SET_LENGTH(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_EXTENSION(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_FINAL(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_BPID(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_OFFSET(&fi->fi_sgt[i], 0);
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;
DPAA_SGTE_SET_ADDR(&fi->fi_sgt[i],
(void *)vaddr);
DPAA_SGTE_SET_LENGTH(&fi->fi_sgt[i], ssize);
DPAA_SGTE_SET_EXTENSION(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_FINAL(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_BPID(&fi->fi_sgt[i], 0);
DPAA_SGTE_SET_OFFSET(&fi->fi_sgt[i], 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) {
dtsec_rm_fi_free(sc, fi);
m_freem(m0);
continue;
}
DPAA_SGTE_SET_FINAL(&fi->fi_sgt[i-1], 1);
DPAA_FD_SET_ADDR(&fd, fi->fi_sgt);
DPAA_FD_SET_LENGTH(&fd, psize);
DPAA_FD_SET_FORMAT(&fd, e_DPAA_FD_FORMAT_TYPE_SHORT_MBSF);
fd.liodn = 0;
fd.bpid = 0;
fd.elion = 0;
DPAA_FD_SET_OFFSET(&fd, 0);
DPAA_FD_SET_STATUS(&fd, 0);
DTSEC_UNLOCK(sc);
if (qman_fqr_enqueue(sc->sc_tx_fqr, 0, &fd) != E_OK) {
dtsec_rm_fi_free(sc, fi);
m_freem(m0);
}
DTSEC_LOCK(sc);
}
}
/** @} */
+802
View File
@@ -0,0 +1,802 @@
/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#include <sys/mbuf.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/socket.h>
#include <sys/sysctl.h>
#include <sys/sockio.h>
#include <machine/bus.h>
#include <machine/resource.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 <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
#include "miibus_if.h"
#include "dpaa_eth.h"
#include "fman.h"
#include "fman_port.h"
#include "if_memac.h"
#include "fman_if.h"
#include "fman_port_if.h"
#define MEMAC_MIN_FRAME_SIZE 64
#define MEMAC_MAX_FRAME_SIZE 32736
#define MEMAC_COMMAND_CONFIG 0x008
#define COMMAND_CONFIG_RXSTP 0x20000000
#define COMMAND_CONFIG_NO_LEN_CHK 0x00020000
#define COMMAND_CONFIG_SWR 0x00001000
#define COMMAND_CONFIG_TXP 0x00000800
#define COMMAND_CONFIG_CRC 0x00000040
#define COMMAND_CONFIG_PROMISC 0x00000010
#define COMMAND_CONFIG_RX_EN 0x00000002
#define COMMAND_CONFIG_TX_EN 0x00000001
#define MEMAC_MAC_ADDR_0 0x00c
#define MEMAC_MAC_ADDR_1 0x010
#define MEMAC_REG_MAXFRM 0x14
#define MEMAC_REG_TX_FIFO_SECTIONS 0x020
#define TX_FIFO_SECTIONS_TX_EMPTY_M 0xffff0000
#define TX_FIFO_SECTIONS_TX_EMPTY_S 16
#define TX_FIFO_SECTIONS_TX_AVAIL_M 0x0000ffff
#define HASHTABLE_CTRL 0x02c
#define CTRL_MCAST 0x00000100
#define CTRL_HASH_ADDR_M 0x0000003f
#define HASHTABLE_SIZE 64
#define MEMAC_IEVENT 0x040
#define IEVENT_RX_EMPTY 0x00000040
#define IEVENT_TX_EMPTY 0x00000020
#define MEMAC_CL01_PAUSE_QUANTA 0x054
#define MEMAC_IF_MODE 0x300
#define IF_MODE_ENA 0x00008000
#define IF_MODE_SSP_M 0x00006000
#define IF_MODE_SSP_100MB 0x00000000
#define IF_MODE_SSP_10MB 0x00002000
#define IF_MODE_SSP_1GB 0x00004000
#define IF_MODE_SFD 0x00001000
#define IF_MODE_MSG 0x00000200
#define IF_MODE_HG 0x00000100
#define IF_MODE_HD 0x00000040
#define IF_MODE_RLP 0x00000020
#define IF_MODE_RG 0x00000004
#define IF_MODE_IFMODE_M 0x00000003
#define IF_MODE_IFMODE_XGMII 0x00000000
#define IF_MODE_IFMODE_MII 0x00000001
#define IF_MODE_IFMODE_GMII 0x00000002
#define DEFAULT_PAUSE_QUANTA 0xf000
/**
* @group FMan MAC routines.
* @{
*/
#define MEMAC_MAC_EXCEPTIONS_END (-1)
static void memac_if_init_locked(struct memac_softc *sc);
static int
memac_fm_mac_init(struct memac_softc *sc, uint8_t *mac)
{
uint32_t reg;
FMAN_GET_REVISION(device_get_parent(sc->sc_base.sc_dev), &sc->sc_base.sc_rev_major,
&sc->sc_base.sc_rev_minor);
if (FMAN_RESET_MAC(device_get_parent(sc->sc_base.sc_dev), sc->sc_base.sc_eth_id) != 0)
return (ENXIO);
reg = bus_read_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG);
reg |= COMMAND_CONFIG_SWR;
bus_write_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG, reg);
while (bus_read_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG) & COMMAND_CONFIG_SWR)
;
/* TODO: TX_FIFO_SECTIONS */
/* TODO: CL01 pause quantum */
bus_write_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG,
COMMAND_CONFIG_NO_LEN_CHK | COMMAND_CONFIG_TXP | COMMAND_CONFIG_CRC);
reg = bus_read_4(sc->sc_base.sc_mem, MEMAC_IF_MODE);
reg &= ~(IF_MODE_IFMODE_M | IF_MODE_RG);
switch (sc->sc_base.sc_mac_enet_mode) {
case MII_CONTYPE_RGMII:
reg |= IF_MODE_RG;
/* FALLTHROUGH */
case MII_CONTYPE_GMII:
case MII_CONTYPE_SGMII:
case MII_CONTYPE_QSGMII:
reg |= IF_MODE_IFMODE_GMII;
break;
case MII_CONTYPE_RMII:
reg |= IF_MODE_RG;
/* FALLTHROUGH */
case MII_CONTYPE_MII:
reg |= IF_MODE_IFMODE_MII;
break;
}
bus_write_4(sc->sc_base.sc_mem, MEMAC_IF_MODE, reg);
return (0);
}
/** @} */
/**
* @group IFnet routines.
* @{
*/
static int
memac_set_mtu(struct memac_softc *sc, unsigned int mtu)
{
mtu += ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN + ETHER_CRC_LEN;
MEMAC_LOCK_ASSERT(sc);
if (mtu >= MEMAC_MIN_FRAME_SIZE && mtu <= MEMAC_MAX_FRAME_SIZE) {
bus_write_4(sc->sc_base.sc_mem, MEMAC_REG_MAXFRM, mtu);
return (mtu);
}
return (0);
}
static u_int
memac_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
{
struct memac_softc *sc = arg;
uint8_t *addr = LLADDR(sdl);
uint32_t hash = 0;
uint8_t a, h;
/* Hash is 6 bits, composed if [XOR{47:40},XOR{39:32},....] */
for (int i = 0; i < 6; i++) {
a = addr[i];
h = 0;
for (int j = 0; j < 8; j++, a >>= 1) {
h ^= (a & 0x1);
}
hash |= (h << i);
}
bus_write_4(sc->sc_base.sc_mem, HASHTABLE_CTRL, hash | CTRL_MCAST);
return (1);
}
static void
memac_setup_multicast(struct memac_softc *sc)
{
if (if_getflags(sc->sc_base.sc_ifnet) & IFF_ALLMULTI) {
for (int i = 0; i < HASHTABLE_SIZE; i++)
bus_write_4(sc->sc_base.sc_mem,
HASHTABLE_CTRL, CTRL_MCAST | i);
} else {
/* Clear the hash table */
for (int i = 0; i < HASHTABLE_SIZE; i++)
bus_write_4(sc->sc_base.sc_mem,
HASHTABLE_CTRL, i);
}
if_foreach_llmaddr(sc->sc_base.sc_ifnet, memac_hash_maddr, sc);
}
static void
memac_setup_promisc(struct memac_softc *sc)
{
uint32_t reg;
reg = bus_read_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG);
reg &= ~COMMAND_CONFIG_PROMISC;
if ((if_getflags(sc->sc_base.sc_ifnet) & IFF_PROMISC) != 0)
bus_write_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG,
reg | COMMAND_CONFIG_PROMISC);
}
static void
memac_if_graceful_stop(struct memac_softc *sc)
{
struct resource *regs = sc->sc_base.sc_mem;
uint32_t reg;
reg = bus_read_4(regs, MEMAC_COMMAND_CONFIG);
reg |= COMMAND_CONFIG_RXSTP;
bus_write_4(regs, MEMAC_COMMAND_CONFIG, reg);
while ((bus_read_4(regs, MEMAC_IEVENT) & IEVENT_RX_EMPTY) == 0)
;
reg &= COMMAND_CONFIG_RX_EN;
bus_write_4(regs, MEMAC_COMMAND_CONFIG, reg);
while ((bus_read_4(regs, MEMAC_IEVENT) & IEVENT_TX_EMPTY) == 0)
;
bus_write_4(regs, MEMAC_COMMAND_CONFIG, reg & ~COMMAND_CONFIG_TX_EN);
}
static void
memac_mac_enable(struct memac_softc *sc)
{
uint32_t reg = bus_read_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG);
reg |= (COMMAND_CONFIG_RX_EN | COMMAND_CONFIG_TX_EN);
bus_write_4(sc->sc_base.sc_mem, MEMAC_COMMAND_CONFIG, reg);
}
static int
memac_if_enable_locked(struct memac_softc *sc)
{
int error;
MEMAC_LOCK_ASSERT(sc);
memac_set_mtu(sc, if_getmtu(sc->sc_base.sc_ifnet));
memac_mac_enable(sc);
error = FMAN_PORT_ENABLE(sc->sc_base.sc_rx_port);
if (error != 0)
return (EIO);
error = FMAN_PORT_ENABLE(sc->sc_base.sc_tx_port);
if (error != 0)
return (EIO);
bus_write_4(sc->sc_base.sc_mem, MEMAC_IEVENT, 0);
memac_setup_multicast(sc);
memac_setup_promisc(sc);
if_setdrvflagbits(sc->sc_base.sc_ifnet, IFF_DRV_RUNNING, 0);
/* Refresh link state */
memac_miibus_statchg(sc->sc_base.sc_dev);
return (0);
}
static int
memac_if_disable_locked(struct memac_softc *sc)
{
int error;
MEMAC_LOCK_ASSERT(sc);
error = FMAN_PORT_DISABLE(sc->sc_base.sc_tx_port);
if (error != 0)
return (EIO);
memac_if_graceful_stop(sc);
error = FMAN_PORT_DISABLE(sc->sc_base.sc_rx_port);
if (error != 0)
return (EIO);
if_setdrvflagbits(sc->sc_base.sc_ifnet, 0, IFF_DRV_RUNNING);
return (0);
}
static int
memac_if_ioctl(if_t ifp, u_long command, caddr_t data)
{
struct memac_softc *sc;
struct ifreq *ifr;
int error;
sc = if_getsoftc(ifp);
ifr = (struct ifreq *)data;
error = 0;
/* Basic functionality to achieve media status reports */
switch (command) {
case SIOCSIFMTU:
MEMAC_LOCK(sc);
if (memac_set_mtu(sc, ifr->ifr_mtu))
if_setmtu(ifp, ifr->ifr_mtu);
else
error = EINVAL;
MEMAC_UNLOCK(sc);
break;
case SIOCSIFFLAGS:
MEMAC_LOCK(sc);
if (if_getflags(ifp) & IFF_UP) {
if (!(if_getdrvflags(ifp) & IFF_DRV_RUNNING))
memac_if_init_locked(sc);
} else if (if_getdrvflags(ifp) & IFF_DRV_RUNNING)
error = memac_if_disable_locked(sc);
MEMAC_UNLOCK(sc);
break;
case SIOCADDMULTI:
case SIOCDELMULTI:
if (if_getflags(sc->sc_base.sc_ifnet) & IFF_UP) {
MEMAC_LOCK(sc);
memac_setup_multicast(sc);
MEMAC_UNLOCK(sc);
}
break;
case SIOCGIFMEDIA:
case SIOCSIFMEDIA:
error = ifmedia_ioctl(ifp, ifr, &sc->sc_base.sc_mii->mii_media,
command);
break;
default:
error = ether_ioctl(ifp, command, data);
}
return (error);
}
static void
memac_if_tick(void *arg)
{
struct memac_softc *sc;
sc = arg;
/* TODO */
MEMAC_LOCK(sc);
mii_tick(sc->sc_base.sc_mii);
callout_reset(&sc->sc_base.sc_tick_callout, hz, memac_if_tick, sc);
MEMAC_UNLOCK(sc);
}
static void
memac_if_deinit_locked(struct memac_softc *sc)
{
MEMAC_LOCK_ASSERT(sc);
MEMAC_UNLOCK(sc);
callout_drain(&sc->sc_base.sc_tick_callout);
MEMAC_LOCK(sc);
}
static void
memac_if_set_macaddr(struct memac_softc *sc, const char *addr)
{
uint32_t reg;
reg = (addr[3] << 24) | (addr[2] << 16) | (addr[1] << 8) | addr[0];
bus_write_4(sc->sc_base.sc_mem, MEMAC_MAC_ADDR_0, reg);
reg = (addr[5] << 8) | (addr[4]);
bus_write_4(sc->sc_base.sc_mem, MEMAC_MAC_ADDR_1, reg);
}
static void
memac_if_init_locked(struct memac_softc *sc)
{
int error;
const char *macaddr;
MEMAC_LOCK_ASSERT(sc);
macaddr = if_getlladdr(sc->sc_base.sc_ifnet);
memac_if_set_macaddr(sc, macaddr);
/* Start MII polling */
if (sc->sc_base.sc_mii)
callout_reset(&sc->sc_base.sc_tick_callout, hz,
memac_if_tick, sc);
if (if_getflags(sc->sc_base.sc_ifnet) & IFF_UP) {
error = memac_if_enable_locked(sc);
if (error != 0)
goto err;
} else {
error = memac_if_disable_locked(sc);
if (error != 0)
goto err;
}
if_link_state_change(sc->sc_base.sc_ifnet, LINK_STATE_UP);
bus_write_4(sc->sc_base.sc_mem, MEMAC_CL01_PAUSE_QUANTA,
DEFAULT_PAUSE_QUANTA);
return;
err:
memac_if_deinit_locked(sc);
device_printf(sc->sc_base.sc_dev, "initialization error.\n");
return;
}
static void
memac_if_init(void *data)
{
struct memac_softc *sc;
sc = data;
MEMAC_LOCK(sc);
memac_if_init_locked(sc);
MEMAC_UNLOCK(sc);
}
static void
memac_if_start(if_t ifp)
{
struct memac_softc *sc;
sc = if_getsoftc(ifp);
MEMAC_LOCK(sc);
dpaa_eth_if_start_locked(&sc->sc_base);
MEMAC_UNLOCK(sc);
}
static void
memac_if_watchdog(if_t ifp)
{
/* TODO */
}
/** @} */
/**
* @group IFmedia routines.
* @{
*/
static int
memac_ifmedia_upd(if_t ifp)
{
struct memac_softc *sc = if_getsoftc(ifp);
return (0);
MEMAC_LOCK(sc);
mii_mediachg(sc->sc_base.sc_mii);
MEMAC_UNLOCK(sc);
return (0);
}
static void
memac_ifmedia_fixed_sts(if_t ifp, struct ifmediareq *ifmr)
{
struct memac_softc *sc = if_getsoftc(ifp);
MEMAC_LOCK(sc);
ifmr->ifm_count = 1;
ifmr->ifm_mask = 0;
ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE;
ifmr->ifm_current = ifmr->ifm_active =
sc->sc_base.sc_mii->mii_media.ifm_cur->ifm_media;
ifmr->ifm_active = ifmr->ifm_current;
/*
* In non-PHY usecases, we need to signal link state up, otherwise
* certain things requiring a link event (e.g async DHCP client) from
* devd do not happen.
*/
if (if_getlinkstate(ifp) == LINK_STATE_UNKNOWN) {
if_link_state_change(ifp, LINK_STATE_UP);
}
/* We assume the link is static, as in a peer switch. */
MEMAC_UNLOCK(sc);
return;
}
static void
memac_ifmedia_sts(if_t ifp, struct ifmediareq *ifmr)
{
struct memac_softc *sc = if_getsoftc(ifp);
MEMAC_LOCK(sc);
mii_pollstat(sc->sc_base.sc_mii);
ifmr->ifm_active = sc->sc_base.sc_mii->mii_media_active;
ifmr->ifm_status = sc->sc_base.sc_mii->mii_media_status;
MEMAC_UNLOCK(sc);
}
/** @} */
/**
* @group dTSEC bus interface.
* @{
*/
int
memac_attach(device_t dev)
{
struct memac_softc *sc;
int error;
if_t ifp;
sc = device_get_softc(dev);
sc->sc_base.sc_dev = dev;
/* Init locks */
mtx_init(&sc->sc_base.sc_lock, device_get_nameunit(dev),
"mEMAC Global Lock", MTX_DEF);
mtx_init(&sc->sc_base.sc_mii_lock, device_get_nameunit(dev),
"mEMAC MII Lock", MTX_DEF);
/* Init callouts */
callout_init(&sc->sc_base.sc_tick_callout, CALLOUT_MPSAFE);
/* Create RX buffer pool */
error = dpaa_eth_pool_rx_init(&sc->sc_base);
if (error != 0)
return (EIO);
/* Create RX frame queue range */
error = dpaa_eth_fq_rx_init(&sc->sc_base);
if (error != 0)
return (EIO);
/* Create frame info pool */
error = dpaa_eth_fi_pool_init(&sc->sc_base);
if (error != 0)
return (EIO);
/* Create TX frame queue range */
error = dpaa_eth_fq_tx_init(&sc->sc_base);
if (error != 0)
return (EIO);
/* Init FMan MAC module. */
error = memac_fm_mac_init(sc, sc->sc_base.sc_mac_addr);
if (error != 0) {
memac_detach(dev);
return (ENXIO);
}
dpaa_eth_fm_port_rx_init(&sc->sc_base);
dpaa_eth_fm_port_tx_init(&sc->sc_base);
/* Create network interface for upper layers */
ifp = sc->sc_base.sc_ifnet = if_alloc(IFT_ETHER);
if_setsoftc(ifp, sc);
if_setflags(ifp, IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST);
if_setinitfn(ifp, memac_if_init);
if_setstartfn(ifp, memac_if_start);
if_setioctlfn(ifp, memac_if_ioctl);
if_setsendqlen(ifp, IFQ_MAXLEN);
if_setsendqready(ifp);
if (sc->sc_base.sc_phy_addr >= 0)
if_initname(ifp, device_get_name(sc->sc_base.sc_dev),
device_get_unit(sc->sc_base.sc_dev));
else
if_initname(ifp, "memac_phy",
device_get_unit(sc->sc_base.sc_dev));
if_setcapabilities(ifp, IFCAP_VLAN_MTU);
if_setcapenable(ifp, if_getcapabilities(ifp));
/* Attach PHY(s) */
if (!sc->sc_fixed_link) {
error = mii_attach(sc->sc_base.sc_dev, &sc->sc_base.sc_mii_dev,
ifp, memac_ifmedia_upd, memac_ifmedia_sts, BMSR_DEFCAPMASK,
sc->sc_base.sc_phy_addr, MII_OFFSET_ANY, 0);
if (error) {
device_printf(sc->sc_base.sc_dev,
"attaching PHYs failed: %d\n", error);
memac_detach(sc->sc_base.sc_dev);
return (error);
}
sc->sc_base.sc_mii = device_get_softc(sc->sc_base.sc_mii_dev);
} else {
phandle_t node;
uint32_t type = IFM_ETHER;
uint32_t speed;
node = ofw_bus_find_child(ofw_bus_get_node(dev), "fixed-link");
if (OF_getencprop(node, "speed", &speed, sizeof(speed)) <= 0) {
device_printf(dev,
"fixed link has no speed property\n");
memac_detach(sc->sc_base.sc_dev);
return (ENXIO);
}
switch (speed) {
case 10:
type |= IFM_10_T;
break;
case 100:
type |= IFM_100_TX;
break;
case 1000:
type |= IFM_1000_T;
break;
case 2500:
type |= IFM_2500_T;
break;
case 5000:
type |= IFM_5000_T;
break;
case 10000:
type |= IFM_10G_T;
break;
}
if (OF_hasprop(node, "full-duplex"))
type |= IFM_FDX;
sc->sc_base.sc_mii = malloc(sizeof(*sc->sc_base.sc_mii),
M_DEVBUF, M_WAITOK | M_ZERO);
ifmedia_init(&sc->sc_base.sc_mii->mii_media, 0,
memac_ifmedia_upd, memac_ifmedia_fixed_sts);
ifmedia_add(&sc->sc_base.sc_mii->mii_media, type, 0, NULL);
ifmedia_set(&sc->sc_base.sc_mii->mii_media, type);
}
/* Attach to stack */
ether_ifattach(ifp, sc->sc_base.sc_mac_addr);
return (0);
}
int
memac_detach(device_t dev)
{
struct memac_softc *sc;
if_t ifp;
sc = device_get_softc(dev);
ifp = sc->sc_base.sc_ifnet;
if (device_is_attached(dev)) {
ether_ifdetach(ifp);
/* Shutdown interface */
MEMAC_LOCK(sc);
memac_if_deinit_locked(sc);
MEMAC_UNLOCK(sc);
}
if (sc->sc_base.sc_ifnet) {
if_free(sc->sc_base.sc_ifnet);
sc->sc_base.sc_ifnet = NULL;
}
/* Free RX/TX FQRs */
dpaa_eth_fq_rx_free(&sc->sc_base);
dpaa_eth_fq_tx_free(&sc->sc_base);
/* Free frame info pool */
dpaa_eth_fi_pool_free(&sc->sc_base);
/* Free RX buffer pool */
dpaa_eth_pool_rx_free(&sc->sc_base);
/* Destroy lock */
mtx_destroy(&sc->sc_base.sc_lock);
return (0);
}
int
memac_suspend(device_t dev)
{
return (0);
}
int
memac_resume(device_t dev)
{
return (0);
}
int
memac_shutdown(device_t dev)
{
return (0);
}
/** @} */
/**
* @group MII bus interface.
* @{
*/
int
memac_miibus_readreg(device_t dev, int phy, int reg)
{
struct memac_softc *sc;
sc = device_get_softc(dev);
return (MIIBUS_READREG(sc->sc_base.sc_mdio, phy, reg));
}
int
memac_miibus_writereg(device_t dev, int phy, int reg, int value)
{
struct memac_softc *sc;
sc = device_get_softc(dev);
return (MIIBUS_WRITEREG(sc->sc_base.sc_mdio, phy, reg, value));
}
void
memac_miibus_statchg(device_t dev)
{
struct memac_softc *sc;
uint32_t reg;
bool duplex;
int speed;
sc = device_get_softc(dev);
MEMAC_LOCK_ASSERT(sc);
duplex = ((sc->sc_base.sc_mii->mii_media_active & IFM_GMASK) == IFM_FDX);
switch (IFM_SUBTYPE(sc->sc_base.sc_mii->mii_media_active)) {
case IFM_AUTO:
speed = IF_MODE_ENA;
break;
case IFM_1000_T:
case IFM_1000_SX:
if (!duplex) {
device_printf(sc->sc_base.sc_dev,
"Only full-duplex supported for 1Gbps speeds");
return;
}
speed = IF_MODE_SSP_1GB;
break;
case IFM_100_TX:
speed = IF_MODE_SSP_100MB;
break;
default:
speed = IF_MODE_SSP_10MB;
break;
}
reg = bus_read_4(sc->sc_base.sc_mem, MEMAC_IF_MODE);
reg &= ~(IF_MODE_ENA | IF_MODE_SSP_M | IF_MODE_SFD);
reg |= 0x2;
if (duplex)
reg |= IF_MODE_SFD;
else
reg |= IF_MODE_HD;
reg |= speed;
bus_write_4(sc->sc_base.sc_mem, MEMAC_IF_MODE, reg);
}
/** @} */
@@ -1,5 +1,5 @@
/*-
* Copyright (c) 2012 Semihalf.
* Copyright (c) 2011-2012 Semihalf.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,28 +24,46 @@
* SUCH DAMAGE.
*/
#ifndef IF_DTSEC_RM_H_
#define IF_DTSEC_RM_H_
#ifndef IF_MEMAC_H_
#define IF_MEMAC_H_
/**
* @group dTSEC Regular Mode API.
* @group dTSEC common API.
* @{
*/
int dtsec_rm_fm_port_rx_init(struct dtsec_softc *sc, int unit);
int dtsec_rm_fm_port_tx_init(struct dtsec_softc *sc, int unit);
#define MEMAC_MODE_REGULAR 0
void dtsec_rm_if_start_locked(struct dtsec_softc *sc);
#define MEMAC_LOCK(sc) mtx_lock(&(sc)->sc_base.sc_lock)
#define MEMAC_UNLOCK(sc) mtx_unlock(&(sc)->sc_base.sc_lock)
#define MEMAC_LOCK_ASSERT(sc) mtx_assert(&(sc)->sc_base.sc_lock, MA_OWNED)
#define MEMAC_MII_LOCK(sc) mtx_lock(&(sc)->sc_base.sc_mii_lock)
#define MEMAC_MII_UNLOCK(sc) mtx_unlock(&(sc)->sc_base.sc_mii_lock)
int dtsec_rm_pool_rx_init(struct dtsec_softc *sc);
void dtsec_rm_pool_rx_free(struct dtsec_softc *sc);
enum eth_dev_type {
ETH_MEMAC = 0x1,
ETH_10GSEC = 0x2
};
int dtsec_rm_fi_pool_init(struct dtsec_softc *sc);
void dtsec_rm_fi_pool_free(struct dtsec_softc *sc);
int dtsec_rm_fqr_rx_init(struct dtsec_softc *sc);
int dtsec_rm_fqr_tx_init(struct dtsec_softc *sc);
void dtsec_rm_fqr_rx_free(struct dtsec_softc *sc);
void dtsec_rm_fqr_tx_free(struct dtsec_softc *sc);
struct memac_softc {
struct dpaa_eth_softc sc_base;
bool sc_fixed_link;
};
/** @} */
#endif /* IF_DTSEC_RM_H_ */
/**
* @group dTSEC bus interface.
* @{
*/
int memac_attach(device_t dev);
int memac_detach(device_t dev);
int memac_suspend(device_t dev);
int memac_resume(device_t dev);
int memac_shutdown(device_t dev);
int memac_miibus_readreg(device_t dev, int phy, int reg);
int memac_miibus_writereg(device_t dev, int phy, int reg,
int value);
void memac_miibus_statchg(device_t dev);
/** @} */
#endif /* IF_MEMAC_H_ */
+190
View File
@@ -0,0 +1,190 @@
/*-
* 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/bus.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/rman.h>
#include <sys/socket.h>
#include <machine/bus.h>
#include <powerpc/mpc85xx/mpc85xx.h>
#include <net/if.h>
#include <net/if_media.h>
#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>
#include <dev/mii/mii_fdt.h>
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/ofw/openfirm.h>
#include "miibus_if.h"
#include "dpaa_eth.h"
#include "if_memac.h"
#include "fman.h"
static int memac_fdt_probe(device_t dev);
static int memac_fdt_attach(device_t dev);
static device_method_t memac_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, memac_fdt_probe),
DEVMETHOD(device_attach, memac_fdt_attach),
DEVMETHOD(device_detach, memac_detach),
DEVMETHOD(device_shutdown, memac_shutdown),
DEVMETHOD(device_suspend, memac_suspend),
DEVMETHOD(device_resume, memac_resume),
/* Bus interface */
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
/* MII interface */
DEVMETHOD(miibus_readreg, memac_miibus_readreg),
DEVMETHOD(miibus_writereg, memac_miibus_writereg),
DEVMETHOD(miibus_statchg, memac_miibus_statchg),
DEVMETHOD_END
};
DEFINE_CLASS_0(memac, memac_driver, memac_methods, sizeof(struct memac_softc));
DRIVER_MODULE(memac, fman, memac_driver, 0, 0);
DRIVER_MODULE(miibus, memac, miibus_driver, 0, 0);
MODULE_DEPEND(memac, ether, 1, 1, 1);
MODULE_DEPEND(memac, miibus, 1, 1, 1);
static int
memac_fdt_probe(device_t dev)
{
if (!ofw_bus_status_okay(dev))
return (ENXIO);
if (!ofw_bus_is_compatible(dev, "fsl,fman-memac"))
return (ENXIO);
device_set_desc(dev,
"Freescale Multirate Ethernet Media Access Controller");
return (BUS_PROBE_DEFAULT);
}
static int
memac_fdt_attach(device_t dev)
{
struct memac_softc *sc;
device_t phy_dev;
phandle_t enet_node, phy_node;
phandle_t fman_rxtx_node[2];
pcell_t fman_tx_cell, mac_id;
sc = device_get_softc(dev);
enet_node = ofw_bus_get_node(dev);
if (OF_getprop(enet_node, "local-mac-address",
(void *)sc->sc_base.sc_mac_addr, 6) == -1) {
device_printf(dev,
"Could not load local-mac-addr property from DTS\n");
return (ENXIO);
}
/* Get PHY connection type */
sc->sc_base.sc_mac_enet_mode = mii_fdt_get_contype(enet_node);
sc->sc_fixed_link = OF_hasprop(enet_node, "fixed-link") ||
(ofw_bus_find_child(enet_node, "fixed-link") != 0);
if (!sc->sc_fixed_link) {
OF_getprop(enet_node, "phy-handle", &phy_node, sizeof(phy_node));
phy_node = OF_node_from_xref(phy_node);
if (OF_getencprop(phy_node, "reg", (void *)&sc->sc_base.sc_phy_addr,
sizeof(sc->sc_base.sc_phy_addr)) <= 0)
return (ENXIO);
phy_dev = OF_device_from_xref(OF_xref_from_node(OF_parent(phy_node)));
if (phy_dev == NULL) {
device_printf(dev, "No PHY found.\n");
return (ENXIO);
}
sc->sc_base.sc_mdio = phy_dev;
}
/* Get MAC memory offset in SoC */
sc->sc_base.sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0, RF_ACTIVE);
if (sc->sc_base.sc_mem == NULL)
return (ENXIO);
sc->sc_base.sc_mac_enet_mode = mii_fdt_get_contype(enet_node);
if (sc->sc_base.sc_mac_enet_mode == MII_CONTYPE_UNKNOWN) {
device_printf(dev, "unknown MII type, defaulting to SGMII\n");
sc->sc_base.sc_mac_enet_mode = MII_CONTYPE_SGMII;
}
if (OF_getencprop(enet_node, "cell-index",
(void *)&mac_id, sizeof(mac_id)) <= 0)
return (ENXIO);
sc->sc_base.sc_eth_id = mac_id;
/* Get RX/TX port handles */
if (OF_getencprop(enet_node, "fsl,fman-ports", (void *)fman_rxtx_node,
sizeof(fman_rxtx_node)) <= 0)
return (ENXIO);
if (fman_rxtx_node[0] == 0)
return (ENXIO);
if (fman_rxtx_node[1] == 0)
return (ENXIO);
sc->sc_base.sc_rx_port = OF_device_from_xref(fman_rxtx_node[0]);
sc->sc_base.sc_tx_port = OF_device_from_xref(fman_rxtx_node[1]);
if (sc->sc_base.sc_rx_port == NULL || sc->sc_base.sc_tx_port == NULL)
return (ENXIO);
fman_rxtx_node[1] = OF_node_from_xref(fman_rxtx_node[1]);
if (OF_getencprop(fman_rxtx_node[1], "cell-index", &fman_tx_cell,
sizeof(fman_tx_cell)) <= 0)
return (ENXIO);
/* Get QMan channel */
sc->sc_base.sc_port_tx_qman_chan = fman_qman_channel_id(device_get_parent(dev),
fman_tx_cell);
return (memac_attach(dev));
}
+21 -30
View File
@@ -24,39 +24,30 @@
* SUCH DAMAGE.
*/
typedef struct dpaa_portal {
int dp_irid; /* interrupt rid */
struct resource *dp_ires; /* interrupt resource */
#ifndef DPAA_PORTALS_H
#define DPAA_PORTALS_H
bool dp_regs_mapped; /* register mapping status */
t_Handle dp_ph; /* portal's handle */
vm_paddr_t dp_ce_pa; /* portal's CE area PA */
vm_paddr_t dp_ci_pa; /* portal's CI area PA */
uint32_t dp_ce_size; /* portal's CE area size */
uint32_t dp_ci_size; /* portal's CI area size */
uintptr_t dp_intr_num; /* portal's intr. number */
} dpaa_portal_t;
struct dpaa_portals_softc {
struct dpaa_portal_softc {
device_t sc_dev; /* device handle */
vm_paddr_t sc_dp_pa; /* portal's PA */
uint32_t sc_dp_size; /* portal's size */
int sc_rrid[2]; /* memory rid */
struct resource *sc_rres[2]; /* memory resource */
dpaa_portal_t sc_dp[MAXCPU];
vm_paddr_t sc_ce_pa; /* portal's CE PA */
vm_offset_t sc_ce_va;
vm_paddr_t sc_ci_pa; /* portal's CI PA */
vm_offset_t sc_ci_va;
int sc_cpu;
uint32_t sc_ce_size; /* portal's CE size */
uint32_t sc_ci_size; /* portal's CI size */
struct resource *sc_mres[2]; /* memory resource */
struct resource *sc_ires; /* Interrupt */
void *sc_intr_cookie;
bool sc_regs_mapped; /* register mapping status */
};
struct dpaa_portals_devinfo {
struct resource_list di_res;
int di_intr_rid;
};
int bman_portal_attach(device_t, int);
int bman_portal_detach(device_t);
int bman_portals_attach(device_t);
int bman_portals_detach(device_t);
int qman_portal_attach(device_t, int);
int qman_portal_detach(device_t);
int qman_portals_attach(device_t);
int qman_portals_detach(device_t);
int dpaa_portal_alloc_res(device_t, struct dpaa_portals_devinfo *, int);
void dpaa_portal_map_registers(struct dpaa_portals_softc *);
int dpaa_portal_alloc_res(device_t, int);
void dpaa_portal_map_registers(struct dpaa_portal_softc *);
#endif
+27 -108
View File
@@ -41,127 +41,46 @@
#include <machine/resource.h>
#include <machine/tlb.h>
#include <contrib/ncsw/inc/error_ext.h>
#include <contrib/ncsw/inc/xx_ext.h>
#include "portals.h"
int
dpaa_portal_alloc_res(device_t dev, struct dpaa_portals_devinfo *di, int cpu)
dpaa_portal_alloc_res(device_t dev, int cpu)
{
struct dpaa_portals_softc *sc = device_get_softc(dev);
struct resource_list_entry *rle;
int err;
struct resource_list *res;
struct dpaa_portal_softc *sc = device_get_softc(dev);
/* Check if MallocSmart allocator is ready */
if (XX_MallocSmartInit() != E_OK)
sc->sc_mres[0] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
0, RF_ACTIVE);
if (sc->sc_mres[0] == NULL) {
device_printf(dev,
"Could not allocate cache enabled memory.\n");
return (ENXIO);
res = &di->di_res;
/*
* Allocate memory.
* Reserve only one pair of CE/CI virtual memory regions
* for all CPUs, in order to save the space.
*/
if (sc->sc_rres[0] == NULL) {
/* Cache enabled area */
rle = resource_list_find(res, SYS_RES_MEMORY, 0);
sc->sc_rrid[0] = 0;
sc->sc_rres[0] = bus_alloc_resource(dev,
SYS_RES_MEMORY, &sc->sc_rrid[0], rle->start + sc->sc_dp_pa,
rle->end + sc->sc_dp_pa, rle->count, RF_ACTIVE);
if (sc->sc_rres[0] == NULL) {
device_printf(dev,
"Could not allocate cache enabled memory.\n");
return (ENXIO);
}
tlb1_set_entry(rman_get_bushandle(sc->sc_rres[0]),
rle->start + sc->sc_dp_pa, rle->count, _TLB_ENTRY_MEM);
/* Cache inhibited area */
rle = resource_list_find(res, SYS_RES_MEMORY, 1);
sc->sc_rrid[1] = 1;
sc->sc_rres[1] = bus_alloc_resource(dev,
SYS_RES_MEMORY, &sc->sc_rrid[1], rle->start + sc->sc_dp_pa,
rle->end + sc->sc_dp_pa, rle->count, RF_ACTIVE);
if (sc->sc_rres[1] == NULL) {
device_printf(dev,
"Could not allocate cache inhibited memory.\n");
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_rrid[0], sc->sc_rres[0]);
return (ENXIO);
}
tlb1_set_entry(rman_get_bushandle(sc->sc_rres[1]),
rle->start + sc->sc_dp_pa, rle->count, _TLB_ENTRY_IO);
sc->sc_dp[cpu].dp_regs_mapped = 1;
}
/* Acquire portal's CE_PA and CI_PA */
rle = resource_list_find(res, SYS_RES_MEMORY, 0);
sc->sc_dp[cpu].dp_ce_pa = rle->start + sc->sc_dp_pa;
sc->sc_dp[cpu].dp_ce_size = rle->count;
rle = resource_list_find(res, SYS_RES_MEMORY, 1);
sc->sc_dp[cpu].dp_ci_pa = rle->start + sc->sc_dp_pa;
sc->sc_dp[cpu].dp_ci_size = rle->count;
/* Cache inhibited area */
sc->sc_mres[1] = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
1, RF_ACTIVE);
if (sc->sc_mres[1] == NULL) {
device_printf(dev,
"Could not allocate cache inhibited memory.\n");
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mres[0]);
return (ENXIO);
}
sc->sc_dev = dev;
sc->sc_ce_va = rman_get_bushandle(sc->sc_mres[0]);
sc->sc_ce_size = rman_get_size(sc->sc_mres[0]);
sc->sc_ce_pa = pmap_kextract(sc->sc_ce_va);
sc->sc_ci_va = rman_get_bushandle(sc->sc_mres[1]);
sc->sc_ci_size = rman_get_size(sc->sc_mres[1]);
sc->sc_ci_pa = pmap_kextract(sc->sc_ci_va);
tlb1_set_entry(sc->sc_ce_va, sc->sc_ce_pa, sc->sc_ce_size,
_TLB_ENTRY_MEM | _TLB_ENTRY_SHARED);
sc->sc_ires = bus_alloc_resource_any(dev, SYS_RES_IRQ, 0, RF_ACTIVE);
/* Allocate interrupts */
rle = resource_list_find(res, SYS_RES_IRQ, 0);
sc->sc_dp[cpu].dp_irid = 0;
sc->sc_dp[cpu].dp_ires = bus_alloc_resource(dev,
SYS_RES_IRQ, &sc->sc_dp[cpu].dp_irid, rle->start, rle->end,
rle->count, RF_ACTIVE);
/* Save interrupt number for later use */
sc->sc_dp[cpu].dp_intr_num = rle->start;
if (sc->sc_dp[cpu].dp_ires == NULL) {
if (sc->sc_ires == NULL) {
device_printf(dev, "Could not allocate irq.\n");
return (ENXIO);
}
err = XX_PreallocAndBindIntr(dev, (uintptr_t)sc->sc_dp[cpu].dp_ires, cpu);
if (err != E_OK) {
device_printf(dev, "Could not prealloc and bind interrupt\n");
bus_release_resource(dev, SYS_RES_IRQ,
sc->sc_dp[cpu].dp_irid, sc->sc_dp[cpu].dp_ires);
sc->sc_dp[cpu].dp_ires = NULL;
return (ENXIO);
}
#if 0
err = bus_generic_config_intr(dev, rle->start, di->di_intr_trig,
di->di_intr_pol);
if (err != 0) {
device_printf(dev, "Could not configure interrupt\n");
bus_release_resource(dev, SYS_RES_IRQ,
sc->sc_dp[cpu].dp_irid, sc->sc_dp[cpu].dp_ires);
sc->sc_dp[cpu].dp_ires = NULL;
return (err);
}
#endif
return (0);
}
void
dpaa_portal_map_registers(struct dpaa_portals_softc *sc)
{
unsigned int cpu;
sched_pin();
cpu = PCPU_GET(cpuid);
if (sc->sc_dp[cpu].dp_regs_mapped)
goto out;
tlb1_set_entry(rman_get_bushandle(sc->sc_rres[0]),
sc->sc_dp[cpu].dp_ce_pa, sc->sc_dp[cpu].dp_ce_size,
_TLB_ENTRY_MEM);
tlb1_set_entry(rman_get_bushandle(sc->sc_rres[1]),
sc->sc_dp[cpu].dp_ci_pa, sc->sc_dp[cpu].dp_ci_size,
_TLB_ENTRY_IO);
sc->sc_dp[cpu].dp_regs_mapped = 1;
out:
sched_unpin();
}
+473 -329
View File
@@ -1,3 +1,8 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
/*-
* Copyright (c) 2011-2012 Semihalf.
* All rights reserved.
@@ -29,6 +34,7 @@
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/proc.h>
@@ -41,139 +47,276 @@
#include <machine/resource.h>
#include <machine/tlb.h>
#include "qman.h"
#include "dpaa_common.h"
#include "portals.h"
#include "qman.h"
#include "qman_var.h"
#include "qman_portal_if.h"
/* Registers */
#define QCSP_IO_CFG(n) (0x004 + (n) * 16)
#define IO_CFG_SDEST_M 0x00ff0000
#define IO_CFG_SDEST_S 16
#define QMAN_DCP_CFG(n) (0x300 + (n) * 0x10)
#define DCP_CFG_ED 0x00000100
#define DCP_CFG_ED_3 0x00001000
#define QMAN_PFDR_FP_LWIT 0x410
#define QMAN_PFDR_CFG 0x414
#define QMAN_SFDR_CFG 0x500
#define QMAN_MCR 0xb00
#define MCR_INIT_PFDR 0x01000000
#define MCR_READ_PFDR 0x02000000
#define MCR_READ_SFDR 0x03000000
#define MCR_QUERY_FQD_FILL 0x10000000
#define MCR_QUERY_FQD_TAGS 0x11000000
#define MCR_QUERY_FQD_CACHE 0x12000000
#define MCR_QUERY_WQ 0x20000000
#define MCR_RSLT_OK 0xf0000000
#define MCR_RSLT_OK_DATA 0xf1000000
#define MCR_RSLT_ABRT_INV 0xf4000000
#define MCR_RSLT_ABRT_DIS 0xf8000000
#define MCR_RSLT_ABRT_IDX 0xff000000
#define MCR_RSLT_ABRT_MASK 0xff000000
#define QMAN_MCP0 0xb04
#define QMAN_MCP1 0xb08
#define QMAN_IP_REV_1 0xbf8
#define IP_MJ_M 0x0000ff00
#define IP_MJ_S 8
#define IP_MN_M 0x000000ff
#define QMAN_FQD_BARE 0xc00
#define QMAN_FQD_BAR 0xc04
#define QMAN_FQD_AR 0xc10
#define AR_EN 0x80000000
#define QMAN_PFDR_BARE 0xc20
#define QMAN_PFDR_BAR 0xc24
#define QMAN_PFDR_AR 0xc30
#define QMAN_QCSP_BARE 0xc80
#define QMAN_QCSP_BAR 0xc84
#define QMAN_QCSP_AR 0xc90
#define QMAN_CI_SCHED_CFG 0xd00
#define CI_SCHED_CFG_SW 0x80000000
#define CI_SCHED_CFG_SRCCIV 0x04000000 /* Recommended */
#define CI_SCHED_CFG_SRQ_W_M 0x00000700
#define CI_SCHED_CFG_SRQ_W_S 8
#define CI_SCHED_CFG_RW_W_M 0x00000070
#define CI_SCHED_CFG_RW_W_S 4
#define CI_SCHED_CFG_BMAN_W_M 0x00000007
#define QMAN_ERR_ISR 0xe00
#define QMAN_ERR_IER 0xe04
#define QCSP_IO_CFG_3(n) (0x1004 + (n) * 16)
/* Software portals. Cache-inhibited registers */
#define QCSP_DQRR_PDQCR 0x05c
/* Software portals. Cache-enabled registers */
#define QCSP_VERB_INIT_FQ_PARK 0x40
#define QCSP_VERB_INIT_FQ_SCHED 0x41
#define QCSP_VERB_QUERY_FQ 0x44
#define QCSP_VERB_QUERY_FQ_NP 0x45
#define QCSP_VERB_ALTER_FQ_SCHED 0x48
#define QCSP_VERB_ALTER_FQ_FE 0x49
#define QCSP_VERB_ALTER_FQ_RETIRE 0x4a
#define QCSP_VERB_ALTER_FQ_TAKE_OUT 0x4b
#define QCSP_VERB_ALTER_FQ_RETIRE_CTXB 0x4c
#define QCSP_VERB_ALTER_FQ_XON 0x4d
#define QCSP_VERB_ALTER_FQ_XOFF 0x4e
/* Init FQ */
#define QCSP_INIT_FQ_WE_OAC 0x0100
#define QCSP_INIT_FQ_WE_ORPC 0x0080
#define QCSP_INIT_FQ_WE_CGID 0x0040
#define QCSP_INIT_FQ_WE_FQ_CTRL 0x0020
#define QCSP_INIT_FQ_WE_DEST_WQ 0x0010
#define QCSP_INIT_FQ_WE_ICS_CRED 0x0008
#define QCSP_INIT_FQ_WE_TD_THRESH 0x0004
#define QCSP_INIT_FQ_WE_CONTEXT_B 0x0002
#define QCSP_INIT_FQ_WE_CONTEXT_A 0x0001
#define QMAN_MC_RES_OK 0xf0
#define QMAN_MC_AFQS_NE 0x01
/* Init FQ options */
#define QM_FQCTRL_CGE 0x0400
#define QM_FQCTRL_TDE 0x0200
#define QM_FQCTRL_ORP 0x0100
#define QM_FQCTRL_CTXASTASH 0x0080
#define QM_FQCTRL_CPCSTASH 0x0040
#define QM_FQCTRL_FORCESFDR 0x0008
#define QM_FQCTRL_AVOIDBLOCK 0x0004
#define QM_FQCTRL_HOLDACTIVE 0x0002
#define QM_FQCTRL_LIC 0x0001
#define QMAN_CHANNEL_POOL1_REV1 0x21
#define QMAN_CHANNEL_POOL1_REV3 0x401
#define QMAN_PFDR_MAX 0xfffeff
/* P1023 has only 3 pool channels, but we don't support that SoC. */
#define QMAN_POOL_CHANNELS 15
/* P1023 only supports 64 congestion groups... */
#define QMAN_CGRS 256
extern struct dpaa_portals_softc *qp_sc;
static struct qman_softc *qman_sc;
extern t_Handle qman_portal_setup(struct qman_softc *qsc);
static MALLOC_DEFINE(M_QMAN, "qman", "DPAA Queue Manager structures");
int qman_channel_base;
int qman_total_fqids;
struct qman_fq **qman_fq_list;
/* Entries sorted right-to-left in bit order of the ISR */
static const char * const qman_errors[] = {
"Invalid enqueue queue",
"Invalid enqueue channel!",
"Invalid enqueue state",
"Invalid enqueue overflow",
"Invalid enqueue configuration",
NULL,
NULL,
NULL,
"Invalid dequeue queue",
"Invalid dequeue source",
"Invalid dequeue FQ",
"Invalid dequeue direct connect portal",
NULL,
NULL,
NULL,
NULL,
"Invalid command verb",
"Invalid FQ flow control state",
NULL,
NULL,
NULL,
NULL,
NULL,
"Insufficient free PFDRs",
"Single-bit ECC error",
"Multi-bit ECC error",
"PFDR low watermark",
"Invalid target transaction",
"Initiator data error",
NULL,
NULL
};
static void
qman_exception(t_Handle app, e_QmExceptions exception)
qman_isr(void *arg)
{
struct qman_softc *sc;
const char *message;
struct qman_softc *sc = arg;
uint32_t ier, isr, isr_bit;
int i;
sc = app;
ier = bus_read_4(sc->sc_rres, QMAN_ERR_IER);
isr = bus_read_4(sc->sc_rres, QMAN_ERR_ISR);
switch (exception) {
case e_QM_EX_CORENET_INITIATOR_DATA:
message = "Initiator Data Error";
break;
case e_QM_EX_CORENET_TARGET_DATA:
message = "CoreNet Target Data Error";
break;
case e_QM_EX_CORENET_INVALID_TARGET_TRANSACTION:
message = "Invalid Target Transaction";
break;
case e_QM_EX_PFDR_THRESHOLD:
message = "PFDR Low Watermark Interrupt";
break;
case e_QM_EX_PFDR_ENQUEUE_BLOCKED:
message = "PFDR Enqueues Blocked Interrupt";
break;
case e_QM_EX_SINGLE_ECC:
message = "Single Bit ECC Error Interrupt";
break;
case e_QM_EX_MULTI_ECC:
message = "Multi Bit ECC Error Interrupt";
break;
case e_QM_EX_INVALID_COMMAND:
message = "Invalid Command Verb Interrupt";
break;
case e_QM_EX_DEQUEUE_DCP:
message = "Invalid Dequeue Direct Connect Portal Interrupt";
break;
case e_QM_EX_DEQUEUE_FQ:
message = "Invalid Dequeue FQ Interrupt";
break;
case e_QM_EX_DEQUEUE_SOURCE:
message = "Invalid Dequeue Source Interrupt";
break;
case e_QM_EX_DEQUEUE_QUEUE:
message = "Invalid Dequeue Queue Interrupt";
break;
case e_QM_EX_ENQUEUE_OVERFLOW:
message = "Invalid Enqueue Overflow Interrupt";
break;
case e_QM_EX_ENQUEUE_STATE:
message = "Invalid Enqueue State Interrupt";
break;
case e_QM_EX_ENQUEUE_CHANNEL:
message = "Invalid Enqueue Channel Interrupt";
break;
case e_QM_EX_ENQUEUE_QUEUE:
message = "Invalid Enqueue Queue Interrupt";
break;
case e_QM_EX_CG_STATE_CHANGE:
message = "CG change state notification";
break;
default:
message = "Unknown error";
if ((ier & isr) == 0)
return;
isr_bit = (isr & ier);
for (i = 0; isr_bit != 0; i++, isr_bit >>= 1) {
if (isr_bit & 1)
device_printf(sc->sc_dev, "%s", qman_errors[i]);
}
device_printf(sc->sc_dev, "QMan Exception: %s.\n", message);
bus_write_4(sc->sc_rres, QMAN_ERR_ISR, isr);
}
/**
* General received frame callback.
* This is called, when user did not register his own callback for a given
* frame queue range (fqr).
*/
e_RxStoreResponse
qman_received_frame_callback(t_Handle app, t_Handle qm_fqr, t_Handle qm_portal,
uint32_t fqid_offset, t_DpaaFD *frame)
/* Set up reserved memory configuration for PFDR and FQD, per `off`. */
static int
qman_set_memory(struct qman_softc *sc, vm_paddr_t pa,
vm_size_t size, bus_size_t off)
{
struct qman_softc *sc;
uint32_t bar, bare;
vm_paddr_t old_bar;
sc = app;
/*
* Register offsets:
* 0 - BARE
* 4 - BAR
* 0x10 - AR
*/
bare = bus_read_4(sc->sc_rres, off);
bar = bus_read_4(sc->sc_rres, off + 4);
old_bar = (vm_paddr_t)bare << 32 | bar;
device_printf(sc->sc_dev, "dummy callback for received frame.\n");
return (e_RX_STORE_RESPONSE_CONTINUE);
if (old_bar != 0 && old_bar != pa) {
device_printf(sc->sc_dev, "QMan BAR already initialized!\n");
return (ENOMEM);
} else if (old_bar == pa)
return (EEXIST);
/*
* Zero the memory and flush cache through DMAP. QMan accesses the
* memory as non-coherent.
*/
memset((void *)PHYS_TO_DMAP(pa), 0, size);
cpu_flush_dcache((void *)PHYS_TO_DMAP(pa), size);
bus_write_4(sc->sc_rres, off, pa >> 32);
bus_write_4(sc->sc_rres, off + 4, (uint32_t)pa);
bus_write_4(sc->sc_rres, off + 0x10, AR_EN | (ilog2(size) - 1));
return (0);
}
/**
* General rejected frame callback.
* This is called, when user did not register his own callback for a given
* frame queue range (fqr).
/*
* Set up PFDR structures. Some things to keep in mind:
* - npfdr is the total number of PFDRs in the private memory. PFDRs are 64
* bytes in size, so npfdr is (pfdr_sz/64).
* - PFDR 0-7 are reserved, so the base starts at 8, not 0, so we adjust
* internally.
* - The second parameter is the last PFDR, not the number of PFDRs, so needs to
* be adjusted down one more, so subtract 9.
*/
e_RxStoreResponse
qman_rejected_frame_callback(t_Handle app, t_Handle qm_fqr, t_Handle qm_portal,
uint32_t fqid_offset, t_DpaaFD *frame,
t_QmRejectedFrameInfo *qm_rejected_frame_info)
static int
qman_setup_pfdr(struct qman_softc *sc, int npfdr)
{
struct qman_softc *sc;
uint32_t res;
sc = app;
npfdr = min(npfdr, QMAN_PFDR_MAX);
bus_write_4(sc->sc_rres, QMAN_MCP0, 8);
bus_write_4(sc->sc_rres, QMAN_MCP1, npfdr - 9);
bus_write_4(sc->sc_rres, QMAN_MCR, MCR_INIT_PFDR);
device_printf(sc->sc_dev, "dummy callback for rejected frame.\n");
return (e_RX_STORE_RESPONSE_CONTINUE);
for (int timeout = 100000; timeout > 0; timeout--) {
DELAY(1);
res = bus_read_4(sc->sc_rres, QMAN_MCR);
if (res >= MCR_RSLT_OK)
break;
}
if (res < MCR_RSLT_OK)
return (EBUSY);
if (res == MCR_RSLT_OK)
return (0);
return (ENXIO);
}
int
qman_attach(device_t dev)
{
struct qman_softc *sc;
t_QmParam qp;
t_Error error;
t_QmRevisionInfo rev;
int error;
vm_paddr_t fqd_pa, pfdr_pa;
vm_size_t fqd_sz, pfdr_sz;
int qman_channel_pool1 = QMAN_CHANNEL_POOL1_REV1;
uint32_t ver;
uint32_t nfqd;
bool qman3 = false;
sc = device_get_softc(dev);
sc->sc_dev = dev;
qman_sc = sc;
if (XX_MallocSmartInit() != E_OK) {
device_printf(dev, "could not initialize smart allocator.\n");
return (ENXIO);
}
sched_pin();
/* Allocate resources */
sc->sc_rrid = 0;
sc->sc_rres = bus_alloc_resource(dev, SYS_RES_MEMORY,
&sc->sc_rrid, 0, ~0, QMAN_CCSR_SIZE, RF_ACTIVE);
sc->sc_rres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 0, RF_ACTIVE);
if (sc->sc_rres == NULL) {
device_printf(dev, "could not allocate memory.\n");
goto err;
@@ -186,57 +329,79 @@ qman_attach(device_t dev)
device_printf(dev, "could not allocate error interrupt.\n");
goto err;
}
error = dpaa_map_private_memory(dev, 0, "fsl,qman-fqd",
&fqd_pa, &fqd_sz);
error = dpaa_map_private_memory(dev, 1, "fsl,qman-pfdr",
&pfdr_pa, &pfdr_sz);
if (qp_sc == NULL)
bzero((void *)PHYS_TO_DMAP(fqd_pa), fqd_sz);
cpu_flush_dcache((void *)PHYS_TO_DMAP(fqd_pa), fqd_sz);
/*
* FQDs are 64 bytes in size, with 24 bit pointers, so FQIDs are 24
* bits, fits fine in a uint32_t.
*/
nfqd = fqd_sz / 64;
qman_total_fqids = nfqd;
qman_channel_base = qman_channel_pool1;
qman_fq_list = malloc(nfqd * sizeof(struct qman_fq *), M_QMAN,
M_WAITOK);
error = qman_set_memory(sc, fqd_pa, fqd_sz, QMAN_FQD_BARE);
if (error != 0 && error != EEXIST)
goto err;
dpaa_portal_map_registers(qp_sc);
/* Initialize QMan */
qp.guestId = NCSW_MASTER_ID;
qp.baseAddress = rman_get_bushandle(sc->sc_rres);
qp.swPortalsBaseAddress = rman_get_bushandle(qp_sc->sc_rres[0]);
qp.liodn = 0;
qp.totalNumOfFqids = QMAN_MAX_FQIDS;
qp.fqdMemPartitionId = NCSW_MASTER_ID;
qp.pfdrMemPartitionId = NCSW_MASTER_ID;
qp.f_Exception = qman_exception;
qp.h_App = sc;
qp.errIrq = (uintptr_t)sc->sc_ires;
qp.partFqidBase = QMAN_FQID_BASE;
qp.partNumOfFqids = QMAN_MAX_FQIDS;
qp.partCgsBase = 0;
qp.partNumOfCgs = 0;
sc->sc_qh = QM_Config(&qp);
if (sc->sc_qh == NULL) {
device_printf(dev, "could not be configured\n");
error = qman_set_memory(sc, pfdr_pa, pfdr_sz, QMAN_PFDR_BARE);
if (error != 0 && error != EEXIST)
goto err;
if (error == 0) {
/* Initialize PFDRs if it hasn't been initialized before */
error = qman_setup_pfdr(sc, pfdr_sz / 64);
if (error != 0)
goto err;
/* Magic constant from documentation */
bus_write_4(sc->sc_rres, QMAN_PFDR_CFG, 64);
}
error = QM_Init(sc->sc_qh);
if (error != E_OK) {
bus_write_4(sc->sc_rres, QMAN_ERR_ISR, 0xffffffff);
bus_write_4(sc->sc_rres, QMAN_ERR_IER, 0xffffffff);
ver = bus_read_4(sc->sc_rres, QMAN_IP_REV_1);
sc->sc_qman_major = ((ver & IP_MJ_M) >> IP_MJ_S);
if (sc->sc_qman_major >= 3)
qman3 = true;
if (qman3)
qman_channel_pool1 = QMAN_CHANNEL_POOL1_REV3;
sc->sc_qman_base_channel = qman_channel_pool1;
sc->sc_fqalloc =
vmem_create("qman-fqalloc", 1, nfqd - 1, 1, 0, M_WAITOK);
sc->sc_qpalloc =
vmem_create("qman-fqalloc", qman_channel_pool1,
QMAN_POOL_CHANNELS, 1, 0, M_WAITOK);
sc->sc_cgalloc = vmem_create("qman->cgalloc", 0, QMAN_CGRS,
1, 0, M_WAITOK);
if (bus_setup_intr(dev, sc->sc_ires, INTR_TYPE_NET, NULL, qman_isr,
sc, &sc->sc_intr_cookie) != 0)
goto err;
if (error != 0) {
device_printf(dev, "could not be initialized\n");
goto err;
}
bus_write_4(sc->sc_rres, QMAN_DCP_CFG(0),
qman3 ? DCP_CFG_ED_3 : DCP_CFG_ED);
bus_write_4(sc->sc_rres, QMAN_DCP_CFG(1),
qman3 ? DCP_CFG_ED_3 : DCP_CFG_ED);
error = QM_GetRevision(sc->sc_qh, &rev);
if (error != E_OK) {
device_printf(dev, "could not get QMan revision\n");
goto err;
}
bus_write_4(sc->sc_rres, 0xd00, 0x80000322);
device_printf(dev, "Hardware version: %d.%d.\n",
rev.majorRev, rev.minorRev);
sched_unpin();
qman_portal_setup(sc);
/* TODO: DO we need a taskqueue? Allocate here if so */
return (0);
err:
sched_unpin();
qman_detach(dev);
return (ENXIO);
}
@@ -248,11 +413,15 @@ qman_detach(device_t dev)
sc = device_get_softc(dev);
if (sc->sc_qh)
QM_Free(sc->sc_qh);
if (sc->sc_fqalloc != NULL)
vmem_destroy(sc->sc_fqalloc);
if (sc->sc_qpalloc != NULL)
vmem_destroy(sc->sc_qpalloc);
if (sc->sc_cgalloc != NULL)
vmem_destroy(sc->sc_cgalloc);
if (sc->sc_ires != NULL)
XX_DeallocIntr((uintptr_t)sc->sc_ires);
if (sc->sc_intr_cookie != NULL)
bus_teardown_intr(dev, sc->sc_ires, sc->sc_intr_cookie);
if (sc->sc_ires != NULL)
bus_release_resource(dev, SYS_RES_IRQ,
@@ -262,6 +431,9 @@ qman_detach(device_t dev)
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_rrid, sc->sc_rres);
free(qman_fq_list, M_QMAN);
qman_fq_list = NULL;
return (0);
}
@@ -286,261 +458,233 @@ qman_shutdown(device_t dev)
return (0);
}
int
qman_alloc_channel(void)
{
struct qman_softc *sc = qman_sc;
vmem_addr_t channel;
vmem_alloc(sc->sc_qpalloc, 1, M_BESTFIT | M_WAITOK, &channel);
return (channel);
}
void
qman_free_channel(int channel)
{
struct qman_softc *sc = qman_sc;
vmem_free(sc->sc_qpalloc, channel, 1);
}
/**
* @group QMan API functions implementation.
* @{
*/
t_Handle
qman_fqr_create(uint32_t fqids_num, e_QmFQChannel channel, uint8_t wq,
struct qman_fq *
qman_fq_from_index(uint32_t fqid)
{
if (fqid > qman_total_fqids)
return (NULL);
return (qman_fq_list[fqid]);
}
/* Allocate and initialize an FQ Range */
struct qman_fq *
qman_fq_create(uint32_t fqids_num, int channel, uint8_t wq,
bool force_fqid, uint32_t fqid_or_align, bool init_parked,
bool hold_active, bool prefer_in_cache, bool congst_avoid_ena,
t_Handle congst_group, int8_t overhead_accounting_len,
void *congst_group, int8_t overhead_accounting_len,
uint32_t tail_drop_threshold)
{
union qman_mc_command cmd;
struct qman_softc *sc;
t_QmFqrParams fqr;
t_Handle fqrh, portal;
union qman_mc_result *res;
struct qman_fq *fqh;
device_t portal;
vmem_addr_t fqid_base;
uint8_t rslt;
sc = qman_sc;
sched_pin();
if (fqids_num != 1) {
device_printf(sc->sc_dev,
"Only one fq allocation allowed currently\n");
return (NULL);
}
bzero(&cmd, sizeof(cmd));
vmem_alloc(sc->sc_fqalloc, fqids_num, M_BESTFIT | M_WAITOK, &fqid_base);
cmd.init_fq.fqid = fqid_base;
cmd.init_fq.count = fqids_num - 1;
cmd.init_fq.dest_chan = channel;
cmd.init_fq.dest_wq = wq;
cmd.init_fq.we_mask = QCSP_INIT_FQ_WE_DEST_WQ | QCSP_INIT_FQ_WE_FQ_CTRL;
if (init_parked)
cmd.init_fq.verb = QCSP_VERB_INIT_FQ_PARK;
else
cmd.init_fq.verb = QCSP_VERB_INIT_FQ_SCHED;
cmd.init_fq.fq_ctrl = (prefer_in_cache ? QM_FQCTRL_LIC : 0) |
(hold_active ? QM_FQCTRL_HOLDACTIVE : 0) |
(congst_avoid_ena ? QM_FQCTRL_AVOIDBLOCK : 0);
critical_enter();
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
portal = DPCPU_GET(qman_affine_portal);
res = QMAN_PORTAL_MC_SEND_RAW(portal, &cmd);
rslt = 0;
if (res != NULL)
rslt = res->init_fq.rslt;
critical_exit();
if (res == NULL || rslt != QMAN_MC_RES_OK) {
vmem_free(sc->sc_fqalloc, fqid_base, fqids_num);
goto err;
}
fqr.h_Qm = sc->sc_qh;
fqr.h_QmPortal = portal;
fqr.initParked = init_parked;
fqr.holdActive = hold_active;
fqr.preferInCache = prefer_in_cache;
fqh = malloc(sizeof(*fqh), M_QMAN, M_WAITOK | M_ZERO);
fqh->fqid = fqid_base;
/* We do not support stashing */
fqr.useContextAForStash = FALSE;
fqr.p_ContextA = 0;
fqr.p_ContextB = 0;
qman_fq_list[fqid_base] = fqh;
fqr.channel = channel;
fqr.wq = wq;
fqr.shadowMode = FALSE;
fqr.numOfFqids = fqids_num;
/* FQID */
fqr.useForce = force_fqid;
if (force_fqid) {
fqr.qs.frcQ.fqid = fqid_or_align;
} else {
fqr.qs.nonFrcQs.align = fqid_or_align;
}
/* Congestion Avoidance */
fqr.congestionAvoidanceEnable = congst_avoid_ena;
if (congst_avoid_ena) {
fqr.congestionAvoidanceParams.h_QmCg = congst_group;
fqr.congestionAvoidanceParams.overheadAccountingLength =
overhead_accounting_len;
fqr.congestionAvoidanceParams.fqTailDropThreshold =
tail_drop_threshold;
} else {
fqr.congestionAvoidanceParams.h_QmCg = 0;
fqr.congestionAvoidanceParams.overheadAccountingLength = 0;
fqr.congestionAvoidanceParams.fqTailDropThreshold = 0;
}
fqrh = QM_FQR_Create(&fqr);
if (fqrh == NULL) {
device_printf(sc->sc_dev, "could not create Frame Queue Range"
"\n");
goto err;
}
sc->sc_fqr_cpu[QM_FQR_GetFqid(fqrh)] = PCPU_GET(cpuid);
sched_unpin();
return (fqrh);
return (fqh);
err:
sched_unpin();
return (NULL);
}
t_Error
qman_fqr_free(t_Handle fqr)
static int
qman_fq_retire(device_t portal, struct qman_fq *fq)
{
struct qman_softc *sc;
t_Error error;
union qman_mc_command cmd;
union qman_mc_result *rr;
sc = qman_sc;
thread_lock(curthread);
sched_bind(curthread, sc->sc_fqr_cpu[QM_FQR_GetFqid(fqr)]);
thread_unlock(curthread);
bzero(&cmd, sizeof(cmd));
error = QM_FQR_Free(fqr);
cmd.alter_fqs.verb = QCSP_VERB_ALTER_FQ_RETIRE;
cmd.alter_fqs.fqid = fq->fqid;
rr = QMAN_PORTAL_MC_SEND_RAW(portal, &cmd);
if (rr == NULL)
return (ETIMEDOUT);
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
return (error);
}
t_Error
qman_fqr_register_cb(t_Handle fqr, t_QmReceivedFrameCallback *callback,
t_Handle app)
{
struct qman_softc *sc;
t_Error error;
t_Handle portal;
sc = qman_sc;
sched_pin();
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
sched_unpin();
return (E_NOT_SUPPORTED);
if (rr->alter_fqs.rslt == QMAN_MC_RES_OK) {
if (rr->alter_fqs.fqs & QMAN_MC_AFQS_NE) {
/* TODO: Drain.... */
}
return (0);
}
error = QM_FQR_RegisterCB(fqr, callback, app);
sched_unpin();
return (error);
return (0);
}
t_Error
qman_fqr_enqueue(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame)
int
qman_fq_free(struct qman_fq *fq)
{
struct qman_softc *sc;
t_Error error;
t_Handle portal;
int error;
sc = qman_sc;
sched_pin();
critical_enter();
error = qman_fq_retire(DPCPU_GET(qman_affine_portal), fq);
/* TODO: Take FQ out of service. */
critical_exit();
if (error != 0)
return (error);
vmem_free(sc->sc_fqalloc, fq->fqid, 1);
qman_fq_list[fq->fqid] = NULL;
free(fq, M_QMAN);
return (0);
}
int
qman_fq_register_cb(struct qman_fq *fq, qman_cb_dqrr callback,
void *ctx)
{
fq->cb.dqrr = callback;
fq->cb.ctx = ctx;
return (0);
}
int
qman_fq_enqueue(struct qman_fq *fq, struct dpaa_fd *frame)
{
struct qman_softc *sc;
int error;
void *portal;
sc = qman_sc;
critical_enter();
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
portal = DPCPU_GET(qman_affine_portal);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
sched_unpin();
return (E_NOT_SUPPORTED);
critical_exit();
return (ENXIO);
}
error = QM_FQR_Enqueue(fqr, portal, fqid_off, frame);
error = QMAN_PORTAL_ENQUEUE(portal, fq, frame);
sched_unpin();
critical_exit();
return (error);
}
uint32_t
qman_fqr_get_counter(t_Handle fqr, uint32_t fqid_off,
e_QmFqrCounters counter)
qman_fq_get_fqid(struct qman_fq *fq)
{
struct qman_softc *sc;
uint32_t val;
t_Handle portal;
sc = qman_sc;
sched_pin();
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
sched_unpin();
return (0);
}
val = QM_FQR_GetCounter(fqr, portal, fqid_off, counter);
sched_unpin();
return (val);
return (fq->fqid);
}
t_Error
qman_fqr_pull_frame(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame)
{
struct qman_softc *sc;
t_Error error;
t_Handle portal;
sc = qman_sc;
sched_pin();
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
sched_unpin();
return (E_NOT_SUPPORTED);
}
error = QM_FQR_PullFrame(fqr, portal, fqid_off, frame);
sched_unpin();
return (error);
}
uint32_t
qman_fqr_get_base_fqid(t_Handle fqr)
qman_fq_get_counter(struct qman_fq *fq, int counter)
{
struct qman_softc *sc;
uint32_t val;
t_Handle portal;
union qman_mc_result *cmd_res;
union qman_mc_command command;
device_t portal;
u_int ret = 0;
sc = qman_sc;
sched_pin();
bzero(&command, sizeof(command));
command.query_fq_np.verb = QCSP_VERB_QUERY_FQ_NP;
command.query_fq_np.fqid = fq->fqid;
critical_enter();
portal = DPCPU_GET(qman_affine_portal);
cmd_res = QMAN_PORTAL_MC_SEND_RAW(portal, &command);
if (counter == QMAN_COUNTER_FRAME)
ret = cmd_res->query_fq_np.frm_cnt;
else if (counter == QMAN_COUNTER_BYTES)
ret = cmd_res->query_fq_np.byte_cnt;
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
sched_unpin();
return (0);
}
critical_exit();
val = QM_FQR_GetFqid(fqr);
sched_unpin();
return (val);
return (ret);
}
t_Error
qman_poll(e_QmPortalPollSource source)
void
qman_set_sdest(uint16_t channel, int cpu)
{
struct qman_softc *sc;
t_Error error;
t_Handle portal;
struct qman_softc *sc = qman_sc;
uint32_t reg;
sc = qman_sc;
sched_pin();
/* Ensure we have got QMan port initialized */
portal = qman_portal_setup(sc);
if (portal == NULL) {
device_printf(sc->sc_dev, "could not setup QMan portal\n");
sched_unpin();
return (E_NOT_SUPPORTED);
if (sc->sc_qman_major >= 3) {
reg = bus_read_4(sc->sc_rres, QCSP_IO_CFG_3(channel));
reg &= IO_CFG_SDEST_M;
reg |= (cpu << IO_CFG_SDEST_S);
bus_write_4(sc->sc_rres, QCSP_IO_CFG_3(channel), reg);
} else {
reg = bus_read_4(sc->sc_rres, QCSP_IO_CFG(channel));
reg &= IO_CFG_SDEST_M;
reg |= (cpu << IO_CFG_SDEST_S);
bus_write_4(sc->sc_rres, QCSP_IO_CFG(channel), reg);
}
error = QM_Poll(sc->sc_qh, source);
sched_unpin();
return (error);
}
/*
+142 -42
View File
@@ -27,10 +27,13 @@
#ifndef _QMAN_H
#define _QMAN_H
#include <sys/vmem.h>
#include <machine/vmparam.h>
#include <contrib/ncsw/inc/Peripherals/qm_ext.h>
struct qman_fq;
struct qman_fq;
struct dpaa_fd;
struct qman_portal;
/**
* @group QMan private defines/declarations
@@ -44,13 +47,15 @@
/**
* Pool channel common to all software portals.
* @note Value of 0 reflects the e_QM_FQ_CHANNEL_POOL1 from e_QmFQChannel
* type used in qman_fqr_create().
* type used in qman_fq_create().
*/
#define QMAN_COMMON_POOL_CHANNEL 0
#define QMAN_FQID_BASE 1
#define QMAN_CCSR_SIZE 0x1000
/* Counters */
#define QMAN_COUNTER_FRAME 0
#define QMAN_COUNTER_BYTES 1
/*
* Portal defines
@@ -69,18 +74,103 @@ struct qman_softc {
struct resource *sc_rres; /* register resource */
int sc_irid; /* interrupt rid */
struct resource *sc_ires; /* interrupt resource */
vmem_t *sc_fqalloc;
vmem_t *sc_qpalloc;
vmem_t *sc_cgalloc;
void *sc_intr_cookie;
int sc_qman_base_channel;
int sc_qman_major;
bool sc_regs_mapped[MAXCPU];
t_Handle sc_qh; /* QMAN handle */
t_Handle sc_qph[MAXCPU]; /* QMAN portal handles */
vm_paddr_t sc_qp_pa; /* QMAN portal PA */
int sc_fqr_cpu[QMAN_MAX_FQIDS];
int sc_fq_cpu[QMAN_MAX_FQIDS];
};
struct qman_fd {
uint64_t dd:2;
uint64_t liodn_off:6;
uint64_t bpid:8;
uint64_t eliodn_off:4;
uint64_t _rsvd0:4;
uint64_t addr:40;
union {
struct {
uint32_t format:3;
uint32_t offset:9;
uint32_t length:20;
};
struct {
uint32_t format2:3;
uint32_t wlength:29;
};
};
uint32_t cmd_stat;
};
_Static_assert(sizeof(struct qman_fd) == 16, "qman_fd size mismatch");
struct qman_dqrr_entry {
uint8_t verb;
uint8_t stat;
uint16_t seqnum;
uint8_t tok;
uint8_t _rsvd0[3];
uint32_t fqid;
uint32_t ctxb;
struct qman_fd fd;
uint8_t _rsvd1[32];
};
/* Bits for qman_dqrr_entry fields */
#define QMAN_DQRR_STAT_FQ_EMPTY 0x80
#define QMAN_DQRR_STAT_FQ_HELD_ACTIVE 0x40
#define QMAN_DQRR_STAT_FQ_FORCED 0x20
#define QMAN_DQRR_STAT_HAS_FRAME 0x10
#define QMAN_DQRR_STAT_VDQCR 0x02
#define QMAN_DQRR_STAT_EXPIRED 0x01
struct qman_mr_entry {
union {
struct {
uint8_t verb;
uint8_t data[63];
};
struct {
uint8_t verb;
uint8_t dca;
uint16_t seqnum;
uint32_t rc:8;
uint32_t orp:24;
uint32_t fqid;
uint32_t tag;
struct qman_fd fd;
uint8_t _rsvd[32];
} ern;
struct {
uint8_t verb;
uint8_t fqs;
uint8_t _rsvd0[6];
uint32_t fqid;
uint32_t ctxb;
uint8_t _rsvd1[48];
} fqscn;
};
};
_Static_assert(sizeof(struct qman_mr_entry) == 64, "bad sizeof qman_mr");
/** @> */
typedef int (*qman_cb_dqrr)(device_t, struct qman_fq *,
struct qman_fd *, void *);
typedef void (*qman_cb_mr)(device_t, struct qman_fq *,
struct qman_mr_entry *);
struct qman_cb {
qman_cb_dqrr dqrr;
qman_cb_mr ern;
qman_cb_mr fqscn;
void *ctx;
};
/**
* @group QMan bus interface
* @{
@@ -91,6 +181,8 @@ int qman_suspend(device_t dev);
int qman_resume(device_t dev);
int qman_shutdown(device_t dev);
/** @> */
int qman_create_affine_portal(device_t, vm_offset_t, vm_offset_t, int);
void qman_set_sdest(uint16_t, int);
/**
@@ -149,69 +241,77 @@ int qman_shutdown(device_t dev);
*
* @return A handle to newly created FQR object.
*/
t_Handle qman_fqr_create(uint32_t fqids_num, e_QmFQChannel channel, uint8_t wq,
bool force_fqid, uint32_t fqid_or_align, bool init_parked,
struct qman_fq *qman_fq_create(uint32_t fqids_num, int channel,
uint8_t wq, bool force_fqid, uint32_t fqid_or_align, bool init_parked,
bool hold_active, bool prefer_in_cache, bool congst_avoid_ena,
t_Handle congst_group, int8_t overhead_accounting_len,
void *congst_group, int8_t overhead_accounting_len,
uint32_t tail_drop_threshold);
/**
* Free Frame Queue Range.
*
* @param fqr A handle to FQR to be freed.
* @param fq A handle to FQR to be freed.
* @return E_OK on success; error code otherwise.
*/
t_Error qman_fqr_free(t_Handle fqr);
int qman_fq_free(struct qman_fq *fq);
/**
* Register the callback function.
* The callback function will be called when a frame comes from this FQR.
*
* @param fqr A handle to FQR.
* @param fq A handle to FQR.
* @param callback A pointer to the callback function.
* @param app A pointer to the user's data.
* @return E_OK on success; error code otherwise.
*/
t_Error qman_fqr_register_cb(t_Handle fqr, t_QmReceivedFrameCallback *callback,
t_Handle app);
int qman_fq_register_cb(struct qman_fq *fq, qman_cb_dqrr callback,
void *ctx);
/**
* Enqueue a frame on a given FQR.
* Enqueue a frame on a given FQ.
*
* @param fqr A handle to FQR.
* @param fqid_off FQID offset wihin the FQR.
* @param fq A handle to FQ.
* @param frame A frame to be enqueued to the transmission.
* @return E_OK on success; error code otherwise.
*/
t_Error qman_fqr_enqueue(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame);
int qman_fq_enqueue(struct qman_fq *fq, struct dpaa_fd *frame);
/**
* Get one of the FQR counter's value.
* Get one of the FQ counter's value.
*
* @param fqr A handle to FQR.
* @param fqid_off FQID offset within the FQR.
* @param fq A handle to FQ.
* @param counter The requested counter.
* @return Counter's current value.
*/
uint32_t qman_fqr_get_counter(t_Handle fqr, uint32_t fqid_off,
e_QmFqrCounters counter);
uint32_t qman_fq_get_counter(struct qman_fq *fq, int counter);
/**
* Pull frame from FQR.
* Pull frame from FQ.
*
* @param fqr A handle to FQR.
* @param fqid_off FQID offset within the FQR.
* @param fq A handle to FQ.
* @param frame The received frame.
* @return E_OK on success; error code otherwise.
*/
t_Error qman_fqr_pull_frame(t_Handle fqr, uint32_t fqid_off, t_DpaaFD *frame);
int qman_fq_pull_frame(struct qman_fq *fq, struct dpaa_fd *frame);
/**
* Get base FQID of the FQR.
* @param fqr A handle to FQR.
* @return Base FQID of the FQR.
* Get FQID of the FQ.
* @param fq A handle to FQ.
* @return FQID of the FQ.
*/
uint32_t qman_fqr_get_base_fqid(t_Handle fqr);
uint32_t qman_fq_get_fqid(struct qman_fq *fq);
/*
* Allocate a QMan channel to be used with an FQ.
* @return Channel ID
*/
int qman_alloc_channel(void);
/*
* Free a channel
* @param chan Channel ID returned from qman_alloc_channel().
*/
void qman_free_channel(int);
/**
* Poll frames from QMan.
@@ -220,24 +320,24 @@ uint32_t qman_fqr_get_base_fqid(t_Handle fqr);
* @param source Type of frames to be polled.
* @return E_OK on success; error otherwise.
*/
t_Error qman_poll(e_QmPortalPollSource source);
int qman_poll(int source);
/**
* General received frame callback.
* This is called, when user did not register his own callback for a given
* frame queue range (fqr).
* frame queue range (fq).
*/
e_RxStoreResponse qman_received_frame_callback(t_Handle app, t_Handle qm_fqr,
t_Handle qm_portal, uint32_t fqid_offset, t_DpaaFD *frame);
int qman_received_frame_callback(void *ctx, struct qman_fq *fq,
void *qm_portal, uint32_t fqid_offset, struct dpaa_fd *frame);
/**
* General rejected frame callback.
* This is called, when user did not register his own callback for a given
* frame queue range (fqr).
* frame queue range (fq).
*/
e_RxStoreResponse qman_rejected_frame_callback(t_Handle app, t_Handle qm_fqr,
t_Handle qm_portal, uint32_t fqid_offset, t_DpaaFD *frame,
t_QmRejectedFrameInfo *qm_rejected_frame_info);
int qman_rejected_frame_callback(void *ctx, struct qman_fq *fq,
void *qm_portal, uint32_t fqid_offset, struct dpaa_fd *frame,
void *qm_rejected_frame_info);
/** @} */
+30 -138
View File
@@ -41,6 +41,8 @@
#include "qman.h"
#include "portals.h"
#include "qman_var.h"
#include "qman_portal_if.h"
#define FQMAN_DEVSTR "Freescale Queue Manager"
@@ -59,12 +61,7 @@ static device_method_t qman_methods[] = {
DEVMETHOD_END
};
static driver_t qman_driver = {
"qman",
qman_methods,
sizeof(struct qman_softc),
};
DEFINE_CLASS_0(qman, qman_driver, qman_methods, sizeof(struct qman_softc));
EARLY_DRIVER_MODULE(qman, simplebus, qman_driver, 0, 0, BUS_PASS_SUPPORTDEV);
static int
@@ -82,53 +79,38 @@ qman_fdt_probe(device_t dev)
/*
* QMAN Portals
*/
#define QMAN_PORT_DEVSTR "Freescale Queue Manager - Portals"
#define QMAN_PORT_DEVSTR "Freescale Queue Manager - Portal"
static device_probe_t qman_portals_fdt_probe;
static device_attach_t qman_portals_fdt_attach;
static int portal_ncpus;
static device_probe_t qman_portal_fdt_probe;
static device_attach_t qman_portal_fdt_attach;
static device_method_t qm_portals_methods[] = {
static device_method_t qman_portal_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, qman_portals_fdt_probe),
DEVMETHOD(device_attach, qman_portals_fdt_attach),
DEVMETHOD(device_detach, qman_portals_detach),
DEVMETHOD(device_probe, qman_portal_fdt_probe),
DEVMETHOD(device_attach, qman_portal_fdt_attach),
DEVMETHOD(device_detach, qman_portal_detach),
DEVMETHOD(qman_portal_enqueue, qman_portal_fq_enqueue),
DEVMETHOD(qman_portal_mc_send_raw, qman_portal_mc_send_raw),
DEVMETHOD(qman_portal_static_dequeue_channel,
qman_portal_static_dequeue_channel),
DEVMETHOD(qman_portal_static_dequeue_rm_channel,
qman_portal_static_dequeue_rm_channel),
DEVMETHOD_END
};
static driver_t qm_portals_driver = {
"qman-portals",
qm_portals_methods,
sizeof(struct dpaa_portals_softc),
};
EARLY_DRIVER_MODULE(qman_portals, ofwbus, qm_portals_driver, 0, 0,
BUS_PASS_BUS);
static void
get_addr_props(phandle_t node, uint32_t *addrp, uint32_t *sizep)
{
*addrp = 2;
*sizep = 1;
OF_getencprop(node, "#address-cells", addrp, sizeof(*addrp));
OF_getencprop(node, "#size-cells", sizep, sizeof(*sizep));
}
DEFINE_CLASS_0(qman_portal, qman_portal_driver, qman_portal_methods,
sizeof(struct qman_softc));
EARLY_DRIVER_MODULE(qman_portal, simplebus, qman_portal_driver, 0, 0,
BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
static int
qman_portals_fdt_probe(device_t dev)
qman_portal_fdt_probe(device_t dev)
{
phandle_t node;
if (ofw_bus_is_compatible(dev, "simple-bus")) {
node = ofw_bus_get_node(dev);
for (node = OF_child(node); node > 0; node = OF_peer(node)) {
if (ofw_bus_node_is_compatible(node, "fsl,qman-portal"))
break;
}
if (node <= 0)
return (ENXIO);
} else if (!ofw_bus_is_compatible(dev, "fsl,qman-portals"))
if (!ofw_bus_is_compatible(dev, "fsl,qman-portal"))
return (ENXIO);
device_set_desc(dev, QMAN_PORT_DEVSTR);
@@ -137,105 +119,15 @@ qman_portals_fdt_probe(device_t dev)
}
static int
qman_portals_fdt_attach(device_t dev)
qman_portal_fdt_attach(device_t dev)
{
struct dpaa_portals_softc *sc;
phandle_t node, child, cpu_node;
vm_paddr_t portal_pa, portal_par_pa;
vm_size_t portal_size;
uint32_t addr, paddr, size;
ihandle_t cpu;
int cpu_num, cpus, intr_rid;
struct dpaa_portals_devinfo di;
struct ofw_bus_devinfo ofw_di = {};
cell_t *range;
int nrange;
int i;
int portal_cpu = portal_ncpus;
cpus = 0;
sc = device_get_softc(dev);
sc->sc_dev = dev;
node = ofw_bus_get_node(dev);
/* Get this node's range */
get_addr_props(ofw_bus_get_node(device_get_parent(dev)), &paddr, &size);
get_addr_props(node, &addr, &size);
nrange = OF_getencprop_alloc_multi(node, "ranges",
sizeof(*range), (void **)&range);
if (nrange < addr + paddr + size)
/* Don't attach to more portals than we have CPUs */
if (mp_ncpus == portal_ncpus)
return (ENXIO);
portal_pa = portal_par_pa = 0;
portal_size = 0;
for (i = 0; i < addr; i++) {
portal_pa <<= 32;
portal_pa |= range[i];
}
for (; i < paddr + addr; i++) {
portal_par_pa <<= 32;
portal_par_pa |= range[i];
}
portal_pa += portal_par_pa;
for (; i < size + paddr + addr; i++) {
portal_size = (uintmax_t)portal_size << 32;
portal_size |= range[i];
}
OF_prop_free(range);
sc->sc_dp_size = portal_size;
sc->sc_dp_pa = portal_pa;
/* Find portals tied to CPUs */
for (child = OF_child(node); child != 0; child = OF_peer(child)) {
if (cpus >= mp_ncpus)
break;
if (!ofw_bus_node_is_compatible(child, "fsl,qman-portal")) {
continue;
}
/* Checkout related cpu */
if (OF_getprop(child, "cpu-handle", (void *)&cpu,
sizeof(cpu)) > 0) {
cpu_node = OF_instance_to_package(cpu);
/* Acquire cpu number */
if (OF_getencprop(cpu_node, "reg", &cpu_num, sizeof(cpu_num)) <= 0) {
device_printf(dev, "Could not retrieve CPU number.\n");
return (ENXIO);
}
} else
cpu_num = cpus;
cpus++;
portal_ncpus++;
if (ofw_bus_gen_setup_devinfo(&ofw_di, child) != 0) {
device_printf(dev, "could not set up devinfo\n");
continue;
}
resource_list_init(&di.di_res);
if (ofw_bus_reg_to_rl(dev, child, addr, size, &di.di_res)) {
device_printf(dev, "%s: could not process 'reg' "
"property\n", ofw_di.obd_name);
ofw_bus_gen_destroy_devinfo(&ofw_di);
continue;
}
if (ofw_bus_intr_to_rl(dev, child, &di.di_res, &intr_rid)) {
device_printf(dev, "%s: could not process "
"'interrupts' property\n", ofw_di.obd_name);
resource_list_free(&di.di_res);
ofw_bus_gen_destroy_devinfo(&ofw_di);
continue;
}
di.di_intr_rid = intr_rid;
if (dpaa_portal_alloc_res(dev, &di, cpu_num))
goto err;
}
ofw_bus_gen_destroy_devinfo(&ofw_di);
return (qman_portals_attach(dev));
err:
resource_list_free(&di.di_res);
ofw_bus_gen_destroy_devinfo(&ofw_di);
qman_portals_detach(dev);
return (ENXIO);
return (qman_portal_attach(dev, portal_cpu));
}
+37
View File
@@ -0,0 +1,37 @@
#-
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2026 Justin Hibbits
#include <sys/pcpu.h>
#include <machine/bus.h>
#include <dev/dpaa/portals.h>
#include <dev/dpaa/qman.h>
#include <dev/dpaa/qman_var.h>
/**
* @brief DPAA QMan portal interface
*
*/
INTERFACE qman_portal;
METHOD int enqueue {
device_t dev;
struct qman_fq *fq;
struct dpaa_fd *fd;
};
METHOD union qman_mc_result * mc_send_raw {
device_t dev;
union qman_mc_command *cmd;
};
METHOD void static_dequeue_channel {
device_t dev;
int channel;
}
METHOD void static_dequeue_rm_channel {
device_t dev;
int channel;
}
+389 -139
View File
@@ -1,27 +1,7 @@
/*-
* Copyright (c) 2012 Semihalf.
* All rights reserved.
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* 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.
* Copyright (c) 2026 Justin Hibbits
*/
#include "opt_platform.h"
@@ -36,6 +16,7 @@
#include <sys/proc.h>
#include <sys/pcpu.h>
#include <sys/sched.h>
#include <ddb/ddb.h>
#include <machine/bus.h>
#include <machine/tlb.h>
@@ -47,143 +28,412 @@
#include "qman.h"
#include "portals.h"
#include "qman_var.h"
extern e_RxStoreResponse qman_received_frame_callback(t_Handle, t_Handle,
t_Handle, uint32_t, t_DpaaFD *);
extern e_RxStoreResponse qman_rejected_frame_callback(t_Handle, t_Handle,
t_Handle, uint32_t, t_DpaaFD *, t_QmRejectedFrameInfo *);
t_Handle qman_portal_setup(struct qman_softc *);
/* Cache-enabled registers */
#define QCSP_EQCR_N(n) (0x0000 + (n * 64))
#define QMAN_EQCR_COUNT 8
#define QCSP_DQRR_N(n) (0x1000 + (n * 64))
#define QMAN_DQRR_COUNT 16
#define QCSP_MR_N(n) (0x2000 + (n * 64))
#define QMAN_MR_COUNT 8
#define QCSP_CR 0x3800
#define QCSP_RR(n) (0x3900 + 0x40 * (n))
struct dpaa_portals_softc *qp_sc;
#define QCSP_EQCR_PI_CENA 0x0000
#define EQCR_PI_VP 0x00000008
#define EQCR_PI_PI_M 0x00000007
#define QCSP_EQCR_CI_CENA 0x0004
#define EQCR_CI_C 0x00000008
#define EQCR_CI_CI_M 0x00000007
#define QCSP_DQRR_PI_CENA 0x0000
#define DQRR_PI_VP 0x00000010
#define DQRR_PI_PI_M 0x0000000f
#define QCSP_DQRR_CI_CENA 0x0004
#define DQRR_CI_C 0x00000010
#define DQRR_CI_CI_M 0x0000000f
#define QMAN_MC_VERB_VBIT 0x80
/* Cache-inhibited registers */
#define QCSP_EQCR_PI_CINH 0x0000
#define QCSP_EQCR_CI_CINH 0x0004
#define QCSP_DQRR_PI_CINH 0x0040
#define QCSP_DQRR_CI_CINH 0x0044
#define QCSP_EQCR_ITR 0x0008
#define QCSP_DQRR_ITR 0x0048
#define QCSP_DQRR_SDQCR 0x0054
#define SDQCR_SS 0x40000000
#define SDQCR_FC 0x20000000
#define SDQCR_DP 0x10000000
#define SDQCR_DCT_NUL 0x00000000
#define SDQCR_DCT_PRI_PREC 0x01000000
#define SDQCR_DCT_ACTIVE_WQ 0x02000000
#define SDQCR_DCT_ACTIVE_FQ_O 0x03000000
#define SDQCR_DCT_M 0x03000000
#define SDQCR_TOKEN_M 0x00ff0000
#define SDQCR_TOKEN_S 16
#define DQRR_DQ_SRC_M 0x0000ffff
#define DQRR_DQ_SRC_DCP 0x00008000
#define SDQCR_DQ_SRC_CHAN(n) (0x8000 >> (n + 1))
#define QCSP_DQRR_VDQCR 0x0058
#define QCSP_DQRR_PDQCR 0x005c
#define QCSP_MR_ITR 0x0088
#define QCSP_CFG 0x0100
#define CFG_EST_M 0x70000000
#define CFG_EST_S 28
#define CFG_EP 0x04000000
#define CFG_EPM_M 0x03000000
#define CFG_EPM_PI_CI 0x00000000
#define CFG_EPM_PI_CE 0x01000000
#define CFG_EPM_VB1 0x02000000
#define CFG_EPM_VB2 0x03000000
#define CFG_DQRR_MF_M 0x00f00000
#define CFG_DQRR_MF_S 20
#define CFG_DP 0x00040000
#define CFG_DCM_C_M 0x00030000
#define CFG_DCM_CI_CI 0x00000000
#define CFG_DCM_CI_CE 0x00010000
#define CFG_DCM_DCA1 0x00020000
#define CFG_DCM_DCA2 0x00030000
#define CFG_SD 0x00000200
#define CFG_MM 0x00000100
#define CFG_RE 0x00000080
#define CFG_RP 0x00000040
#define CFG_SE 0x00000020
#define CFG_SP 0x00000010
#define CFG_SDEST_M 0x00000007
#define QCSP_ISR 0x0e00
#define QM_PIRQ_CSCI 0x00100000
#define QM_PIRQ_EQCI 0x00080000
#define QM_PIRQ_EQRI 0x00040000
#define QM_PIRQ_DQRI 0x00020000
#define QM_PIRQ_MRI 0x00010000
#define QM_PIRQ_DQ_AVAIL_M 0x0000ffff
#define QCSP_IER 0x0e04
#define QCSP_ISDR 0x0e08
#define QCSP_IIR 0xe0c
#define QM_EQCR_VERB_CMD_ENQUEUE 0x01
#define QM_EQCR_VERB_BIT_INT 0x04
#define DEF_SDQCR_TOKEN 0xab
static void qman_portal_loop_rings(struct qman_portal_softc *sc);
static void qman_portal_isr(void *);
DPCPU_DEFINE(device_t, qman_affine_portal);
DPAA_RING(qman_eqcr, QMAN_EQCR_COUNT, QCSP_EQCR_PI_CENA, QCSP_EQCR_CI_CENA,
QCSP_EQCR_PI_CINH, QCSP_EQCR_CI_CINH);
DPAA_RING(qman_dqrr, QMAN_DQRR_COUNT, QCSP_DQRR_PI_CENA, QCSP_DQRR_CI_CENA,
QCSP_DQRR_PI_CINH, QCSP_DQRR_CI_CINH);
/*
* pmode: one of the CFG_EPM constants.
* stash_prio: 0 or CFG_EP
* stash_thresh: 0-7
*/
static int
qman_eqcr_init(struct qman_portal_softc *sc, int pmode, u_int stash_thresh,
u_int stash_prio)
{
struct resource *regs = sc->sc_base.sc_mres[1];
uint32_t reg;
sc->sc_eqcr.ring =
(struct qman_eqcr_entry *)(sc->sc_base.sc_ce_va + QCSP_EQCR_N(0));
qman_eqcr_ring_init(&sc->sc_eqcr, &sc->sc_base);
reg = bus_read_4(regs, QCSP_CFG);
reg &= 0x00ffffff;
reg |= pmode;
reg |= ((stash_thresh << CFG_EST_S) & CFG_EST_M);
reg |= stash_prio;
bus_write_4(regs, QCSP_CFG, reg);
return (0);
}
static int
qman_dqrr_init(struct qman_portal_softc *sc)
{
struct resource *regs = sc->sc_base.sc_mres[1];
uint32_t reg;
/* Dequeue from the direct-connect channel and pool 0, up to 3 frames */
bus_write_4(regs, QCSP_DQRR_SDQCR,
SDQCR_FC | SDQCR_DP | SDQCR_DCT_PRI_PREC |
(DEF_SDQCR_TOKEN << SDQCR_TOKEN_S) |
DQRR_DQ_SRC_DCP | SDQCR_DQ_SRC_CHAN(0));
bus_write_4(regs, QCSP_DQRR_VDQCR, 0);
bus_write_4(regs, QCSP_DQRR_PDQCR, 0);
sc->sc_dqrr.ring =
(struct qman_dqrr_entry *)(sc->sc_base.sc_ce_va + QCSP_DQRR_N(0));
qman_dqrr_ring_init(&sc->sc_dqrr, &sc->sc_base);
/* Set DQRR max fill to 15 */
reg = bus_read_4(regs, QCSP_CFG);
reg |= (0xf << CFG_DQRR_MF_S);
bus_write_4(regs, QCSP_CFG, reg);
for (int i = 0; i < QMAN_DQRR_COUNT; i++)
__asm __volatile ("dcbi 0,%0" :: "r"(&sc->sc_dqrr.ring[i]) : "memory");
return (0);
}
int
qman_portals_attach(device_t dev)
qman_portal_attach(device_t dev, int cpu)
{
struct dpaa_portals_softc *sc;
struct qman_portal_softc *sc = device_get_softc(dev);
union qman_mc_command *cr;
pcell_t cell;
phandle_t node;
sc = qp_sc = device_get_softc(dev);
/* Map bman portal to physical address space */
if (law_enable(OCP85XX_TGTIF_QMAN, sc->sc_dp_pa, sc->sc_dp_size)) {
qman_portals_detach(dev);
sc->sc_base.sc_cpu = cpu;
dpaa_portal_alloc_res(dev, cpu);
qman_eqcr_init(sc, CFG_EPM_VB1, 0, 0);
qman_dqrr_init(sc);
bus_setup_intr(dev, sc->sc_base.sc_ires, INTR_TYPE_NET | INTR_MPSAFE,
NULL, qman_portal_isr, sc, &sc->sc_base.sc_intr_cookie);
bus_bind_intr(dev, sc->sc_base.sc_ires, cpu);
node = ofw_bus_get_node(dev);
if (OF_getencprop(node, "cell-index", &cell, sizeof(cell)) <= 0) {
device_printf(dev, "missing 'cell-index' property\n");
return (ENXIO);
}
/* Set portal properties for XX_VirtToPhys() */
XX_PortalSetInfo(dev);
sc->sc_affine_channel = cell;
DPCPU_ID_SET(cpu, qman_affine_portal, dev);
bus_write_4(sc->sc_base.sc_mres[1], QCSP_IER,
QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI | QM_PIRQ_CSCI |
QM_PIRQ_DQRI);
bus_write_4(sc->sc_base.sc_mres[1], QCSP_ISDR, 0);
/* Initialize the MC polarity bit, it may not be 0. */
cr = (union qman_mc_command *)(sc->sc_base.sc_ce_va + QCSP_CR);
sc->sc_mc.polarity =
(cr->common.verb & QMAN_MC_VERB_VBIT) ^ QMAN_MC_VERB_VBIT;
/* TODO: LIODN. Fake it for now */
qman_set_sdest(sc->sc_affine_channel, cpu);
bus_attach_children(dev);
return (0);
}
int
qman_portal_detach(device_t dev)
{
struct qman_portal_softc *sc;
int i;
sc = device_get_softc(dev);
/* TODO: Unmap TLB regions */
thread_lock(curthread);
sched_bind(curthread, sc->sc_base.sc_cpu);
thread_unlock(curthread);
if (sc->sc_base.sc_ires != NULL)
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_base.sc_ires);
for (i = 0; i < nitems(sc->sc_base.sc_mres); i++) {
if (sc->sc_base.sc_mres[i] != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
i, sc->sc_base.sc_mres[i]);
}
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
return (0);
}
static void
qman_portal_isr(void *arg)
{
struct qman_portal_softc *sc = arg;
qman_portal_loop_rings(sc);
}
int
qman_portals_detach(device_t dev)
qman_portal_fq_enqueue(device_t dev, struct qman_fq *fq, struct dpaa_fd *frame)
{
struct dpaa_portals_softc *sc;
int i;
struct qman_portal_softc *sc = device_get_softc(dev);
struct qman_eqcr_entry *eqcr;
qp_sc = NULL;
sc = device_get_softc(dev);
for (i = 0; i < ARRAY_SIZE(sc->sc_dp); i++) {
if (sc->sc_dp[i].dp_ph != NULL) {
thread_lock(curthread);
sched_bind(curthread, i);
thread_unlock(curthread);
QM_PORTAL_Free(sc->sc_dp[i].dp_ph);
thread_lock(curthread);
sched_unbind(curthread);
thread_unlock(curthread);
}
if (sc->sc_dp[i].dp_ires != NULL) {
XX_DeallocIntr((uintptr_t)sc->sc_dp[i].dp_ires);
bus_release_resource(dev, SYS_RES_IRQ,
sc->sc_dp[i].dp_irid, sc->sc_dp[i].dp_ires);
}
}
for (i = 0; i < ARRAY_SIZE(sc->sc_rres); i++) {
if (sc->sc_rres[i] != NULL)
bus_release_resource(dev, SYS_RES_MEMORY,
sc->sc_rrid[i],
sc->sc_rres[i]);
}
/* Get available... */
eqcr = qman_eqcr_start(&sc->sc_eqcr, &sc->sc_base);
if (eqcr == NULL)
return (EBUSY);
eqcr->fd = *frame;
eqcr->fqid = fq->fqid;
qman_eqcr_commit(&sc->sc_eqcr, QM_EQCR_VERB_CMD_ENQUEUE);
return (0);
}
t_Handle
qman_portal_setup(struct qman_softc *qsc)
static int
qman_portal_loop_dqrr(struct qman_portal_softc *sc)
{
struct dpaa_portals_softc *sc;
t_QmPortalParam qpp;
unsigned int cpu;
uintptr_t p;
t_Handle portal;
struct qman_dqrr_entry *dqrr;
struct qman_dqrr_entry *base;
struct qman_fq *fq;
int ci = bus_read_4(sc->sc_base.sc_mres[1], QCSP_DQRR_CI_CINH) &
DQRR_CI_CI_M;
int pi = bus_read_4(sc->sc_base.sc_mres[1], QCSP_DQRR_PI_CINH) &
DQRR_PI_PI_M;
/* Return NULL if we're not ready or while detach */
if (qp_sc == NULL)
return (NULL);
base = sc->sc_dqrr.ring;
do {
dqrr = &base[ci];
dpaa_flush_line(dqrr);
dpaa_touch_line(dqrr);
if ((dqrr->stat & QMAN_DQRR_STAT_HAS_FRAME)) {
fq = qman_fq_from_index(dqrr->fqid);
if (fq != NULL && fq->cb.dqrr != NULL) {
fq->cb.dqrr(sc->sc_base.sc_dev, fq,
&dqrr->fd, fq->cb.ctx);
}
} else
break;
ci = (ci + 1) & DQRR_CI_CI_M;
bus_write_4(sc->sc_base.sc_mres[1], QCSP_DQRR_CI_CINH, ci);
} while (ci != pi);
sc = qp_sc;
sched_pin();
portal = NULL;
cpu = PCPU_GET(cpuid);
/* Check if portal is ready */
while (atomic_cmpset_acq_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph,
0, -1) == 0) {
p = atomic_load_acq_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph);
/* Return if portal is already initialized */
if (p != 0 && p != -1) {
sched_unpin();
return ((t_Handle)p);
}
/* Not inititialized and "owned" by another thread */
sched_relinquish(curthread);
}
/* Map portal registers */
dpaa_portal_map_registers(sc);
/* Configure and initialize portal */
qpp.ceBaseAddress = rman_get_bushandle(sc->sc_rres[0]);
qpp.ciBaseAddress = rman_get_bushandle(sc->sc_rres[1]);
qpp.h_Qm = qsc->sc_qh;
qpp.swPortalId = cpu;
qpp.irq = (uintptr_t)sc->sc_dp[cpu].dp_ires;
qpp.fdLiodnOffset = 0;
qpp.f_DfltFrame = qman_received_frame_callback;
qpp.f_RejectedFrame = qman_rejected_frame_callback;
qpp.h_App = qsc;
portal = QM_PORTAL_Config(&qpp);
if (portal == NULL)
goto err;
if (QM_PORTAL_Init(portal) != E_OK)
goto err;
if (QM_PORTAL_AddPoolChannel(portal, QMAN_COMMON_POOL_CHANNEL) != E_OK)
goto err;
atomic_store_rel_ptr((uintptr_t *)&sc->sc_dp[cpu].dp_ph,
(uintptr_t)portal);
sched_unpin();
return (portal);
err:
if (portal != NULL)
QM_PORTAL_Free(portal);
atomic_store_rel_32((uint32_t *)&sc->sc_dp[cpu].dp_ph, 0);
sched_unpin();
return (NULL);
return (0);
}
static void
qman_portal_loop_rings(struct qman_portal_softc *sc)
{
uint32_t isr;
isr = bus_read_4(sc->sc_base.sc_mres[1], QCSP_ISR);
/* Handle DQRR first. */
if ((isr & QM_PIRQ_DQRI)) {
qman_portal_loop_dqrr(sc);
}
if ((isr & QM_PIRQ_CSCI)) {
}
if ((isr & QM_PIRQ_EQRI)) {
qman_eqcr_update(&sc->sc_eqcr, &sc->sc_base);
}
bus_write_4(sc->sc_base.sc_mres[1], QCSP_ISR, isr);
}
/* MC commands */
/* Assumes pinned */
union qman_mc_result *
qman_portal_mc_send_raw(device_t dev, union qman_mc_command *c)
{
struct qman_portal_softc *sc;
int res_idx;
union qman_mc_result *rr;
union qman_mc_command *cr;
int timeout = 10000;
uint8_t verb;
sc = device_get_softc(dev);
verb = c->common.verb;
c->common.verb = 0;
cr = (union qman_mc_command *)(sc->sc_base.sc_ce_va + QCSP_CR);
dpaa_zero_line(cr);
*cr = *c;
dpaa_lw_barrier();
cr->common.verb = verb | sc->sc_mc.polarity;
res_idx = (sc->sc_mc.polarity ? 1 : 0);
sc->sc_mc.polarity ^= QMAN_MC_VERB_VBIT;
dpaa_flush_line(cr);
dpaa_touch_line(cr);
rr = (union qman_mc_result *)(sc->sc_base.sc_ce_va + QCSP_RR(res_idx));
for (; timeout > 0; --timeout) {
dpaa_flush_line(rr);
if (rr->common.verb != 0)
break;
}
if (timeout == 0)
return (NULL);
return (rr);
}
void
qman_portal_static_dequeue_channel(device_t dev, int channel)
{
struct qman_portal_softc *sc = device_get_softc(dev);
uint32_t reg;
reg = bus_read_4(sc->sc_base.sc_mres[1], QCSP_DQRR_SDQCR);
reg |= (1 << (15 - (channel - qman_channel_base)));
bus_write_4(sc->sc_base.sc_mres[1], QCSP_DQRR_SDQCR, reg);
}
void
qman_portal_static_dequeue_rm_channel(device_t dev, int channel)
{
struct qman_portal_softc *sc = device_get_softc(dev);
uint32_t reg;
reg = bus_read_4(sc->sc_base.sc_mres[1], QCSP_DQRR_SDQCR);
reg &= ~(1 << (15 - (channel - qman_channel_base)));
bus_write_4(sc->sc_base.sc_mres[1], QCSP_DQRR_SDQCR, reg);
}
DB_SHOW_COMMAND(fqid, qman_show_fqid)
{
union qman_mc_command cmd;
union qman_mc_result *res;
union qman_mc_result save_res;
device_t portal;
if (!have_addr)
return;
bzero(&cmd, sizeof(cmd));
cmd.query_fq_np.fqid = addr;
/* Ensure we have got QMan port initialized */
portal = DPCPU_GET(qman_affine_portal);
res = qman_portal_mc_send_raw(portal, &cmd);
if (res != NULL)
save_res = *res;
/* Dump all NP fields */
if (res != NULL && save_res.query_fq_np.rslt == 0xf0) {
db_printf("FQID: %d\n", (int)addr);
db_printf(" State: %x\n", save_res.query_fq_np.state);
db_printf(" Link: %x\n", save_res.query_fq_np.fqd_link);
db_printf(" ODP_SEQ: %x\n", save_res.query_fq_np.odp_seq);
db_printf(" ORP_NESN: %x\n", save_res.query_fq_np.orp_nesn);
db_printf(" ORP_EA_HSEQ: %x\n",
save_res.query_fq_np.orp_ea_hseq);
db_printf(" ORP_EA_TSEQ: %x\n",
save_res.query_fq_np.orp_ea_tseq);
db_printf(" ORP_EA_HPTR: %x\n",
save_res.query_fq_np.orp_ea_hptr);
db_printf(" ORP_EA_TPTR: %x\n",
save_res.query_fq_np.orp_ea_tptr);
db_printf(" pfdr_hptr: %x\n", save_res.query_fq_np.pfdr_hptr);
db_printf(" pfdr_tptr: %x\n", save_res.query_fq_np.pfdr_tptr);
db_printf(" IS: %x\n", save_res.query_fq_np.is);
db_printf(" ICS_SURP: %x\n", save_res.query_fq_np.ics_surp);
db_printf(" byte_cnt: %x\n", save_res.query_fq_np.byte_cnt);
db_printf(" frm_cnt: %x\n", save_res.query_fq_np.frm_cnt);
db_printf(" ra1_sfdr: %x\n", save_res.query_fq_np.ra1_sfdr);
db_printf(" ra2_sfdr: %x\n", save_res.query_fq_np.ra2_sfdr);
db_printf(" od1_sfdr: %x\n", save_res.query_fq_np.od1_sfdr);
db_printf(" od2_sfdr: %x\n", save_res.query_fq_np.od2_sfdr);
db_printf(" od3_sfdr: %x\n", save_res.query_fq_np.od3_sfdr);
}
}
+167
View File
@@ -0,0 +1,167 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2026 Justin Hibbits
*/
#ifndef QMAN_VAR_H
#define QMAN_VAR_H
#include "dpaa_common.h"
#include "portals.h"
struct qman_eqcr_entry {
uint8_t verb;
uint8_t dca;
uint16_t seqnum;
uint32_t orp;
uint32_t fqid;
uint32_t tag;
struct dpaa_fd fd;
uint8_t _rsvd[32];
};
_Static_assert(sizeof(struct qman_eqcr_entry) == 64, "EQCR entry mis-sized");
DPAA_RING_DECLARE(qman_eqcr);
DPAA_RING_DECLARE(qman_dqrr);
DPAA_RING_DECLARE(qman_mr);
union qman_mc_command {
struct {
uint8_t verb;
uint8_t data[63];
} common;
struct {
uint8_t verb;
uint8_t _rsvd0;
uint16_t we_mask;
uint32_t fqid; /* Only bottom 24 bits allowed */
uint16_t count;
uint8_t orpc;
uint8_t cgid;
uint16_t fq_ctrl;
uint16_t dest_chan:13;
uint16_t dest_wq:3;
uint16_t ics_cred;
uint16_t td_thresh_oac;
uint32_t context_b;
uint32_t context_a;
uint8_t _rsvd1[32];
} init_fq;
struct {
uint8_t verb;
uint8_t _rsvd0[3];
uint32_t fqid; /* Only bottom 24 bits used */
uint8_t _rsvd1[56];
} query_fq;
struct {
uint8_t verb;
uint8_t _rsvd0[3];
uint32_t fqid;
uint8_t _rsvd1[56];
} query_fq_np;
struct {
uint8_t verb;
uint8_t _rsvd0[3];
uint32_t fqid;
uint8_t _rsvd1;
uint8_t count;
uint8_t _rsvd2[10];
uint32_t context_b;
uint8_t _rsvd3[40];
} alter_fqs;
};
union qman_mc_result {
struct {
uint8_t verb;
uint8_t data[63];
} common;
struct {
uint8_t verb;
uint8_t rslt;
uint8_t _rsvd[62];
} init_fq;
struct {
uint8_t verb;
uint8_t rslt;
uint8_t _rsvd0[8];
uint8_t orpc;
uint8_t cgid;
uint16_t fq_ctrl;
uint16_t dest_wq;
uint16_t ics_cred;
uint16_t td_thresh;
uint32_t context_b;
uint32_t context_a;
uint16_t oac;
uint8_t _rsvd1[30];
} query_fq;
struct {
uint8_t verb;
uint8_t rslt;
uint8_t _rsvd0;
uint8_t state;
uint32_t fqd_link;
uint16_t odp_seq;
uint16_t orp_nesn;
uint16_t orp_ea_hseq;
uint16_t orp_ea_tseq;
uint32_t orp_ea_hptr;
uint32_t orp_ea_tptr;
uint32_t pfdr_hptr;
uint32_t pfdr_tptr;
uint8_t _rsvd1[5];
uint8_t is;
uint16_t ics_surp;
uint32_t byte_cnt;
uint32_t frm_cnt;
uint32_t _rsvd2;
uint16_t ra1_sfdr;
uint16_t ra2_sfdr;
uint16_t _rsvd3;
uint16_t od1_sfdr;
uint16_t od2_sfdr;
uint16_t od3_sfdr;
} query_fq_np;
struct {
uint8_t verb;
uint8_t rslt;
uint8_t fqs;
uint8_t _rsvd[61];
} alter_fqs;
};
struct qman_mc {
uint8_t polarity;
bool busy;
};
struct qman_fq {
uint32_t fqid;
struct qman_cb cb;
};
struct qman_portal_softc {
struct dpaa_portal_softc sc_base;
/* Rings (Enqueue, Dequeue, Message */
struct qman_eqcr_ring sc_eqcr;
struct qman_dqrr_ring sc_dqrr;
struct qman_mr_ring sc_mr;
struct qman_mc sc_mc;
int sc_affine_channel;
};
struct qman_fq *qman_fq_from_index(uint32_t fqid);
union qman_mc_result *
qman_portal_mc_send_raw(device_t, union qman_mc_command *);
int qman_portal_fq_enqueue(device_t, struct qman_fq *, struct dpaa_fd *);
void qman_portal_static_dequeue_channel(device_t, int);
void qman_portal_static_dequeue_rm_channel(device_t dev, int channel);
extern int qman_channel_base;
DPCPU_DECLARE(device_t, qman_affine_portal);
#endif /* QMAN_VAR_H */
+3 -1
View File
@@ -10,7 +10,6 @@ ident QORIQ64
machine powerpc powerpc64
include "dpaa/config.dpaa"
makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
makeoptions WITH_CTF=1
#makeoptions WERROR="-Werror -Wno-format"
@@ -22,6 +21,7 @@ options _KPOSIX_PRIORITY_SCHEDULING
options ALT_BREAK_TO_DEBUGGER
options BREAK_TO_DEBUGGER
options BOOTP
#options BOOTP_WIRED_TO=memac0
options BOOTP_NFSROOT
#options BOOTP_NFSV3
options CD9660
@@ -134,3 +134,5 @@ device uinput # install /dev/uinput cdev
options HID_DEBUG # enable debug msgs
device hid # Generic HID support
device hidbus # Generic HID bus
options BOOTVERBOSE
-99
View File
@@ -1,99 +0,0 @@
#
# Common kernel config for Freescale QorIQ DPAA development boards like the
# P2041RDB, P3041DS and P5020DS.
#
# This is not standalone kernel config. Use it only for including
# purposes.
#
cpu BOOKE
cpu BOOKE_E500
machine powerpc powerpc
#makeoptions DEBUG=-g #Build kernel with gdb(1) debug symbols
#makeoptions WERROR="-Werror -Wno-format"
makeoptions NO_MODULES=yes
# Platform support
options MPC85XX #Freescale SoC family
options SMP #Symmetric Multi Processing
#options SCHED_4BSD #4BSD scheduler
options INET #InterNETworking
options INET6 #IPv6 communications protocols
options FFS #Berkeley Fast Filesystem
options NFSCL #New Network Filesystem Client
options SOFTUPDATES #Enable FFS soft updates support
options PROCFS #Process filesystem (requires PSEUDOFS)
options PSEUDOFS #Pseudo-filesystem framework
options GEOM_PART_GPT #GUID Partition Tables.
options GEOM_LABEL
options COMPAT_43 #Compatible with BSD 4.3 [KEEP THIS!]
options SYSVSHM #SYSV-style shared memory
options SYSVMSG #SYSV-style message queues
options SYSVSEM #SYSV-style semaphores
options _KPOSIX_PRIORITY_SCHEDULING #Posix P1003_1B real-time extensions
options KDB #Enable the kernel debugger
options DDB #Support DDB
options GDB
options ALT_BREAK_TO_DEBUGGER
options BREAK_TO_DEBUGGER
options DIAGNOSTIC
options INVARIANTS #Enable calls of extra sanity checking
options INVARIANT_SUPPORT #Extra sanity checks of internal structures, required by INVARIANTS
options KTR
options KTR_COMPILE=0xffffffff
options KTR_MASK=KTR_PMAP
options KTR_ENTRIES=8192
options WITNESS #Enable checks to detect deadlocks and cycles
#options WITNESS_KDB
# RamFS Root
#options GEOM_UZIP
#options MD_ROOT
#options MD_ROOT_SIZE=10240
# Netbooting
options BOOTP
options BOOTP_NFSROOT
options BOOTP_NFSV3
options BOOTP_WIRED_TO=dtsec3
options NFS_ROOT
# Block devices
device mmc
device mmcsd
device sdhci
# Network devices
device miibus # MII bus support
device iflib
device em
# I2C support
device iicbus
device iic
device uart
device ehci
device usb
device scbus
device da
device umass
device pty
device cfi
device pci
# Pseudo devices
device ether # Ethernet support
device loop # Network loopback
device bpf # Berkeley packet filter
device md # Memory "disks"
-24
View File
@@ -1,24 +0,0 @@
files "dpaa/files.dpaa"
makeoptions DPAA_COMPILE_CMD="${LINUXKPI_C} ${DPAAWARNFLAGS} \
-Wno-cast-qual -Wno-unused-function -Wno-init-self \
-include $S/contrib/ncsw/build/dflags.h \
-Wno-error=missing-prototypes \
-I$S/contrib/ncsw/build/ \
-I$S/contrib/ncsw/inc \
-I$S/contrib/ncsw/inc/cores \
-I$S/contrib/ncsw/inc/etc \
-I$S/contrib/ncsw/inc/Peripherals \
-I$S/contrib/ncsw/etc \
-I$S/contrib/ncsw/Peripherals/BM \
-I$S/contrib/ncsw/Peripherals/FM \
-I$S/contrib/ncsw/Peripherals/FM/HC \
-I$S/contrib/ncsw/Peripherals/FM/inc \
-I$S/contrib/ncsw/Peripherals/FM/MAC \
-I$S/contrib/ncsw/Peripherals/FM/Pcd \
-I$S/contrib/ncsw/Peripherals/FM/Port \
-I$S/contrib/ncsw/Peripherals/FM/Rtc \
-I$S/contrib/ncsw/Peripherals/QM \
-I$S/contrib/ncsw/inc/flib \
-I$S/contrib/ncsw/inc/integrations"
-122
View File
@@ -1,122 +0,0 @@
# NetCommSw drivers
contrib/ncsw/etc/error.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/etc/ncsw_list.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/etc/memcpy.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/etc/mm.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/etc/ncsw_mem.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/etc/sprint.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/BM/bm.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/BM/bman_low.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/BM/bm_pool.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/BM/bm_portal.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/SP/fm_sp.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Rtc/fm_rtc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Rtc/fman_rtc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Port/fman_port.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Port/fm_port.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Port/fm_port_im.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fm_cc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fm_kg.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fman_kg.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fm_manip.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fm_pcd.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fm_plcr.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fm_prs.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/Pcd/fman_prs.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/dtsec.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/dtsec_mii_acc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/fman_dtsec.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/fman_dtsec_mii_acc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/fm_mac.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/fman_dtsec.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/tgec.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/tgec_mii_acc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/MAC/fman_tgec.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/HC/hc.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/fm_muram.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/fm_ncsw.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/FM/fman_ncsw.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD} ${NO_WMISLEADING_INDENTATION}"
contrib/ncsw/Peripherals/QM/qm.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/Peripherals/QM/qm_portal_fqr.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/user/env/stdlib.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/user/env/xx.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
contrib/ncsw/user/env/core.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
# FreeBSD Wrappers
dev/dpaa/portals_common.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/bman_portals.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/bman.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/bman_fdt.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/qman_portals.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/qman.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/qman_fdt.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/fman.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/fman_mdio.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/fman_fdt.c optional dpaa fdt \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/if_dtsec.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/if_dtsec_im.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/if_dtsec_rm.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/if_dtsec_fdt.c optional dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
# Examples
dev/dpaa/bman-example.c optional bman_example dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"
dev/dpaa/qman-example.c optional qman_example dpaa \
no-depend compile-with "${DPAA_COMPILE_CMD}"