root/sys/dev/neta/if_mvnetavar.h
/*
 * Copyright (c) 2017 Stormshield.
 * Copyright (c) 2017 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 ``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 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_MVNETAVAR_H_
#define _IF_MVNETAVAR_H_
#include <net/if.h>

#define MVNETA_HWHEADER_SIZE            2       /* Marvell Header */
#define MVNETA_ETHER_SIZE               22      /* Maximum ether size */
#define MVNETA_A370_MAX_CSUM_MTU        1600    /* Max frame len for TX csum */
#define MVNETA_A3700_MAX_CSUM_MTU       9600

#define MVNETA_MAX_FRAME                (MJUM9BYTES)

/*
 * Default limit of queue length
 *
 * queue 0 is lowest priority and queue 7 is highest priority.
 * IP packet is received on queue 7 by default.
 */
#define MVNETA_TX_RING_CNT      512
#define MVNETA_RX_RING_CNT      256

#define MVNETA_BUFRING_SIZE     1024

#define MVNETA_PACKET_OFFSET    64

#define MVNETA_RXTH_COUNT       128
#define MVNETA_RX_REFILL_COUNT  8
#define MVNETA_TX_RECLAIM_COUNT 32

/*
 * Device Register access
 */
#define MVNETA_READ(sc, reg) \
        bus_read_4((sc)->res[0], (reg))
#define MVNETA_WRITE(sc, reg, val) \
        bus_write_4((sc)->res[0], (reg), (val))

#define MVNETA_READ_REGION(sc, reg, val, c) \
        bus_read_region_4((sc)->res[0], (reg), (val), (c))
#define MVNETA_WRITE_REGION(sc, reg, val, c) \
        bus_write_region_4((sc)->res[0], (reg), (val), (c))

#define MVNETA_READ_MIB(sc, reg) \
        bus_read_4((sc)->res[0], MVNETA_PORTMIB_BASE + (reg))

#define MVNETA_IS_LINKUP(sc) \
        (MVNETA_READ((sc), MVNETA_PSR) & MVNETA_PSR_LINKUP)

#define MVNETA_IS_QUEUE_SET(queues, q) \
        ((((queues) >> (q)) & 0x1))

/*
 * EEE: Lower Power Idle config
 * Default timer is duration of MTU sized frame transmission.
 * The timer can be negotiated by LLDP protocol, but we have no
 * support.
 */
#define MVNETA_LPI_TS           (ETHERMTU * 8 / 1000) /* [us] */
#define MVNETA_LPI_TW           (ETHERMTU * 8 / 1000) /* [us] */
#define MVNETA_LPI_LI           (ETHERMTU * 8 / 1000) /* [us] */

/*
 * DMA Descriptor
 *
 * the ethernet device has 8 rx/tx DMA queues. each of queue has its own
 * decriptor list. descriptors are simply index by counter inside the device.
 */
#define MVNETA_TX_SEGLIMIT      32

#define MVNETA_QUEUE_IDLE       1
#define MVNETA_QUEUE_WORKING    2
#define MVNETA_QUEUE_DISABLED   3

struct mvneta_buf {
        struct mbuf *   m;      /* pointer to related mbuf */
        bus_dmamap_t    dmap;
};

struct mvneta_rx_ring {
        int                             queue_status;
        /* Real descriptors array. shared by RxDMA */
        struct mvneta_rx_desc           *desc;
        bus_dmamap_t                    desc_map;
        bus_addr_t                      desc_pa;

        /* Virtual address of the RX buffer */
        void                            *rxbuf_virt_addr[MVNETA_RX_RING_CNT];

        /* Managment entries for each of descritors */
        struct mvneta_buf               rxbuf[MVNETA_RX_RING_CNT];

        /* locks */
        struct mtx                      ring_mtx;

        /* Index */
        int                             dma;
        int                             cpu;

        /* Limit */
        int                             queue_th_received;
        int                             queue_th_time; /* [Tclk] */

        /* LRO */
        struct lro_ctrl                 lro;
        boolean_t                       lro_enabled;
        /* Is this queue out of mbuf */
        boolean_t                       needs_refill;
} __aligned(CACHE_LINE_SIZE);

struct mvneta_tx_ring {
        /* Index of this queue */
        int                             qidx;
        /* IFNET pointer */
        if_t                            ifp;
        /* Ring buffer for IFNET */
        struct buf_ring                 *br;
        /* Real descriptors array. shared by TxDMA */
        struct mvneta_tx_desc           *desc;
        bus_dmamap_t                    desc_map;
        bus_addr_t                      desc_pa;

        /* Managment entries for each of descritors */
        struct mvneta_buf               txbuf[MVNETA_TX_RING_CNT];

        /* locks */
        struct mtx                      ring_mtx;

        /* Index */
        int                             used;
        int                             dma;
        int                             cpu;

        /* watchdog */
#define MVNETA_WATCHDOG_TXCOMP  (hz / 10) /* 100ms */
#define MVNETA_WATCHDOG (10 * hz) /* 10s */
        int                             watchdog_time;
        int                             queue_status;
        boolean_t                       queue_hung;

        /* Task */
        struct task                     task;
        struct taskqueue                *taskq;

