Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

/*
 * 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.
 *
 * $FreeBSD$
 *
 */

#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_MAX_CSUM_MTU	1600	/* Port1,2 hw limit */

/*
 * Limit support for frame up to hw csum limit
 * until jumbo frame support is added.
 */
#define	MVNETA_MAX_FRAME		(MVNETA_MAX_CSUM_MTU + MVNETA_ETHER_SIZE)

/*
 * 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_PACKET_SIZE	MCLBYTES

#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_4(sc, reg) \
	bus_read_4((sc)->res[0], MVNETA_PORTMIB_BASE + (reg))
#define	MVNETA_READ_MIB_8(sc, reg) \
	bus_read_8((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 */
	struct ifnet			*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 hander, sysctl hander,
	 * ioctl hander, and so on.
	 */
	struct mtx	mtx;
	struct resource *res[2];
	void            *ih_cookie[1];

	struct ifnet	*ifp;
	uint32_t        mvneta_if_flags;
	uint32_t        mvneta_media;

	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
int mvneta_fdt_mac_address(struct mvneta_softc *, uint8_t *);
#endif

#endif /* _IF_MVNETAVAR_H_ */