root/sys/dev/dpaa/fman_port.c
/*
 * Copyright (c) 2026 Justin Hibbits
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#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_parser.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   /* Counts are units of 16 bytes */
#define   RICP_ICEOF_M            0x001f0000
#define   RICP_ICEOF_S            16
#define   RICP_ICIOF_M            0x00000f00
#define   RICP_ICIOF_S            8
#define   RICP_ICSZ_S             0x0000001f
#define FMBM_RIM                0x018
#define FMBM_REBM               0x01c
#define   REBM_BSM_M              0x01ff0000
#define   REBM_BSM_S              16
#define   REBM_BEM_M              0x000001ff
#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_TICP       0x014
#define   TICP_ICEOF_M            0x001f0000
#define   TICP_ICEOF_S            16
#define   TICP_ICIOF_M            0x00000f00
#define   TICP_ICIOF_S            8
#define   TICP_ICSZ_S             0x0000001f
#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)
{
        uint32_t reg;

        /* 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_BMI | NIA_BMI_AC_ENQ_FRAME);
        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);

        /* Insert internal context ahead of the frame */
        reg = sizeof(struct fman_internal_context) << REBM_BSM_S;
        bus_write_4(sc->sc_mem, FMBM_REBM, reg);
        reg = howmany(FMAN_PARSE_RESULT_OFF, 0x10) << RICP_ICIOF_S;
        reg |= howmany(sizeof(struct fman_internal_context), 0x10);
        bus_write_4(sc->sc_mem, FMBM_RICP, reg);

        bus_write_4(sc->sc_mem, FMBM_RFNE, NIA_ENG_HWP);
        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);

        /* Insert internal context ahead of the frame */
        reg = howmany(FMAN_PARSE_RESULT_OFF, 0x10) << TICP_ICIOF_S;
        reg |= howmany(sizeof(struct fman_internal_context), 0x10);
        bus_write_4(sc->sc_mem, FMBM_TICP, reg);

        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;

        /* Stop the parser so we can initialize it for our uses */
        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);

        /* Re-enable the parser */
        bus_write_4(sc->sc_mem, HWP_PCAC, 0);

        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);