        /* Stats */
        uint32_t                        drv_error;
} __aligned(CACHE_LINE_SIZE);

static __inline int
tx_counter_adv(int ctr, int n)
{

        ctr += n;
        while (__predict_false(ctr >= MVNETA_TX_RING_CNT))
                ctr -= MVNETA_TX_RING_CNT;

        return (ctr);
}

static __inline int
rx_counter_adv(int ctr, int n)
{

        ctr += n;
        while (__predict_false(ctr >= MVNETA_RX_RING_CNT))
                ctr -= MVNETA_RX_RING_CNT;

        return (ctr);
}

/*
 * Timeout control
 */
#define MVNETA_PHY_TIMEOUT      10000   /* msec */
#define RX_DISABLE_TIMEOUT      0x1000000 /* times */
#define TX_DISABLE_TIMEOUT      0x1000000 /* times */
#define TX_FIFO_EMPTY_TIMEOUT   0x1000000 /* times */

/*
 * Debug
 */
#define KASSERT_SC_MTX(sc) \
    KASSERT(mtx_owned(&(sc)->mtx), ("SC mutex not owned"))
#define KASSERT_BM_MTX(sc) \
    KASSERT(mtx_owned(&(sc)->bm.bm_mtx), ("BM mutex not owned"))
#define KASSERT_RX_MTX(sc, q) \
    KASSERT(mtx_owned(&(sc)->rx_ring[(q)].ring_mtx),\
        ("RX mutex not owned"))
#define KASSERT_TX_MTX(sc, q) \
    KASSERT(mtx_owned(&(sc)->tx_ring[(q)].ring_mtx),\
        ("TX mutex not owned"))

/*
 * sysctl(9) parameters
 */
struct mvneta_sysctl_queue {
        struct mvneta_softc     *sc;
        int                     rxtx;
        int                     queue;
};
#define MVNETA_SYSCTL_RX                0
#define MVNETA_SYSCTL_TX                1

struct mvneta_sysctl_mib {
        struct mvneta_softc     *sc;
        int                     index;
        uint64_t                counter;
};

enum mvneta_phy_mode {
        MVNETA_PHY_QSGMII,
        MVNETA_PHY_SGMII,
        MVNETA_PHY_RGMII,
        MVNETA_PHY_RGMII_ID
};

/*
 * Ethernet Device main context
 */
DECLARE_CLASS(mvneta_driver);

struct mvneta_softc {
        device_t        dev;
        uint32_t        version;
        /*
         * mtx must be held by interface functions to/from
         * other frameworks. interrupt handler, sysctl handler,
         * ioctl handler, and so on.
         */
        struct mtx      mtx;
        struct resource *res[2];
        void            *ih_cookie[1];
        
        uint64_t        clk_freq;

        if_t            ifp;
        uint32_t        mvneta_if_flags;
        uint32_t        mvneta_media;
        uint32_t        tx_csum_limit;
        uint32_t        rx_frame_size;

        int                     phy_attached;
        enum mvneta_phy_mode    phy_mode;
        int                     phy_addr;
        int                     phy_speed;      /* PHY speed */
        boolean_t               phy_fdx;        /* Full duplex mode */
        boolean_t               autoneg;        /* Autonegotiation status */
        boolean_t               use_inband_status;      /* In-band link status */

        /*
         * Link State control
         */
        boolean_t       linkup;
        device_t        miibus;
        struct mii_data *mii;
        uint8_t         enaddr[ETHER_ADDR_LEN];
        struct ifmedia  mvneta_ifmedia;

        bus_dma_tag_t   rx_dtag;
        bus_dma_tag_t   rxbuf_dtag;
        bus_dma_tag_t   tx_dtag;
        bus_dma_tag_t   txmbuf_dtag;
        struct mvneta_rx_ring           rx_ring[MVNETA_RX_QNUM_MAX];
        struct mvneta_tx_ring           tx_ring[MVNETA_TX_QNUM_MAX];

        /*
         * Maintance clock
         */
        struct callout          tick_ch;

        int cf_lpi;
        int cf_fc;
        int debug;

        /*
         * Sysctl interfaces
         */
        struct mvneta_sysctl_queue sysctl_rx_queue[MVNETA_RX_QNUM_MAX];
        struct mvneta_sysctl_queue sysctl_tx_queue[MVNETA_TX_QNUM_MAX];

        /*
         * MIB counter
         */
        struct mvneta_sysctl_mib sysctl_mib[MVNETA_PORTMIB_NOCOUNTER];
        uint64_t counter_pdfc;
        uint64_t counter_pofc;
        uint32_t counter_watchdog;              /* manual reset when clearing mib */
        uint32_t counter_watchdog_mib;  /* reset after each mib update */
};
#define MVNETA_RX_RING(sc, q) \
    (&(sc)->rx_ring[(q)])
#define MVNETA_TX_RING(sc, q) \
    (&(sc)->tx_ring[(q)])

int mvneta_attach(device_t);

#ifdef FDT
boolean_t mvneta_has_switch_fdt(device_t);
int mvneta_fdt_mac_address(struct mvneta_softc *, uint8_t *);
#endif

#endif /* _IF_MVNETAVAR_H_ */