diff options
| author | Justin Hibbits <jhibbits@FreeBSD.org> | 2026-04-24 03:38:19 +0000 |
|---|---|---|
| committer | Justin Hibbits <jhibbits@FreeBSD.org> | 2026-05-03 19:09:29 +0000 |
| commit | fd8d34ce272ba40f3e0218198ba542a29c390a4a (patch) | |
| tree | d5860795f3a55f35784e6a87dc8629537197f3fc /sys/dev/dpaa/fman.c | |
| parent | d22051b18e9b7512c5cf033d7b6eee2df03c985b (diff) | |
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.
Diffstat (limited to 'sys/dev/dpaa/fman.c')
| -rw-r--r-- | sys/dev/dpaa/fman.c | 850 |
1 files changed, 627 insertions, 223 deletions
diff --git a/sys/dev/dpaa/fman.c b/sys/dev/dpaa/fman.c index 393c28487ba9..c0c7ec8cb17b 100644 --- a/sys/dev/dpaa/fman.c +++ b/sys/dev/dpaa/fman.c @@ -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> @@ -33,6 +13,7 @@ #include <sys/malloc.h> #include <dev/fdt/simplebus.h> +#include <dev/fdt/fdt_common.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> @@ -40,50 +21,140 @@ #include "opt_platform.h" -#include <contrib/ncsw/inc/Peripherals/fm_ext.h> -#include <contrib/ncsw/inc/Peripherals/fm_muram_ext.h> -#include <contrib/ncsw/inc/ncsw_ext.h> -#include <contrib/ncsw/integrations/fman_ucode.h> +#include <powerpc/mpc85xx/mpc85xx.h> #include "fman.h" +#define FMAN_BMI_OFFSET 0x80000 +#define FMAN_QMI_OFFSET 0x80400 +#define FMAN_KG_OFFSET 0xc1000 +#define FMAN_DMA_OFFSET 0xc2000 +#define FMAN_FPM_OFFSET 0xc3000 +#define FMAN_IMEM_OFFSET 0xc4000 +#define FMAN_HWP_OFFSET 0xc7000 +#define FMAN_CGP_OFFSET 0xdb000 + +#define FM_IP_REV_1 (FMAN_FPM_OFFSET + 0x0c4) +#define IP_REV_1_MAJ_M 0x0000ff00 +#define IP_REV_1_MAJ_S 8 +#define IP_REV_1_MIN_M 0x000000ff +#define FM_RSTC (FMAN_FPM_OFFSET + 0x0cc) +#define FM_RSTC_FM_RESET 0x80000000 + +#define FMBM_INIT (FMAN_BMI_OFFSET + 0x000) +#define INIT_STR 0x80000000 +#define FMBM_CFG1 (FMAN_BMI_OFFSET + 0x0004) +#define FBPS_M 0x07ff0000 +#define FBPS_S 16 +#define FBPO_M 0x000007ff +#define FMBM_CFG2 (FMAN_BMI_OFFSET + 0x0008) +#define TNTSKS_M 0x007f0000 +#define TNTSKS_S 16 +#define FMBM_IEVR (FMAN_BMI_OFFSET + 0x0020) +#define IEVR_SPEC 0x80000000 +#define IEVR_LEC 0x40000000 +#define IEVR_STEC 0x20000000 +#define IEVR_DEC 0x10000000 +#define FMBM_IER (FMAN_BMI_OFFSET + 0x0024) +#define IER_SPECE 0x80000000 +#define IER_LECE 0x40000000 +#define IER_STECE 0x20000000 +#define IER_DECE 0x10000000 +#define FMBM_PP(n) (FMAN_BMI_OFFSET + 0x104 + ((n - 1) * 4)) +#define PP_MXT_M 0x3f000000 +#define PP_MXT_S 24 +#define PP_EXT_M 0x000f0000 +#define PP_EXT_S 16 +#define PP_MXD_M 0x00000f00 +#define PP_MXD_S 8 +#define PP_EXD_M 0x0000000f +#define FMBM_PFS(n) (FMAN_BMI_OFFSET + 0x204 + ((n - 1) * 4)) +#define PFS_EXBS_M 0x03ff0000 +#define PFS_EXBS_S 16 +#define PFS_IFSZ_M 0x000003ff +#define FMQM_GC (FMAN_QMI_OFFSET + 0x000) +#define GC_STEN 0x10000000 +#define GC_ENQ_THR_S 8 +#define GC_ENQ_THR_M 0x00003f00 +#define GC_DEQ_THR_M 0x0000003f +#define FMQM_EIE (FMAN_QMI_OFFSET + 0x008) +#define EIE_DEE 0x80000000 +#define EIE_DFUPE 0x40000000 +#define FMQM_EIEN (FMAN_QMI_OFFSET + 0x00c) +#define EIEN_DEE 0x80000000 +#define EIEN_DFUPE 0x40000000 +#define FMQM_IE +#define IRAM_ADDR (FMAN_IMEM_OFFSET + 0x000) +#define IADD_AIE 0x80000000 +#define IRAM_DATA (FMAN_IMEM_OFFSET + 0x004) +#define IRAM_READY (FMAN_IMEM_OFFSET + 0x0c) +#define IREADY_READY 0x80000000 + +#define FMPR_RPIMAC (FMAN_HWP_OFFSET + 0x844) +#define HWP_RPIMAC_PEN 0x00000001 + +#define FMDM_SR (FMAN_DMA_OFFSET + 0x000) +#define SR_CMDQNE 0x10000000 +#define SR_BER 0x08000000 +#define SR_RDB_ECC 0x04000000 +#define SR_WRB_SECC 0x02000000 +#define FMDM_MR (FMAN_DMA_OFFSET + 0x004) +#define MR_CEN_M 0x0000e000 +#define MR_CEN_S 13 +#define FMDM_SETR (FMAN_DMA_OFFSET + 0x010) +#define FMDM_EBCR (FMAN_DMA_OFFSET + 0x2c) +#define FMDM_PLRn(n) (FMAN_DMA_OFFSET + 0x060 + (4 * (n / 2))) +#define PLRN_LIODN_M(n) (0xfff << PLRN_LIODN_S(n)) +#define PLRN_LIODN_S(n) ((n & 1) ? 0 : 16) + +#define FMFP_TSC1 (FMAN_FPM_OFFSET + 0x060) +#define TSC1_TEN 0x80000000 +#define FMFP_TSC2 (FMAN_FPM_OFFSET + 0x064) +#define TSC2_TSIV_INT_S 16 +#define FM_RCR (FMAN_FPM_OFFSET + 0x070) +#define RCR_FEE 0x80000000 +#define RCR_IEE 0x40000000 +#define RCR_MET 0x20000000 +#define RCR_IET 0x10000000 +#define RCR_SFE 0x08000000 +#define FMFP_EE (FMAN_FPM_OFFSET + 0x0dc) +#define EE_DECC 0x80000000 +#define EE_STL 0x40000000 +#define EE_SECC 0x20000000 +#define EE_RFM 0x00010000 +#define EE_DECC_EN 0x00008000 +#define EE_STL_EN 0x00004000 +#define EE_SECC_EN 0x00002000 +#define EE_EHM 0x00000008 +#define EE_CER 0x00000002 +#define EE_DER 0x00000001 +#define FMFP_CEV0 (FMAN_FPM_OFFSET + 0x0e0) +#define FMFP_CEV1 (FMAN_FPM_OFFSET + 0x0e4) +#define FMFP_CEV2 (FMAN_FPM_OFFSET + 0x0e8) +#define FMFP_CEV3 (FMAN_FPM_OFFSET + 0x0ec) + +/* DMA constants */ +#define DMA_CAM_UNITS 8 +#define DMA_CAM_SIZE 64 +#define DMA_CAM_ALIGN 64 + + +/* Timestamp counter */ +#define FM_TIMESTAMP_1US_BIT 8 static MALLOC_DEFINE(M_FMAN, "fman", "fman devices information"); +static void fman_intr(void *arg); + /** * @group FMan private defines. * @{ */ -enum fman_irq_enum { - FMAN_IRQ_NUM = 0, - FMAN_ERR_IRQ_NUM = 1 -}; - -enum fman_mu_ram_map { - FMAN_MURAM_OFF = 0x0, - FMAN_MURAM_SIZE = 0x28000 -}; - -struct fman_config { - device_t fman_device; - uintptr_t mem_base_addr; - uintptr_t irq_num; - uintptr_t err_irq_num; - uint8_t fm_id; - t_FmExceptionsCallback *exception_callback; - t_FmBusErrorCallback *bus_error_callback; -}; /** * @group FMan private methods/members. * @{ */ -/** - * Frame Manager firmware. - * We use the same firmware for both P3041 and P2041 devices. - */ -const uint32_t fman_firmware[] = FMAN_UC_IMG; -const uint32_t fman_firmware_size = sizeof(fman_firmware); int fman_activate_resource(device_t bus, device_t child, struct resource *res) @@ -125,7 +196,7 @@ fman_release_resource(device_t bus, device_t child, struct resource *res) passthrough = (device_get_parent(child) != bus); rl = BUS_GET_RESOURCE_LIST(bus, child); if (rman_get_type(res) != SYS_RES_IRQ) { - if ((rman_get_flags(res) & RF_ACTIVE) != 0 ){ + if ((rman_get_flags(res) & RF_ACTIVE) != 0) { rv = bus_deactivate_resource(child, res); if (rv != 0) return (rv); @@ -209,202 +280,400 @@ fman_alloc_resource(device_t bus, device_t child, int type, int rid, return (NULL); } + static int -fman_fill_ranges(phandle_t node, struct simplebus_softc *sc) +fman_get_revision_major(struct fman_softc *sc) { - int host_address_cells; - cell_t *base_ranges; - ssize_t nbase_ranges; - int err; - int i, j, k; + uint32_t reg; - err = OF_searchencprop(OF_parent(node), "#address-cells", - &host_address_cells, sizeof(host_address_cells)); - if (err <= 0) - return (-1); + reg = bus_read_4(sc->mem_res, FM_IP_REV_1); - nbase_ranges = OF_getproplen(node, "ranges"); - if (nbase_ranges < 0) - return (-1); - sc->nranges = nbase_ranges / sizeof(cell_t) / - (sc->acells + host_address_cells + sc->scells); - if (sc->nranges == 0) - return (0); + return ((reg & IP_REV_1_MAJ_M) >> IP_REV_1_MAJ_S); +} - sc->ranges = malloc(sc->nranges * sizeof(sc->ranges[0]), - M_DEVBUF, M_WAITOK); - base_ranges = malloc(nbase_ranges, M_DEVBUF, M_WAITOK); - OF_getencprop(node, "ranges", base_ranges, nbase_ranges); +static int +fman_get_revision_minor(struct fman_softc *sc) +{ + uint32_t reg; - for (i = 0, j = 0; i < sc->nranges; i++) { - sc->ranges[i].bus = 0; - for (k = 0; k < sc->acells; k++) { - sc->ranges[i].bus <<= 32; - sc->ranges[i].bus |= base_ranges[j++]; - } - sc->ranges[i].host = 0; - for (k = 0; k < host_address_cells; k++) { - sc->ranges[i].host <<= 32; - sc->ranges[i].host |= base_ranges[j++]; - } - sc->ranges[i].size = 0; - for (k = 0; k < sc->scells; k++) { - sc->ranges[i].size <<= 32; - sc->ranges[i].size |= base_ranges[j++]; + reg = bus_read_4(sc->mem_res, FM_IP_REV_1); + + return ((reg & IP_REV_1_MIN_M)); +} + +static void +fman_fill_soc_params(struct fman_softc *sc) +{ + + switch (sc->sc_revision_major) { + case 2: + sc->bmi_max_fifo_size = 160 * 1024; + sc->iram_size = 64 * 1024; + sc->dma_thresh_max_commq = 31; + sc->dma_thresh_max_buf = 127; + sc->qmi_max_tnums = 64; + sc->qmi_def_tnums_thresh = 48; + sc->bmi_max_tasks = 128; + sc->max_open_dmas = 32; + sc->dma_cam_num_entries = 32; + sc->port_cgs = 256; + sc->rx_ports = 5; + sc->total_fifo_size = 100 * 1024; + break; + case 3: + sc->bmi_max_fifo_size = 160 * 1024; + sc->iram_size = 64 * 1024; + sc->dma_thresh_max_commq = 31; + sc->dma_thresh_max_buf = 127; + sc->qmi_max_tnums = 64; + sc->qmi_def_tnums_thresh = 48; + sc->bmi_max_tasks = 128; + sc->max_open_dmas = 32; + sc->dma_cam_num_entries = 32; + sc->port_cgs = 256; + sc->rx_ports = 6; + sc->total_fifo_size = 136 * 1024; + break; + case 6: + sc->dma_thresh_max_commq = 31; + sc->dma_thresh_max_buf = 127; + sc->qmi_max_tnums = 64; + sc->qmi_def_tnums_thresh = 48; + sc->dma_cam_num_entries = 64; + sc->port_cgs = 256; + switch (sc->sc_revision_minor) { + case 1: + case 4: + sc->bmi_max_fifo_size = 192 * 1024; + sc->bmi_max_tasks = 64; + sc->max_open_dmas = 32; + sc->rx_ports = 5; + sc->total_fifo_size = 156 * 1024; + if (sc->sc_revision_minor == 1) + sc->iram_size = 32 * 1024; + else + sc->iram_size = 64 * 1024; + break; + case 0: + case 2: + case 3: + sc->bmi_max_fifo_size = 384 * 1024; + sc->bmi_max_tasks = 128; + sc->max_open_dmas = 84; + sc->rx_ports = 8; + sc->iram_size = 64 * 1024; + sc->total_fifo_size = 295 * 1024; + break; + default: + device_printf(sc->sc_base.dev, + "Unsupported FManv3 revision: %d\n", + sc->sc_revision_minor); + break; } + break; + default: + device_printf(sc->sc_base.dev, + "Unsupported FMan version: %d\n", sc->sc_revision_major); + break; } - - free(base_ranges, M_DEVBUF); - return (sc->nranges); } -static t_Handle -fman_init(struct fman_softc *sc, struct fman_config *cfg) +static int +fman_reset(struct fman_softc *sc) { - phandle_t node; - t_FmParams fm_params; - t_Handle muram_handle, fm_handle; - t_Error error; - t_FmRevisionInfo revision_info; - uint16_t clock; - uint32_t tmp, mod; + unsigned int count; + + if (sc->sc_revision_major < 6) { + bus_write_4(sc->mem_res, FM_RSTC, FM_RSTC_FM_RESET); + count = 100; + do { + DELAY(1); + } while ((bus_read_4(sc->mem_res, FM_RSTC) & FM_RSTC_FM_RESET) && + --count); + if (count == 0) + return (EBUSY); + return (0); + } else { +#ifdef __powerpc__ + phandle_t node; + u_long base, size; + uint32_t devdisr2; +#define GUTS_DEVDISR2 0x0074 +#define DEVDISR2_FMAN1 0xfcc00000 +#define DEVDISR2_FMAN2 0x000fcc00 + + node = ofw_bus_get_node(device_get_parent(sc->sc_base.dev)); + node = fdt_find_compatible(node, "fsl,qoriq-device-config-2.0", + false); - /* MURAM configuration */ - muram_handle = FM_MURAM_ConfigAndInit(cfg->mem_base_addr + - FMAN_MURAM_OFF, FMAN_MURAM_SIZE); - if (muram_handle == NULL) { - device_printf(cfg->fman_device, "couldn't init FM MURAM module" - "\n"); - return (NULL); + if (node == 0) { + device_printf(sc->sc_base.dev, + "missing device-config node in FDT. Cannot reset FMAN"); + return (0); + } + fdt_regsize(node, &base, &size); + + devdisr2 = ccsr_read4(ccsrbar_va + base + GUTS_DEVDISR2); + if (sc->fm_id == 0) + ccsr_write4(ccsrbar_va + base + GUTS_DEVDISR2, + devdisr2 & ~DEVDISR2_FMAN1); + else + ccsr_write4(ccsrbar_va + base + GUTS_DEVDISR2, + devdisr2 & ~DEVDISR2_FMAN2); +#endif + bus_write_4(sc->mem_res, FM_RSTC, FM_RSTC_FM_RESET); + count = 100; + do { + DELAY(1); + } while ((bus_read_4(sc->mem_res, FM_RSTC) & FM_RSTC_FM_RESET) && + --count); +#ifdef __powerpc__ + ccsr_write4(ccsrbar_va + base + GUTS_DEVDISR2, devdisr2); +#endif + if (count == 0) + return (EBUSY); + return (0); } - sc->muram_handle = muram_handle; +} - /* Fill in FM configuration */ - fm_params.fmId = cfg->fm_id; - /* XXX we support only one partition thus each fman has master id */ - fm_params.guestId = NCSW_MASTER_ID; +static int +fman_clear_iram(struct fman_softc *sc) +{ +#ifdef notyet + int i; - fm_params.baseAddr = cfg->mem_base_addr; - fm_params.h_FmMuram = muram_handle; + /* + * TODO: Allow clearing the IRAM and loading new firmware. Currently + * this is not supported, so assume that there's already firmware + * loaded, and don't clear it just yet. + */ + bus_write_4(sc->mem_res, IRAM_ADDR, IADD_AIE); + for (i = 0; i < 100 && bus_read_4(sc->mem_res, IRAM_ADDR) != IADD_AIE; i++) + DELAY(1); - /* Get FMan clock in Hz */ - if ((tmp = fman_get_clock(sc)) == 0) - return (NULL); + if (i == 100) + return (EBUSY); - /* Convert FMan clock to MHz */ - clock = (uint16_t)(tmp / 1000000); - mod = tmp % 1000000; + for (i = 0; i < sc->iram_size / 4; i++) + bus_write_4(sc->mem_res, IRAM_DATA, 0xffffffff); - if (mod >= 500000) - ++clock; + bus_write_4(sc->mem_res, IRAM_ADDR, sc->iram_size - 4); + for (i = 0; i < 100 && + bus_read_4(sc->mem_res, IRAM_DATA) != 0xffffffff; i++) + DELAY(1); - fm_params.fmClkFreq = clock; - fm_params.f_Exception = cfg->exception_callback; - fm_params.f_BusError = cfg->bus_error_callback; - fm_params.h_App = cfg->fman_device; - fm_params.irq = cfg->irq_num; - fm_params.errIrq = cfg->err_irq_num; + if (i == 100) + return (EBUSY); +#endif - fm_params.firmware.size = fman_firmware_size; - fm_params.firmware.p_Code = (uint32_t*)fman_firmware; + return (0); +} - fm_handle = FM_Config(&fm_params); - if (fm_handle == NULL) { - device_printf(cfg->fman_device, "couldn't configure FM " - "module\n"); - goto err; - } +static int +fman_dma_init(struct fman_softc *sc) +{ + vmem_addr_t addr; + uint32_t reg; + int err; - FM_ConfigResetOnInit(fm_handle, TRUE); + reg = bus_read_4(sc->mem_res, FMDM_SR); + bus_write_4(sc->mem_res, FMDM_SR, reg | SR_BER); + reg = bus_read_4(sc->mem_res, FMDM_MR) & ~MR_CEN_M; + reg |= ((sc->dma_cam_num_entries / DMA_CAM_UNITS) - 1) << MR_CEN_S; + bus_write_4(sc->mem_res, FMDM_MR, reg); - error = FM_Init(fm_handle); - if (error != E_OK) { - device_printf(cfg->fman_device, "couldn't init FM module\n"); - goto err2; - } + err = vmem_xalloc(sc->muram_vmem, + sc->dma_cam_num_entries * DMA_CAM_SIZE, DMA_CAM_ALIGN, 0, 0, + VMEM_ADDR_MIN, VMEM_ADDR_MAX, M_BESTFIT | M_WAITOK, &addr); + if (err != 0) + device_printf(sc->sc_base.dev, + "failed to allocate DMA buffer\n"); + reg = addr; + bus_write_4(sc->mem_res, FMDM_EBCR, reg); + return (0); +} - error = FM_GetRevision(fm_handle, &revision_info); - if (error != E_OK) { - device_printf(cfg->fman_device, "couldn't get FM revision\n"); - goto err2; - } +static int +fman_bmi_init(struct fman_softc *sc) +{ + uint32_t reg; - device_printf(cfg->fman_device, "Hardware version: %d.%d.\n", - revision_info.majorRev, revision_info.minorRev); + reg = sc->bmi_fifo_base / FMAN_BMI_FIFO_ALIGN; + reg |= (sc->total_fifo_size / FMAN_BMI_FIFO_UNITS - 1) << FBPS_S; + bus_write_4(sc->mem_res, FMBM_CFG1, reg); - /* Initialize the simplebus part of things */ - simplebus_init(sc->sc_base.dev, 0); + reg = ((sc->bmi_max_tasks - 1) << TNTSKS_S) & TNTSKS_M; + //bus_write_4(sc->mem_res, FMBM_CFG2, reg); - node = ofw_bus_get_node(sc->sc_base.dev); - fman_fill_ranges(node, &sc->sc_base); - sc->rman.rm_type = RMAN_ARRAY; - sc->rman.rm_descr = "FMan range"; - rman_init_from_resource(&sc->rman, sc->mem_res); - for (node = OF_child(node); node > 0; node = OF_peer(node)) { - simplebus_add_device(sc->sc_base.dev, node, 0, NULL, -1, NULL); - } + bus_write_4(sc->mem_res, FMBM_IEVR, + IEVR_SPEC | IEVR_LEC | IEVR_STEC | IEVR_DEC); - return (fm_handle); + bus_write_4(sc->mem_res, FMBM_IER, + IER_SPECE | IER_LECE | IER_STECE | IER_DECE); -err2: - FM_Free(fm_handle); -err: - FM_MURAM_Free(muram_handle); - return (NULL); + return (0); } -static void -fman_exception_callback(t_Handle app_handle, e_FmExceptions exception) +static int +fman_qmi_init(struct fman_softc *sc) { - struct fman_softc *sc; - - sc = app_handle; - device_printf(sc->sc_base.dev, "FMan exception occurred.\n"); + bus_write_4(sc->mem_res, FMQM_EIE, EIE_DEE | EIE_DFUPE); + bus_write_4(sc->mem_res, FMQM_EIEN, EIEN_DEE | EIEN_DFUPE); + return (0); } static void -fman_error_callback(t_Handle app_handle, e_FmPortType port_type, - uint8_t port_id, uint64_t addr, uint8_t tnum, uint16_t liodn) +fman_hwp_init(struct fman_softc *sc) { - struct fman_softc *sc; - - sc = app_handle; - device_printf(sc->sc_base.dev, "FMan error occurred.\n"); + /* Start up the parser */ + bus_write_4(sc->mem_res, FMPR_RPIMAC, HWP_RPIMAC_PEN); } -/** @} */ +static int +fman_enable(struct fman_softc *sc) +{ + bus_write_4(sc->mem_res, FMBM_INIT, INIT_STR); + bus_write_4(sc->mem_res, FMQM_GC, 0xc0000000 | + GC_STEN | (sc->qmi_def_tnums_thresh << GC_ENQ_THR_S) | + sc->qmi_def_tnums_thresh); -/** - * @group FMan driver interface. - * @{ + return (0); +} + +/* + * Enable timestamp counting. Matching Freescale's reference code, generate the + * timestamp incrementer to be roughly 256MHz, such that bit 23 would update + * every microsecond. */ +static int +fman_enable_timestamp(struct fman_softc *sc) +{ + uint64_t frac; + uint32_t clock = fman_get_clock(sc) / 1000000; + uint32_t intgr, tmp; + uint32_t ts_freq = 1 << FM_TIMESTAMP_1US_BIT; -int -fman_get_handle(device_t dev, t_Handle *fmh) + intgr = ts_freq / clock; + + frac = ((uint64_t)ts_freq << 16) - ((uint64_t)intgr << 16) * clock; + frac = (frac % clock ? 1 : 0) + (frac / clock); + + tmp = (intgr << TSC2_TSIV_INT_S) | (uint32_t)frac; + + bus_write_4(sc->mem_res, FMFP_TSC2, tmp); + bus_write_4(sc->mem_res, FMFP_TSC1, TSC1_TEN); + + return (0); +} + +static int +fman_keygen_init(struct fman_softc *sc) { - struct fman_softc *sc = device_get_softc(dev); + /* TODO: keygen */ + return (0); +} + +static int +fman_fpm_init(struct fman_softc *sc) +{ + /* Clear all events, and enable interrupts. */ + bus_write_4(sc->mem_res, FMFP_EE, + EE_DECC | EE_STL | EE_SECC | EE_EHM | + EE_DECC_EN | EE_STL_EN | EE_SECC_EN); + + bus_write_4(sc->mem_res, FMFP_CEV0, 0xffffffff); + bus_write_4(sc->mem_res, FMFP_CEV1, 0xffffffff); + bus_write_4(sc->mem_res, FMFP_CEV2, 0xffffffff); + bus_write_4(sc->mem_res, FMFP_CEV3, 0xffffffff); - *fmh = sc->fm_handle; + bus_write_4(sc->mem_res, FM_RCR, RCR_FEE | RCR_IEE); return (0); } -int -fman_get_muram_handle(device_t dev, t_Handle *muramh) +static int +fman_init(struct fman_softc *sc) { - struct fman_softc *sc = device_get_softc(dev); + vmem_addr_t base_addr; + sc->sc_revision_major = fman_get_revision_major(sc); + sc->sc_revision_minor = fman_get_revision_minor(sc); + + if (bootverbose) + device_printf(sc->sc_base.dev, "Hardware version: %d.%d.\n", + sc->sc_revision_major, sc->sc_revision_minor); + + fman_fill_soc_params(sc); + bus_set_region_4(sc->mem_res, FMAN_CGP_OFFSET, 0, sc->port_cgs / 4); + + if (fman_reset(sc) != 0) + goto err; + + if (fman_clear_iram(sc) != 0) + goto err; + + if (fman_dma_init(sc) != 0) + goto err; - *muramh = sc->muram_handle; + fman_fpm_init(sc); + + vmem_alloc(sc->muram_vmem, sc->total_fifo_size, M_BESTFIT | M_WAITOK, + &base_addr); + sc->bmi_fifo_base = base_addr; + + fman_bmi_init(sc); + fman_qmi_init(sc); + fman_hwp_init(sc); + if (fman_keygen_init(sc) != 0) + goto err; + + if (fman_enable(sc) != 0) + goto err; + + fman_enable_timestamp(sc); return (0); +err: + return (ENXIO); } -int -fman_get_bushandle(device_t dev, vm_offset_t *fm_base) +void +fman_get_revision(device_t dev, int *major, int *minor) { struct fman_softc *sc = device_get_softc(dev); - *fm_base = rman_get_bushandle(sc->mem_res); + if (major) + *major = sc->sc_revision_major; + if (minor) + *minor = sc->sc_revision_minor; +} + +/** @} */ + +static int +fman_init_muram(struct fman_softc *sc) +{ + u_long base, size; + phandle_t node; + + node = ofw_bus_get_node(sc->sc_base.dev); + for (node = OF_child(node); node != 0; node = OF_peer(node)) { + char compat[255]; + + if (OF_getprop(node, "compatible", compat, sizeof(compat)) < 0) + continue; + if (strcmp(compat, "fsl,fman-muram") == 0) + break; + } + if (node == 0) { + device_printf(sc->sc_base.dev, "no muram node\n"); + return (ENXIO); + } + if (fdt_regsize(node, &base, &size) != 0) { + device_printf(sc->sc_base.dev, "failed to get muram reg\n"); + return (ENXIO); + } + sc->muram_vmem = vmem_create("MURAM", + base, size, 1, 0, M_WAITOK); return (0); } @@ -413,20 +682,18 @@ int fman_attach(device_t dev) { struct fman_softc *sc; - struct fman_config cfg; pcell_t qchan_range[2]; + pcell_t cell; phandle_t node; sc = device_get_softc(dev); sc->sc_base.dev = dev; - /* Check if MallocSmart allocator is ready */ - if (XX_MallocSmartInit() != E_OK) { - device_printf(dev, "could not initialize smart allocator.\n"); - return (ENXIO); - } - + cell = 0; node = ofw_bus_get_node(dev); + OF_getencprop(node, "cell-index", &cell, sizeof(cell)); + sc->fm_id = cell; + if (OF_getencprop(node, "fsl,qman-channel-range", qchan_range, sizeof(qchan_range)) <= 0) { device_printf(dev, "Missing QMan channel range property!\n"); @@ -450,6 +717,12 @@ fman_attach(device_t dev) goto err; } + if (bus_setup_intr(dev, sc->irq_res, INTR_TYPE_NET | INTR_MPSAFE, + NULL, fman_intr, sc, &sc->irq_cookie) != 0) { + device_printf(dev, "error setting up interrupt handler.\n"); + goto err; + } + sc->err_irq_rid = 1; sc->err_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->err_irq_rid, RF_ACTIVE | RF_SHAREABLE); @@ -458,20 +731,19 @@ fman_attach(device_t dev) goto err; } - /* Set FMan configuration */ - cfg.fman_device = dev; - cfg.fm_id = device_get_unit(dev); - cfg.mem_base_addr = rman_get_bushandle(sc->mem_res); - cfg.irq_num = (uintptr_t)sc->irq_res; - cfg.err_irq_num = (uintptr_t)sc->err_irq_res; - cfg.exception_callback = fman_exception_callback; - cfg.bus_error_callback = fman_error_callback; + /* Initialize the simplebus part of things */ + sc->rman.rm_type = RMAN_ARRAY; + sc->rman.rm_descr = "FMan range"; + rman_init_from_resource(&sc->rman, sc->mem_res); + simplebus_attach_impl(sc->sc_base.dev); + + if (fman_init_muram(sc) != 0) + goto err; + + /* TODO: Interrupts... */ - sc->fm_handle = fman_init(sc, &cfg); - if (sc->fm_handle == NULL) { - device_printf(dev, "could not be configured\n"); + if (fman_init(sc) != 0) goto err; - } bus_attach_children(dev); return (0); @@ -485,16 +757,14 @@ int fman_detach(device_t dev) { struct fman_softc *sc; + int rv; - sc = device_get_softc(dev); + rv = simplebus_detach(dev); - if (sc->muram_handle) { - FM_MURAM_Free(sc->muram_handle); - } + if (rv != 0) + return (rv); - if (sc->fm_handle) { - FM_Free(sc->fm_handle); - } + sc = device_get_softc(dev); if (sc->mem_res) { bus_release_resource(dev, SYS_RES_MEMORY, sc->mem_rid, @@ -511,6 +781,9 @@ fman_detach(device_t dev) sc->err_irq_res); } + if (sc->muram_vmem != NULL) + vmem_destroy(sc->muram_vmem); + return (0); } @@ -535,21 +808,152 @@ fman_shutdown(device_t dev) return (0); } +static void +fman_intr(void *arg) +{ + /* TODO: All FMAN interrupts */ +} + int fman_qman_channel_id(device_t dev, int port) { struct fman_softc *sc; - int qman_port_id[] = {0x31, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, - 0x2f, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}; int i; sc = device_get_softc(dev); - for (i = 0; i < sc->qman_chan_count; i++) { - if (qman_port_id[i] == port) - return (sc->qman_chan_base + i); + if (sc->sc_revision_major >= 6) { + static const int qman_port_id[] = { + 0x30, 0x31, 0x28, 0x29, 0x2a, 0x2b, + 0x2c, 0x2d, 0x02, 0x03, 0x04, 0x05, 0x07, 0x07 + }; + for (i = 0; i < sc->qman_chan_count; i++) { + if (qman_port_id[i] == port) + return (sc->qman_chan_base + i); + } + } else { + static const int qman_port_id[] = { + 0x31, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x01, + 0x02, 0x03, 0x04, 0x05, 0x07, 0x07 + }; + for (i = 0; i < sc->qman_chan_count; i++) { + if (qman_port_id[i] == port) + return (sc->qman_chan_base + i); + } } return (0); } +size_t +fman_get_bmi_max_fifo_size(device_t dev) +{ + struct fman_softc *sc = device_get_softc(dev); + + return (sc->bmi_max_fifo_size); +} + +int +fman_reset_mac(device_t dev, int mac_id) +{ + struct fman_softc *sc = device_get_softc(dev); + int timeout = 100; + uint32_t mask; + + if (mac_id < 0 || mac_id > 9) + return (EINVAL); + + /* MAC bits start at bit 1 for MAC0, and go down */ + mask = (1 << (30 - mac_id)); + bus_write_4(sc->mem_res, FM_RSTC, mask); + while ((bus_read_4(sc->mem_res, FM_RSTC) & mask) && --timeout) + DELAY(10); + + if (timeout == 0) + return (EIO); + + return (0); +} + +static int +fman_set_port_tasks(struct fman_softc *sc, int port_id, + uint8_t tasks, uint8_t extra) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, FMBM_PP(port_id)); + + reg &= ~(PP_MXT_M | PP_EXT_M); + reg |= ((uint32_t)(tasks - 1) << PP_MXT_S) | + ((uint32_t)extra << PP_EXT_S); + bus_write_4(sc->mem_res, FMBM_PP(port_id), reg); + + return (0); +} + +static int +fman_set_port_fifo_size(struct fman_softc *sc, int port_id, + uint32_t fifo_size, uint32_t extra) +{ + uint32_t reg; + + reg = (fifo_size / FMAN_BMI_FIFO_UNITS - 1) | + ((extra / FMAN_BMI_FIFO_UNITS) << PFS_EXBS_S); + + /* TODO: Make sure fifo size doesn't overrun */ + /* See Linux driver, fman set_size_of_fifo */ + + bus_write_4(sc->mem_res, FMBM_PFS(port_id), reg); + return (0); +} + +static int +fman_set_port_dmas(struct fman_softc *sc, int port_id, + int open_dmas, int extra_dmas) +{ + /* TODO: set port DMAs */ + return (0); +} + +static void +fman_set_port_liodn(struct fman_softc *sc, int port_id, uint32_t liodn) +{ + uint32_t reg; + + reg = bus_read_4(sc->mem_res, FMDM_PLRn(port_id)); + reg &= ~PLRN_LIODN_M(port_id); + reg |= liodn << PLRN_LIODN_S(port_id); + bus_write_4(sc->mem_res, FMDM_PLRn(port_id), reg); +} + +int +fman_set_port_params(device_t dev, struct fman_port_init_params *params) +{ + struct fman_softc *sc = device_get_softc(dev); + int error; + + error = fman_set_port_tasks(sc, params->port_id, + params->num_tasks, params->extra_tasks); + + if (error != 0) + return (error); + + if (!params->is_rx_port) { + } + error = fman_set_port_fifo_size(sc, params->port_id, params->fifo_size, + params->extra_fifo_size); + + if (error != 0) + return (error); + + error = fman_set_port_dmas(sc, params->port_id, + params->open_dmas, params->extra_dmas); + + if (error != 0) + return (error); + + fman_set_port_liodn(sc, params->port_id, params->liodn); + + return (0); +} + /** @} */ |
