xref: /freebsd-src/sys/dev/al_eth/al_eth.c (revision 0b45d36510d8c629fcc49805bc64e5893f4ba63c)
17902c8dcSWojciech Macek /*-
27902c8dcSWojciech Macek  * Copyright (c) 2015,2016 Annapurna Labs Ltd. and affiliates
37902c8dcSWojciech Macek  * All rights reserved.
47902c8dcSWojciech Macek  *
57902c8dcSWojciech Macek  * Developed by Semihalf.
67902c8dcSWojciech Macek  *
77902c8dcSWojciech Macek  * Redistribution and use in source and binary forms, with or without
87902c8dcSWojciech Macek  * modification, are permitted provided that the following conditions
97902c8dcSWojciech Macek  * are met:
107902c8dcSWojciech Macek  * 1. Redistributions of source code must retain the above copyright
117902c8dcSWojciech Macek  *    notice, this list of conditions and the following disclaimer.
127902c8dcSWojciech Macek  * 2. Redistributions in binary form must reproduce the above copyright
137902c8dcSWojciech Macek  *    notice, this list of conditions and the following disclaimer in the
147902c8dcSWojciech Macek  *    documentation and/or other materials provided with the distribution.
157902c8dcSWojciech Macek  *
167902c8dcSWojciech Macek  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177902c8dcSWojciech Macek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187902c8dcSWojciech Macek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197902c8dcSWojciech Macek  * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207902c8dcSWojciech Macek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217902c8dcSWojciech Macek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227902c8dcSWojciech Macek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237902c8dcSWojciech Macek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247902c8dcSWojciech Macek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257902c8dcSWojciech Macek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267902c8dcSWojciech Macek  * SUCH DAMAGE.
277902c8dcSWojciech Macek  */
287902c8dcSWojciech Macek 
297902c8dcSWojciech Macek #include <sys/param.h>
307902c8dcSWojciech Macek #include <sys/systm.h>
317902c8dcSWojciech Macek #include <sys/bus.h>
327902c8dcSWojciech Macek #include <sys/kernel.h>
337902c8dcSWojciech Macek #include <sys/kthread.h>
347902c8dcSWojciech Macek #include <sys/lock.h>
357902c8dcSWojciech Macek #include <sys/mbuf.h>
367902c8dcSWojciech Macek #include <sys/malloc.h>
377902c8dcSWojciech Macek #include <sys/module.h>
387902c8dcSWojciech Macek #include <sys/rman.h>
397902c8dcSWojciech Macek #include <sys/socket.h>
407902c8dcSWojciech Macek #include <sys/sockio.h>
417902c8dcSWojciech Macek #include <sys/sysctl.h>
427902c8dcSWojciech Macek #include <sys/taskqueue.h>
437902c8dcSWojciech Macek 
447902c8dcSWojciech Macek #include <machine/atomic.h>
457902c8dcSWojciech Macek 
467902c8dcSWojciech Macek #include "opt_inet.h"
477902c8dcSWojciech Macek #include "opt_inet6.h"
487902c8dcSWojciech Macek 
497902c8dcSWojciech Macek #include <net/ethernet.h>
507902c8dcSWojciech Macek #include <net/if.h>
517902c8dcSWojciech Macek #include <net/if_var.h>
527902c8dcSWojciech Macek #include <net/if_arp.h>
537902c8dcSWojciech Macek #include <net/if_dl.h>
547902c8dcSWojciech Macek #include <net/if_media.h>
557902c8dcSWojciech Macek #include <net/if_types.h>
567902c8dcSWojciech Macek #include <netinet/in.h>
577902c8dcSWojciech Macek #include <net/if_vlan_var.h>
587902c8dcSWojciech Macek #include <netinet/tcp.h>
597902c8dcSWojciech Macek #include <netinet/tcp_lro.h>
607902c8dcSWojciech Macek 
617902c8dcSWojciech Macek #ifdef INET
627902c8dcSWojciech Macek #include <netinet/in.h>
637902c8dcSWojciech Macek #include <netinet/in_systm.h>
647902c8dcSWojciech Macek #include <netinet/in_var.h>
657902c8dcSWojciech Macek #include <netinet/ip.h>
667902c8dcSWojciech Macek #endif
677902c8dcSWojciech Macek 
687902c8dcSWojciech Macek #ifdef INET6
697902c8dcSWojciech Macek #include <netinet/ip6.h>
707902c8dcSWojciech Macek #endif
717902c8dcSWojciech Macek 
727902c8dcSWojciech Macek #include <sys/sockio.h>
737902c8dcSWojciech Macek 
747902c8dcSWojciech Macek #include <dev/pci/pcireg.h>
757902c8dcSWojciech Macek #include <dev/pci/pcivar.h>
767902c8dcSWojciech Macek 
777902c8dcSWojciech Macek #include <dev/mii/mii.h>
787902c8dcSWojciech Macek #include <dev/mii/miivar.h>
797902c8dcSWojciech Macek 
807902c8dcSWojciech Macek #include <al_hal_common.h>
817902c8dcSWojciech Macek #include <al_hal_plat_services.h>
827902c8dcSWojciech Macek #include <al_hal_udma_config.h>
837902c8dcSWojciech Macek #include <al_hal_udma_iofic.h>
847902c8dcSWojciech Macek #include <al_hal_udma_debug.h>
857902c8dcSWojciech Macek #include <al_hal_eth.h>
867902c8dcSWojciech Macek 
877902c8dcSWojciech Macek #include "al_eth.h"
887902c8dcSWojciech Macek #include "al_init_eth_lm.h"
897902c8dcSWojciech Macek #include "arm/annapurna/alpine/alpine_serdes.h"
907902c8dcSWojciech Macek 
917902c8dcSWojciech Macek #include "miibus_if.h"
927902c8dcSWojciech Macek 
937902c8dcSWojciech Macek #define	device_printf_dbg(fmt, ...) do {				\
947902c8dcSWojciech Macek 	if (AL_DBG_LEVEL >= AL_DBG_LEVEL_DBG) { AL_DBG_LOCK();		\
957902c8dcSWojciech Macek 	    device_printf(fmt, __VA_ARGS__); AL_DBG_UNLOCK();}		\
967902c8dcSWojciech Macek 	} while (0)
977902c8dcSWojciech Macek 
987902c8dcSWojciech Macek MALLOC_DEFINE(M_IFAL, "if_al_malloc", "All allocated data for AL ETH driver");
997902c8dcSWojciech Macek 
1007902c8dcSWojciech Macek /* move out to some pci header file */
1017902c8dcSWojciech Macek #define	PCI_VENDOR_ID_ANNAPURNA_LABS	0x1c36
1027902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_ETH		0x0001
1037902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_ETH_ADVANCED	0x0002
1047902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_ETH_NIC	0x0003
1057902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_ETH_FPGA_NIC	0x0030
1067902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_CRYPTO		0x0011
1077902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_CRYPTO_VF	0x8011
1087902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_RAID_DMA	0x0021
1097902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_RAID_DMA_VF	0x8021
1107902c8dcSWojciech Macek #define	PCI_DEVICE_ID_AL_USB		0x0041
1117902c8dcSWojciech Macek 
1127902c8dcSWojciech Macek #define	MAC_ADDR_STR "%02x:%02x:%02x:%02x:%02x:%02x"
1137902c8dcSWojciech Macek #define	MAC_ADDR(addr) addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]
1147902c8dcSWojciech Macek 
1157902c8dcSWojciech Macek #define	AL_ETH_MAC_TABLE_UNICAST_IDX_BASE	0
1167902c8dcSWojciech Macek #define	AL_ETH_MAC_TABLE_UNICAST_MAX_COUNT	4
1177902c8dcSWojciech Macek #define	AL_ETH_MAC_TABLE_ALL_MULTICAST_IDX	(AL_ETH_MAC_TABLE_UNICAST_IDX_BASE + \
1187902c8dcSWojciech Macek 						 AL_ETH_MAC_TABLE_UNICAST_MAX_COUNT)
1197902c8dcSWojciech Macek 
1207902c8dcSWojciech Macek #define	AL_ETH_MAC_TABLE_DROP_IDX		(AL_ETH_FWD_MAC_NUM - 1)
1217902c8dcSWojciech Macek #define	AL_ETH_MAC_TABLE_BROADCAST_IDX		(AL_ETH_MAC_TABLE_DROP_IDX - 1)
1227902c8dcSWojciech Macek 
1237902c8dcSWojciech Macek #define	AL_ETH_THASH_UDMA_SHIFT		0
1247902c8dcSWojciech Macek #define	AL_ETH_THASH_UDMA_MASK		(0xF << AL_ETH_THASH_UDMA_SHIFT)
1257902c8dcSWojciech Macek 
1267902c8dcSWojciech Macek #define	AL_ETH_THASH_Q_SHIFT		4
1277902c8dcSWojciech Macek #define	AL_ETH_THASH_Q_MASK		(0x3 << AL_ETH_THASH_Q_SHIFT)
1287902c8dcSWojciech Macek 
1297902c8dcSWojciech Macek /* the following defines should be moved to hal */
1307902c8dcSWojciech Macek #define	AL_ETH_FSM_ENTRY_IPV4_TCP		0
1317902c8dcSWojciech Macek #define	AL_ETH_FSM_ENTRY_IPV4_UDP		1
1327902c8dcSWojciech Macek #define	AL_ETH_FSM_ENTRY_IPV6_TCP		2
1337902c8dcSWojciech Macek #define	AL_ETH_FSM_ENTRY_IPV6_UDP		3
1347902c8dcSWojciech Macek #define	AL_ETH_FSM_ENTRY_IPV6_NO_UDP_TCP	4
1357902c8dcSWojciech Macek #define	AL_ETH_FSM_ENTRY_IPV4_NO_UDP_TCP	5
1367902c8dcSWojciech Macek 
1377902c8dcSWojciech Macek /* FSM DATA format */
1387902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_OUTER_2_TUPLE	0
1397902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_OUTER_4_TUPLE	1
1407902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_INNER_2_TUPLE	2
1417902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_INNER_4_TUPLE	3
1427902c8dcSWojciech Macek 
1437902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_HASH_SEL	(1 << 2)
1447902c8dcSWojciech Macek 
1457902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_DEFAULT_Q	0
1467902c8dcSWojciech Macek #define	AL_ETH_FSM_DATA_DEFAULT_UDMA	0
1477902c8dcSWojciech Macek 
1487902c8dcSWojciech Macek #define	AL_BR_SIZE	512
1497902c8dcSWojciech Macek #define	AL_TSO_SIZE	65500
1507902c8dcSWojciech Macek #define	AL_DEFAULT_MTU	1500
1517902c8dcSWojciech Macek 
1527902c8dcSWojciech Macek #define	CSUM_OFFLOAD		(CSUM_IP|CSUM_TCP|CSUM_UDP|CSUM_SCTP)
1537902c8dcSWojciech Macek 
1547902c8dcSWojciech Macek #define	AL_IP_ALIGNMENT_OFFSET	2
1557902c8dcSWojciech Macek 
1567902c8dcSWojciech Macek #define	SFP_I2C_ADDR		0x50
1577902c8dcSWojciech Macek 
1587902c8dcSWojciech Macek #define	AL_MASK_GROUP_A_INT	0x7
1597902c8dcSWojciech Macek #define	AL_MASK_GROUP_B_INT	0xF
1607902c8dcSWojciech Macek #define	AL_MASK_GROUP_C_INT	0xF
1617902c8dcSWojciech Macek #define	AL_MASK_GROUP_D_INT	0xFFFFFFFF
1627902c8dcSWojciech Macek 
1637902c8dcSWojciech Macek #define	AL_REG_OFFSET_FORWARD_INTR	(0x1800000 + 0x1210)
1647902c8dcSWojciech Macek #define	AL_EN_FORWARD_INTR	0x1FFFF
1657902c8dcSWojciech Macek #define	AL_DIS_FORWARD_INTR	0
1667902c8dcSWojciech Macek 
1677902c8dcSWojciech Macek #define	AL_M2S_MASK_INIT	0x480
1687902c8dcSWojciech Macek #define	AL_S2M_MASK_INIT	0x1E0
1697902c8dcSWojciech Macek #define	AL_M2S_S2M_MASK_NOT_INT	(0x3f << 25)
1707902c8dcSWojciech Macek 
1717902c8dcSWojciech Macek #define	AL_10BASE_T_SPEED	10
1727902c8dcSWojciech Macek #define	AL_100BASE_TX_SPEED	100
1737902c8dcSWojciech Macek #define	AL_1000BASE_T_SPEED	1000
1747902c8dcSWojciech Macek 
1757902c8dcSWojciech Macek #define	AL_RX_LOCK_INIT(_sc)	mtx_init(&((_sc)->if_rx_lock), "ALRXL", "ALRXL", MTX_DEF)
1767902c8dcSWojciech Macek #define	AL_RX_LOCK(_sc)		mtx_lock(&((_sc)->if_rx_lock))
1777902c8dcSWojciech Macek #define	AL_RX_UNLOCK(_sc)	mtx_unlock(&((_sc)->if_rx_lock))
1787902c8dcSWojciech Macek 
1797902c8dcSWojciech Macek /* helper functions */
1807902c8dcSWojciech Macek static int al_is_device_supported(device_t);
1817902c8dcSWojciech Macek 
1827902c8dcSWojciech Macek static void al_eth_init_rings(struct al_eth_adapter *);
1837902c8dcSWojciech Macek static void al_eth_flow_ctrl_disable(struct al_eth_adapter *);
1847902c8dcSWojciech Macek int al_eth_fpga_read_pci_config(void *, int, uint32_t *);
1857902c8dcSWojciech Macek int al_eth_fpga_write_pci_config(void *, int, uint32_t);
1867902c8dcSWojciech Macek int al_eth_read_pci_config(void *, int, uint32_t *);
1877902c8dcSWojciech Macek int al_eth_write_pci_config(void *, int, uint32_t);
1887902c8dcSWojciech Macek void al_eth_irq_config(uint32_t *, uint32_t);
1897902c8dcSWojciech Macek void al_eth_forward_int_config(uint32_t *, uint32_t);
1907902c8dcSWojciech Macek static void al_eth_start_xmit(void *, int);
1917902c8dcSWojciech Macek static void al_eth_rx_recv_work(void *, int);
1927902c8dcSWojciech Macek static int al_eth_up(struct al_eth_adapter *);
1937902c8dcSWojciech Macek static void al_eth_down(struct al_eth_adapter *);
1947902c8dcSWojciech Macek static void al_eth_interrupts_unmask(struct al_eth_adapter *);
1957902c8dcSWojciech Macek static void al_eth_interrupts_mask(struct al_eth_adapter *);
1967902c8dcSWojciech Macek static int al_eth_check_mtu(struct al_eth_adapter *, int);
197bc14c73bSJustin Hibbits static uint64_t al_get_counter(if_t, ift_counter);
1987902c8dcSWojciech Macek static void al_eth_req_rx_buff_size(struct al_eth_adapter *, int);
1997902c8dcSWojciech Macek static int al_eth_board_params_init(struct al_eth_adapter *);
200bc14c73bSJustin Hibbits static int al_media_update(if_t);
201bc14c73bSJustin Hibbits static void al_media_status(if_t, struct ifmediareq *);
2027902c8dcSWojciech Macek static int al_eth_function_reset(struct al_eth_adapter *);
2037902c8dcSWojciech Macek static int al_eth_hw_init_adapter(struct al_eth_adapter *);
2047902c8dcSWojciech Macek static void al_eth_serdes_init(struct al_eth_adapter *);
2057902c8dcSWojciech Macek static void al_eth_lm_config(struct al_eth_adapter *);
2067902c8dcSWojciech Macek static int al_eth_hw_init(struct al_eth_adapter *);
2077902c8dcSWojciech Macek 
2087902c8dcSWojciech Macek static void al_tick_stats(void *);
2097902c8dcSWojciech Macek 
2107902c8dcSWojciech Macek /* ifnet entry points */
2117902c8dcSWojciech Macek static void al_init(void *);
212bc14c73bSJustin Hibbits static int al_mq_start(if_t, struct mbuf *);
213bc14c73bSJustin Hibbits static void al_qflush(if_t);
214bc14c73bSJustin Hibbits static int al_ioctl(if_t ifp, u_long, caddr_t);
2157902c8dcSWojciech Macek 
2167902c8dcSWojciech Macek /* bus entry points */
2177902c8dcSWojciech Macek static int al_probe(device_t);
2187902c8dcSWojciech Macek static int al_attach(device_t);
2197902c8dcSWojciech Macek static int al_detach(device_t);
2207902c8dcSWojciech Macek static int al_shutdown(device_t);
2217902c8dcSWojciech Macek 
2227902c8dcSWojciech Macek /* mii bus support routines */
2237902c8dcSWojciech Macek static int al_miibus_readreg(device_t, int, int);
2247902c8dcSWojciech Macek static int al_miibus_writereg(device_t, int, int, int);
2257902c8dcSWojciech Macek static void al_miibus_statchg(device_t);
2267902c8dcSWojciech Macek static void al_miibus_linkchg(device_t);
2277902c8dcSWojciech Macek 
2287902c8dcSWojciech Macek struct al_eth_adapter* g_adapters[16];
2297902c8dcSWojciech Macek uint32_t g_adapters_count;
2307902c8dcSWojciech Macek 
2317902c8dcSWojciech Macek /* flag for napi-like mbuf processing, controlled from sysctl */
2327902c8dcSWojciech Macek static int napi = 0;
2337902c8dcSWojciech Macek 
2347902c8dcSWojciech Macek static device_method_t al_methods[] = {
2357902c8dcSWojciech Macek 	/* Device interface */
2367902c8dcSWojciech Macek 	DEVMETHOD(device_probe,		al_probe),
2377902c8dcSWojciech Macek 	DEVMETHOD(device_attach,	al_attach),
2387902c8dcSWojciech Macek 	DEVMETHOD(device_detach,	al_detach),
2397902c8dcSWojciech Macek 	DEVMETHOD(device_shutdown,	al_shutdown),
2407902c8dcSWojciech Macek 
2417902c8dcSWojciech Macek 	DEVMETHOD(miibus_readreg,	al_miibus_readreg),
2427902c8dcSWojciech Macek 	DEVMETHOD(miibus_writereg,	al_miibus_writereg),
2437902c8dcSWojciech Macek 	DEVMETHOD(miibus_statchg,	al_miibus_statchg),
2447902c8dcSWojciech Macek 	DEVMETHOD(miibus_linkchg,	al_miibus_linkchg),
2457902c8dcSWojciech Macek 	{ 0, 0 }
2467902c8dcSWojciech Macek };
2477902c8dcSWojciech Macek 
2487902c8dcSWojciech Macek static driver_t al_driver = {
2497902c8dcSWojciech Macek 	"al",
2507902c8dcSWojciech Macek 	al_methods,
2517902c8dcSWojciech Macek 	sizeof(struct al_eth_adapter),
2527902c8dcSWojciech Macek };
2537902c8dcSWojciech Macek 
2546a9d865cSJohn Baldwin DRIVER_MODULE(al, pci, al_driver, 0, 0);
2553e38757dSJohn Baldwin DRIVER_MODULE(miibus, al, miibus_driver, 0, 0);
2567902c8dcSWojciech Macek 
2577902c8dcSWojciech Macek static int
2587902c8dcSWojciech Macek al_probe(device_t dev)
2597902c8dcSWojciech Macek {
2607902c8dcSWojciech Macek 	if ((al_is_device_supported(dev)) != 0) {
2617902c8dcSWojciech Macek 		device_set_desc(dev, "al");
2627902c8dcSWojciech Macek 		return (BUS_PROBE_DEFAULT);
2637902c8dcSWojciech Macek 	}
2647902c8dcSWojciech Macek 	return (ENXIO);
2657902c8dcSWojciech Macek }
2667902c8dcSWojciech Macek 
2677902c8dcSWojciech Macek static int
2687902c8dcSWojciech Macek al_attach(device_t dev)
2697902c8dcSWojciech Macek {
2707902c8dcSWojciech Macek 	struct al_eth_adapter *adapter;
2717902c8dcSWojciech Macek 	struct sysctl_oid_list *child;
2727902c8dcSWojciech Macek 	struct sysctl_ctx_list *ctx;
2737902c8dcSWojciech Macek 	struct sysctl_oid *tree;
274bc14c73bSJustin Hibbits 	if_t ifp;
2757902c8dcSWojciech Macek 	uint32_t dev_id;
2767902c8dcSWojciech Macek 	uint32_t rev_id;
2777902c8dcSWojciech Macek 	int bar_udma;
2787902c8dcSWojciech Macek 	int bar_mac;
2797902c8dcSWojciech Macek 	int bar_ec;
2807902c8dcSWojciech Macek 	int err;
2817902c8dcSWojciech Macek 
2827902c8dcSWojciech Macek 	err = 0;
2837902c8dcSWojciech Macek 	ifp = NULL;
2847902c8dcSWojciech Macek 	dev_id = rev_id = 0;
2857902c8dcSWojciech Macek 	ctx = device_get_sysctl_ctx(dev);
2867902c8dcSWojciech Macek 	tree = SYSCTL_PARENT(device_get_sysctl_tree(dev));
2877902c8dcSWojciech Macek 	child = SYSCTL_CHILDREN(tree);
2887902c8dcSWojciech Macek 
2897902c8dcSWojciech Macek 	if (g_adapters_count == 0) {
2907902c8dcSWojciech Macek 		SYSCTL_ADD_INT(ctx, child, OID_AUTO, "napi",
2917902c8dcSWojciech Macek 		    CTLFLAG_RW, &napi, 0, "Use pseudo-napi mechanism");
2927902c8dcSWojciech Macek 	}
2937902c8dcSWojciech Macek 	adapter = device_get_softc(dev);
2947902c8dcSWojciech Macek 	adapter->dev = dev;
2957902c8dcSWojciech Macek 	adapter->board_type = ALPINE_INTEGRATED;
2967902c8dcSWojciech Macek 	snprintf(adapter->name, AL_ETH_NAME_MAX_LEN, "%s",
2977902c8dcSWojciech Macek 	    device_get_nameunit(dev));
2987902c8dcSWojciech Macek 	AL_RX_LOCK_INIT(adapter);
2997902c8dcSWojciech Macek 
3007902c8dcSWojciech Macek 	g_adapters[g_adapters_count] = adapter;
3017902c8dcSWojciech Macek 
3027902c8dcSWojciech Macek 	bar_udma = PCIR_BAR(AL_ETH_UDMA_BAR);
3037902c8dcSWojciech Macek 	adapter->udma_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
3047902c8dcSWojciech Macek 	    &bar_udma, RF_ACTIVE);
3057902c8dcSWojciech Macek 	if (adapter->udma_res == NULL) {
3067902c8dcSWojciech Macek 		device_printf(adapter->dev,
3077902c8dcSWojciech Macek 		    "could not allocate memory resources for DMA.\n");
3087902c8dcSWojciech Macek 		err = ENOMEM;
3097902c8dcSWojciech Macek 		goto err_res_dma;
3107902c8dcSWojciech Macek 	}
3117902c8dcSWojciech Macek 	adapter->udma_base = al_bus_dma_to_va(rman_get_bustag(adapter->udma_res),
3127902c8dcSWojciech Macek 	    rman_get_bushandle(adapter->udma_res));
3137902c8dcSWojciech Macek 	bar_mac = PCIR_BAR(AL_ETH_MAC_BAR);
3147902c8dcSWojciech Macek 	adapter->mac_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY,
3157902c8dcSWojciech Macek 	    &bar_mac, RF_ACTIVE);
3167902c8dcSWojciech Macek 	if (adapter->mac_res == NULL) {
3177902c8dcSWojciech Macek 		device_printf(adapter->dev,
3187902c8dcSWojciech Macek 		    "could not allocate memory resources for MAC.\n");
3197902c8dcSWojciech Macek 		err = ENOMEM;
3207902c8dcSWojciech Macek 		goto err_res_mac;
3217902c8dcSWojciech Macek 	}
3227902c8dcSWojciech Macek 	adapter->mac_base = al_bus_dma_to_va(rman_get_bustag(adapter->mac_res),
3237902c8dcSWojciech Macek 	    rman_get_bushandle(adapter->mac_res));
3247902c8dcSWojciech Macek 
3257902c8dcSWojciech Macek 	bar_ec = PCIR_BAR(AL_ETH_EC_BAR);
3267902c8dcSWojciech Macek 	adapter->ec_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &bar_ec,
3277902c8dcSWojciech Macek 	    RF_ACTIVE);
3287902c8dcSWojciech Macek 	if (adapter->ec_res == NULL) {
3297902c8dcSWojciech Macek 		device_printf(adapter->dev,
3307902c8dcSWojciech Macek 		    "could not allocate memory resources for EC.\n");
3317902c8dcSWojciech Macek 		err = ENOMEM;
3327902c8dcSWojciech Macek 		goto err_res_ec;
3337902c8dcSWojciech Macek 	}
3347902c8dcSWojciech Macek 	adapter->ec_base = al_bus_dma_to_va(rman_get_bustag(adapter->ec_res),
3357902c8dcSWojciech Macek 	    rman_get_bushandle(adapter->ec_res));
3367902c8dcSWojciech Macek 
3377902c8dcSWojciech Macek 	adapter->netdev = ifp = if_alloc(IFT_ETHER);
3387902c8dcSWojciech Macek 
339bc14c73bSJustin Hibbits 	if_setsoftc(ifp, adapter);
3407902c8dcSWojciech Macek 	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
341bc14c73bSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
342bc14c73bSJustin Hibbits 	if_setflags(ifp, if_getdrvflags(ifp));
343bc14c73bSJustin Hibbits 	if_setflagbits(ifp, IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | IFF_ALLMULTI, 0);
344bc14c73bSJustin Hibbits 	if_settransmitfn(ifp, al_mq_start);
345bc14c73bSJustin Hibbits 	if_setqflushfn(ifp, al_qflush);
346bc14c73bSJustin Hibbits 	if_setioctlfn(ifp, al_ioctl);
347bc14c73bSJustin Hibbits 	if_setinitfn(ifp, al_init);
348bc14c73bSJustin Hibbits 	if_setgetcounterfn(ifp, al_get_counter);
349bc14c73bSJustin Hibbits 	if_setmtu(ifp, AL_DEFAULT_MTU);
3507902c8dcSWojciech Macek 
351bc14c73bSJustin Hibbits 	adapter->if_flags = if_getflags(ifp);
3527902c8dcSWojciech Macek 
353bc14c73bSJustin Hibbits 	if_setcapabilities(ifp, if_getcapenable(ifp) );
3547902c8dcSWojciech Macek 
355bc14c73bSJustin Hibbits 	if_setcapabilitiesbit(ifp, IFCAP_HWCSUM |
3567902c8dcSWojciech Macek 	    IFCAP_HWCSUM_IPV6 | IFCAP_TSO |
357bc14c73bSJustin Hibbits 	    IFCAP_LRO | IFCAP_JUMBO_MTU, 0);
3587902c8dcSWojciech Macek 
359bc14c73bSJustin Hibbits 	if_setcapenable(ifp, if_getcapabilities(ifp));
3607902c8dcSWojciech Macek 
3617902c8dcSWojciech Macek 	adapter->id_number = g_adapters_count;
3627902c8dcSWojciech Macek 
3637902c8dcSWojciech Macek 	if (adapter->board_type == ALPINE_INTEGRATED) {
3647902c8dcSWojciech Macek 		dev_id = pci_get_device(adapter->dev);
3657902c8dcSWojciech Macek 		rev_id = pci_get_revid(adapter->dev);
3667902c8dcSWojciech Macek 	} else {
3677902c8dcSWojciech Macek 		al_eth_fpga_read_pci_config(adapter->internal_pcie_base,
3687902c8dcSWojciech Macek 		    PCIR_DEVICE, &dev_id);
3697902c8dcSWojciech Macek 		al_eth_fpga_read_pci_config(adapter->internal_pcie_base,
3707902c8dcSWojciech Macek 		    PCIR_REVID, &rev_id);
3717902c8dcSWojciech Macek 	}
3727902c8dcSWojciech Macek 
3737902c8dcSWojciech Macek 	adapter->dev_id = dev_id;
3747902c8dcSWojciech Macek 	adapter->rev_id = rev_id;
3757902c8dcSWojciech Macek 
3767902c8dcSWojciech Macek 	/* set default ring sizes */
3777902c8dcSWojciech Macek 	adapter->tx_ring_count = AL_ETH_DEFAULT_TX_SW_DESCS;
3787902c8dcSWojciech Macek 	adapter->tx_descs_count = AL_ETH_DEFAULT_TX_HW_DESCS;
3797902c8dcSWojciech Macek 	adapter->rx_ring_count = AL_ETH_DEFAULT_RX_DESCS;
3807902c8dcSWojciech Macek 	adapter->rx_descs_count = AL_ETH_DEFAULT_RX_DESCS;
3817902c8dcSWojciech Macek 
3827902c8dcSWojciech Macek 	adapter->num_tx_queues = AL_ETH_NUM_QUEUES;
3837902c8dcSWojciech Macek 	adapter->num_rx_queues = AL_ETH_NUM_QUEUES;
3847902c8dcSWojciech Macek 
3857902c8dcSWojciech Macek 	adapter->small_copy_len	= AL_ETH_DEFAULT_SMALL_PACKET_LEN;
3867902c8dcSWojciech Macek 	adapter->link_poll_interval = AL_ETH_DEFAULT_LINK_POLL_INTERVAL;
3877902c8dcSWojciech Macek 	adapter->max_rx_buff_alloc_size = AL_ETH_DEFAULT_MAX_RX_BUFF_ALLOC_SIZE;
3887902c8dcSWojciech Macek 
389f46a05b5SJustin Hibbits 	al_eth_req_rx_buff_size(adapter, if_getmtu(adapter->netdev));
3907902c8dcSWojciech Macek 
3917902c8dcSWojciech Macek 	adapter->link_config.force_1000_base_x = AL_ETH_DEFAULT_FORCE_1000_BASEX;
3927902c8dcSWojciech Macek 
3937902c8dcSWojciech Macek 	err = al_eth_board_params_init(adapter);
3947902c8dcSWojciech Macek 	if (err != 0)
3957902c8dcSWojciech Macek 		goto err;
3967902c8dcSWojciech Macek 
3977902c8dcSWojciech Macek 	if (adapter->mac_mode == AL_ETH_MAC_MODE_10GbE_Serial) {
3987902c8dcSWojciech Macek 		ifmedia_init(&adapter->media, IFM_IMASK,
3997902c8dcSWojciech Macek 		    al_media_update, al_media_status);
4007902c8dcSWojciech Macek 		ifmedia_add(&adapter->media, IFM_ETHER | IFM_1000_LX, 0, NULL);
4017902c8dcSWojciech Macek 		ifmedia_add(&adapter->media, IFM_ETHER | IFM_10G_LR, 0, NULL);
4027902c8dcSWojciech Macek 		ifmedia_add(&adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL);
4037902c8dcSWojciech Macek 		ifmedia_set(&adapter->media, IFM_ETHER | IFM_AUTO);
4047902c8dcSWojciech Macek 	}
4057902c8dcSWojciech Macek 
4067902c8dcSWojciech Macek 	al_eth_function_reset(adapter);
4077902c8dcSWojciech Macek 
4087902c8dcSWojciech Macek 	err = al_eth_hw_init_adapter(adapter);
4097902c8dcSWojciech Macek 	if (err != 0)
4107902c8dcSWojciech Macek 		goto err;
4117902c8dcSWojciech Macek 
4127902c8dcSWojciech Macek 	al_eth_init_rings(adapter);
4137902c8dcSWojciech Macek 	g_adapters_count++;
4147902c8dcSWojciech Macek 
4157902c8dcSWojciech Macek 	al_eth_lm_config(adapter);
4167902c8dcSWojciech Macek 	mtx_init(&adapter->stats_mtx, "AlStatsMtx", NULL, MTX_DEF);
4177902c8dcSWojciech Macek 	mtx_init(&adapter->wd_mtx, "AlWdMtx", NULL, MTX_DEF);
4187902c8dcSWojciech Macek 	callout_init_mtx(&adapter->stats_callout, &adapter->stats_mtx, 0);
4197902c8dcSWojciech Macek 	callout_init_mtx(&adapter->wd_callout, &adapter->wd_mtx, 0);
4207902c8dcSWojciech Macek 
4217902c8dcSWojciech Macek 	ether_ifattach(ifp, adapter->mac_addr);
422bc14c73bSJustin Hibbits 	if_setmtu(ifp, AL_DEFAULT_MTU);
4237902c8dcSWojciech Macek 
4247902c8dcSWojciech Macek 	if (adapter->mac_mode == AL_ETH_MAC_MODE_RGMII) {
4257902c8dcSWojciech Macek 		al_eth_hw_init(adapter);
4267902c8dcSWojciech Macek 
4277902c8dcSWojciech Macek 		/* Attach PHY(s) */
4287902c8dcSWojciech Macek 		err = mii_attach(adapter->dev, &adapter->miibus, adapter->netdev,
4297902c8dcSWojciech Macek 		    al_media_update, al_media_status, BMSR_DEFCAPMASK, 0,
4307902c8dcSWojciech Macek 		    MII_OFFSET_ANY, 0);
4317902c8dcSWojciech Macek 		if (err != 0) {
4327902c8dcSWojciech Macek 			device_printf(adapter->dev, "attaching PHYs failed\n");
4337902c8dcSWojciech Macek 			return (err);
4347902c8dcSWojciech Macek 		}
4357902c8dcSWojciech Macek 
4367902c8dcSWojciech Macek 		adapter->mii = device_get_softc(adapter->miibus);
4377902c8dcSWojciech Macek 	}
4387902c8dcSWojciech Macek 
4397902c8dcSWojciech Macek 	return (err);
4407902c8dcSWojciech Macek 
4417902c8dcSWojciech Macek err:
4427902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY, bar_ec, adapter->ec_res);
4437902c8dcSWojciech Macek err_res_ec:
4447902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY, bar_mac, adapter->mac_res);
4457902c8dcSWojciech Macek err_res_mac:
4467902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY, bar_udma, adapter->udma_res);
4477902c8dcSWojciech Macek err_res_dma:
4487902c8dcSWojciech Macek 	return (err);
4497902c8dcSWojciech Macek }
4507902c8dcSWojciech Macek 
4517902c8dcSWojciech Macek static int
4527902c8dcSWojciech Macek al_detach(device_t dev)
4537902c8dcSWojciech Macek {
4547902c8dcSWojciech Macek 	struct al_eth_adapter *adapter;
4557902c8dcSWojciech Macek 
4567902c8dcSWojciech Macek 	adapter = device_get_softc(dev);
4577902c8dcSWojciech Macek 	ether_ifdetach(adapter->netdev);
4587902c8dcSWojciech Macek 
4597902c8dcSWojciech Macek 	mtx_destroy(&adapter->stats_mtx);
4607902c8dcSWojciech Macek 	mtx_destroy(&adapter->wd_mtx);
4617902c8dcSWojciech Macek 
4627902c8dcSWojciech Macek 	al_eth_down(adapter);
4637902c8dcSWojciech Macek 
4647902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_IRQ,    0, adapter->irq_res);
4657902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY, 0, adapter->ec_res);
4667902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY, 0, adapter->mac_res);
4677902c8dcSWojciech Macek 	bus_release_resource(dev, SYS_RES_MEMORY, 0, adapter->udma_res);
4687902c8dcSWojciech Macek 
4697902c8dcSWojciech Macek 	return (0);
4707902c8dcSWojciech Macek }
4717902c8dcSWojciech Macek 
4727902c8dcSWojciech Macek int
4737902c8dcSWojciech Macek al_eth_fpga_read_pci_config(void *handle, int where, uint32_t *val)
4747902c8dcSWojciech Macek {
4757902c8dcSWojciech Macek 
4767902c8dcSWojciech Macek 	/* handle is the base address of the adapter */
4777902c8dcSWojciech Macek 	*val = al_reg_read32((void*)((u_long)handle + where));
4787902c8dcSWojciech Macek 
4797902c8dcSWojciech Macek 	return (0);
4807902c8dcSWojciech Macek }
4817902c8dcSWojciech Macek 
4827902c8dcSWojciech Macek int
4837902c8dcSWojciech Macek al_eth_fpga_write_pci_config(void *handle, int where, uint32_t val)
4847902c8dcSWojciech Macek {
4857902c8dcSWojciech Macek 
4867902c8dcSWojciech Macek 	/* handle is the base address of the adapter */
4877902c8dcSWojciech Macek 	al_reg_write32((void*)((u_long)handle + where), val);
4887902c8dcSWojciech Macek 	return (0);
4897902c8dcSWojciech Macek }
4907902c8dcSWojciech Macek 
4917902c8dcSWojciech Macek int
4927902c8dcSWojciech Macek al_eth_read_pci_config(void *handle, int where, uint32_t *val)
4937902c8dcSWojciech Macek {
4947902c8dcSWojciech Macek 
4957902c8dcSWojciech Macek 	/* handle is a pci_dev */
4967902c8dcSWojciech Macek 	*val = pci_read_config((device_t)handle, where, sizeof(*val));
4977902c8dcSWojciech Macek 	return (0);
4987902c8dcSWojciech Macek }
4997902c8dcSWojciech Macek 
5007902c8dcSWojciech Macek int
5017902c8dcSWojciech Macek al_eth_write_pci_config(void *handle, int where, uint32_t val)
5027902c8dcSWojciech Macek {
5037902c8dcSWojciech Macek 
5047902c8dcSWojciech Macek 	/* handle is a pci_dev */
5057902c8dcSWojciech Macek 	pci_write_config((device_t)handle, where, val, sizeof(val));
5067902c8dcSWojciech Macek 	return (0);
5077902c8dcSWojciech Macek }
5087902c8dcSWojciech Macek 
5097902c8dcSWojciech Macek void
5107902c8dcSWojciech Macek al_eth_irq_config(uint32_t *offset, uint32_t value)
5117902c8dcSWojciech Macek {
5127902c8dcSWojciech Macek 
5137902c8dcSWojciech Macek 	al_reg_write32_relaxed(offset, value);
5147902c8dcSWojciech Macek }
5157902c8dcSWojciech Macek 
5167902c8dcSWojciech Macek void
5177902c8dcSWojciech Macek al_eth_forward_int_config(uint32_t *offset, uint32_t value)
5187902c8dcSWojciech Macek {
5197902c8dcSWojciech Macek 
5207902c8dcSWojciech Macek 	al_reg_write32(offset, value);
5217902c8dcSWojciech Macek }
5227902c8dcSWojciech Macek 
5237902c8dcSWojciech Macek static void
5247902c8dcSWojciech Macek al_eth_serdes_init(struct al_eth_adapter *adapter)
5257902c8dcSWojciech Macek {
5267902c8dcSWojciech Macek 	void __iomem	*serdes_base;
5277902c8dcSWojciech Macek 
5287902c8dcSWojciech Macek 	adapter->serdes_init = false;
5297902c8dcSWojciech Macek 
5307902c8dcSWojciech Macek 	serdes_base = alpine_serdes_resource_get(adapter->serdes_grp);
5317902c8dcSWojciech Macek 	if (serdes_base == NULL) {
5327902c8dcSWojciech Macek 		device_printf(adapter->dev, "serdes_base get failed!\n");
5337902c8dcSWojciech Macek 		return;
5347902c8dcSWojciech Macek 	}
5357902c8dcSWojciech Macek 
5367902c8dcSWojciech Macek 	serdes_base = al_bus_dma_to_va(serdes_tag, serdes_base);
5377902c8dcSWojciech Macek 
5387902c8dcSWojciech Macek 	al_serdes_handle_grp_init(serdes_base, adapter->serdes_grp,
5397902c8dcSWojciech Macek 	    &adapter->serdes_obj);
5407902c8dcSWojciech Macek 
5417902c8dcSWojciech Macek 	adapter->serdes_init = true;
5427902c8dcSWojciech Macek }
5437902c8dcSWojciech Macek 
5447902c8dcSWojciech Macek static void
5457902c8dcSWojciech Macek al_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
5467902c8dcSWojciech Macek {
5477902c8dcSWojciech Macek 	bus_addr_t *paddr;
5487902c8dcSWojciech Macek 
5497902c8dcSWojciech Macek 	paddr = arg;
5507902c8dcSWojciech Macek 	*paddr = segs->ds_addr;
5517902c8dcSWojciech Macek }
5527902c8dcSWojciech Macek 
5537902c8dcSWojciech Macek static int
554d8b1601dSMark Johnston al_dma_alloc_coherent(device_t dev, bus_dma_tag_t *tag, bus_dmamap_t *map,
5557902c8dcSWojciech Macek     bus_addr_t *baddr, void **vaddr, uint32_t size)
5567902c8dcSWojciech Macek {
5577902c8dcSWojciech Macek 	int ret;
5587902c8dcSWojciech Macek 	uint32_t maxsize = ((size - 1)/PAGE_SIZE + 1) * PAGE_SIZE;
5597902c8dcSWojciech Macek 
5607902c8dcSWojciech Macek 	ret = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
5617902c8dcSWojciech Macek 	    BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
5627902c8dcSWojciech Macek 	    maxsize, 1, maxsize, BUS_DMA_COHERENT, NULL, NULL, tag);
5637902c8dcSWojciech Macek 	if (ret != 0) {
5647902c8dcSWojciech Macek 		device_printf(dev,
5657902c8dcSWojciech Macek 		    "failed to create bus tag, ret = %d\n", ret);
5667902c8dcSWojciech Macek 		return (ret);
5677902c8dcSWojciech Macek 	}
5687902c8dcSWojciech Macek 
5697902c8dcSWojciech Macek 	ret = bus_dmamem_alloc(*tag, vaddr,
5707902c8dcSWojciech Macek 	    BUS_DMA_COHERENT | BUS_DMA_ZERO, map);
5717902c8dcSWojciech Macek 	if (ret != 0) {
5727902c8dcSWojciech Macek 		device_printf(dev,
5737902c8dcSWojciech Macek 		    "failed to allocate dmamem, ret = %d\n", ret);
5747902c8dcSWojciech Macek 		return (ret);
5757902c8dcSWojciech Macek 	}
5767902c8dcSWojciech Macek 
5777902c8dcSWojciech Macek 	ret = bus_dmamap_load(*tag, *map, *vaddr,
5787902c8dcSWojciech Macek 	    size, al_dma_map_addr, baddr, 0);
5797902c8dcSWojciech Macek 	if (ret != 0) {
5807902c8dcSWojciech Macek 		device_printf(dev,
5817902c8dcSWojciech Macek 		    "failed to allocate bus_dmamap_load, ret = %d\n", ret);
5827902c8dcSWojciech Macek 		return (ret);
5837902c8dcSWojciech Macek 	}
5847902c8dcSWojciech Macek 
5857902c8dcSWojciech Macek 	return (0);
5867902c8dcSWojciech Macek }
5877902c8dcSWojciech Macek 
5887902c8dcSWojciech Macek static void
5897902c8dcSWojciech Macek al_dma_free_coherent(bus_dma_tag_t tag, bus_dmamap_t map, void *vaddr)
5907902c8dcSWojciech Macek {
5917902c8dcSWojciech Macek 
5927902c8dcSWojciech Macek 	bus_dmamap_unload(tag, map);
5937902c8dcSWojciech Macek 	bus_dmamem_free(tag, vaddr, map);
5947902c8dcSWojciech Macek 	bus_dma_tag_destroy(tag);
5957902c8dcSWojciech Macek }
5967902c8dcSWojciech Macek 
5977902c8dcSWojciech Macek static void
5987902c8dcSWojciech Macek al_eth_mac_table_unicast_add(struct al_eth_adapter *adapter,
59957d36163SGleb Smirnoff     uint8_t idx, uint8_t udma_mask)
6007902c8dcSWojciech Macek {
6017902c8dcSWojciech Macek 	struct al_eth_fwd_mac_table_entry entry = { { 0 } };
6027902c8dcSWojciech Macek 
6037902c8dcSWojciech Macek 	memcpy(entry.addr, adapter->mac_addr, sizeof(adapter->mac_addr));
6047902c8dcSWojciech Macek 
6057902c8dcSWojciech Macek 	memset(entry.mask, 0xff, sizeof(entry.mask));
6067902c8dcSWojciech Macek 	entry.rx_valid = true;
6077902c8dcSWojciech Macek 	entry.tx_valid = false;
6087902c8dcSWojciech Macek 	entry.udma_mask = udma_mask;
6097902c8dcSWojciech Macek 	entry.filter = false;
6107902c8dcSWojciech Macek 
6117902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev,
6127902c8dcSWojciech Macek 	    "%s: [%d]: addr "MAC_ADDR_STR" mask "MAC_ADDR_STR"\n",
6137902c8dcSWojciech Macek 	    __func__, idx, MAC_ADDR(entry.addr), MAC_ADDR(entry.mask));
6147902c8dcSWojciech Macek 
6157902c8dcSWojciech Macek 	al_eth_fwd_mac_table_set(&adapter->hal_adapter, idx, &entry);
6167902c8dcSWojciech Macek }
6177902c8dcSWojciech Macek 
6187902c8dcSWojciech Macek static void
6197902c8dcSWojciech Macek al_eth_mac_table_all_multicast_add(struct al_eth_adapter *adapter, uint8_t idx,
6207902c8dcSWojciech Macek     uint8_t udma_mask)
6217902c8dcSWojciech Macek {
6227902c8dcSWojciech Macek 	struct al_eth_fwd_mac_table_entry entry = { { 0 } };
6237902c8dcSWojciech Macek 
6247902c8dcSWojciech Macek 	memset(entry.addr, 0x00, sizeof(entry.addr));
6257902c8dcSWojciech Macek 	memset(entry.mask, 0x00, sizeof(entry.mask));
6267902c8dcSWojciech Macek 	entry.mask[0] |= 1;
6277902c8dcSWojciech Macek 	entry.addr[0] |= 1;
6287902c8dcSWojciech Macek 
6297902c8dcSWojciech Macek 	entry.rx_valid = true;
6307902c8dcSWojciech Macek 	entry.tx_valid = false;
6317902c8dcSWojciech Macek 	entry.udma_mask = udma_mask;
6327902c8dcSWojciech Macek 	entry.filter = false;
6337902c8dcSWojciech Macek 
6347902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev,
6357902c8dcSWojciech Macek 	    "%s: [%d]: addr "MAC_ADDR_STR" mask "MAC_ADDR_STR"\n",
6367902c8dcSWojciech Macek 	    __func__, idx, MAC_ADDR(entry.addr), MAC_ADDR(entry.mask));
6377902c8dcSWojciech Macek 
6387902c8dcSWojciech Macek 	al_eth_fwd_mac_table_set(&adapter->hal_adapter, idx, &entry);
6397902c8dcSWojciech Macek }
6407902c8dcSWojciech Macek 
6417902c8dcSWojciech Macek static void
6427902c8dcSWojciech Macek al_eth_mac_table_broadcast_add(struct al_eth_adapter *adapter,
6437902c8dcSWojciech Macek     uint8_t idx, uint8_t udma_mask)
6447902c8dcSWojciech Macek {
6457902c8dcSWojciech Macek 	struct al_eth_fwd_mac_table_entry entry = { { 0 } };
6467902c8dcSWojciech Macek 
6477902c8dcSWojciech Macek 	memset(entry.addr, 0xff, sizeof(entry.addr));
6487902c8dcSWojciech Macek 	memset(entry.mask, 0xff, sizeof(entry.mask));
6497902c8dcSWojciech Macek 
6507902c8dcSWojciech Macek 	entry.rx_valid = true;
6517902c8dcSWojciech Macek 	entry.tx_valid = false;
6527902c8dcSWojciech Macek 	entry.udma_mask = udma_mask;
6537902c8dcSWojciech Macek 	entry.filter = false;
6547902c8dcSWojciech Macek 
6557902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev,
6567902c8dcSWojciech Macek 	    "%s: [%d]: addr "MAC_ADDR_STR" mask "MAC_ADDR_STR"\n",
6577902c8dcSWojciech Macek 	    __func__, idx, MAC_ADDR(entry.addr), MAC_ADDR(entry.mask));
6587902c8dcSWojciech Macek 
6597902c8dcSWojciech Macek 	al_eth_fwd_mac_table_set(&adapter->hal_adapter, idx, &entry);
6607902c8dcSWojciech Macek }
6617902c8dcSWojciech Macek 
6627902c8dcSWojciech Macek static void
6637902c8dcSWojciech Macek al_eth_mac_table_promiscuous_set(struct al_eth_adapter *adapter,
664463edaf4SJohn Baldwin     bool promiscuous)
6657902c8dcSWojciech Macek {
6667902c8dcSWojciech Macek 	struct al_eth_fwd_mac_table_entry entry = { { 0 } };
6677902c8dcSWojciech Macek 
6687902c8dcSWojciech Macek 	memset(entry.addr, 0x00, sizeof(entry.addr));
6697902c8dcSWojciech Macek 	memset(entry.mask, 0x00, sizeof(entry.mask));
6707902c8dcSWojciech Macek 
6717902c8dcSWojciech Macek 	entry.rx_valid = true;
6727902c8dcSWojciech Macek 	entry.tx_valid = false;
6737902c8dcSWojciech Macek 	entry.udma_mask = (promiscuous) ? 1 : 0;
6747902c8dcSWojciech Macek 	entry.filter = (promiscuous) ? false : true;
6757902c8dcSWojciech Macek 
6767902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "%s: %s promiscuous mode\n",
6777902c8dcSWojciech Macek 	    __func__, (promiscuous) ? "enter" : "exit");
6787902c8dcSWojciech Macek 
6797902c8dcSWojciech Macek 	al_eth_fwd_mac_table_set(&adapter->hal_adapter,
6807902c8dcSWojciech Macek 	    AL_ETH_MAC_TABLE_DROP_IDX, &entry);
6817902c8dcSWojciech Macek }
6827902c8dcSWojciech Macek 
6837902c8dcSWojciech Macek static void
6847902c8dcSWojciech Macek al_eth_set_thash_table_entry(struct al_eth_adapter *adapter, uint8_t idx,
6857902c8dcSWojciech Macek     uint8_t udma, uint32_t queue)
6867902c8dcSWojciech Macek {
6877902c8dcSWojciech Macek 
6887902c8dcSWojciech Macek 	if (udma != 0)
6897902c8dcSWojciech Macek 		panic("only UDMA0 is supporter");
6907902c8dcSWojciech Macek 
6917902c8dcSWojciech Macek 	if (queue >= AL_ETH_NUM_QUEUES)
6927902c8dcSWojciech Macek 		panic("invalid queue number");
6937902c8dcSWojciech Macek 
6947902c8dcSWojciech Macek 	al_eth_thash_table_set(&adapter->hal_adapter, idx, udma, queue);
6957902c8dcSWojciech Macek }
6967902c8dcSWojciech Macek 
6977902c8dcSWojciech Macek /* init FSM, no tunneling supported yet, if packet is tcp/udp over ipv4/ipv6, use 4 tuple hash */
6987902c8dcSWojciech Macek static void
6997902c8dcSWojciech Macek al_eth_fsm_table_init(struct al_eth_adapter *adapter)
7007902c8dcSWojciech Macek {
7017902c8dcSWojciech Macek 	uint32_t val;
7027902c8dcSWojciech Macek 	int i;
7037902c8dcSWojciech Macek 
7047902c8dcSWojciech Macek 	for (i = 0; i < AL_ETH_RX_FSM_TABLE_SIZE; i++) {
7057902c8dcSWojciech Macek 		uint8_t outer_type = AL_ETH_FSM_ENTRY_OUTER(i);
7067902c8dcSWojciech Macek 		switch (outer_type) {
7077902c8dcSWojciech Macek 		case AL_ETH_FSM_ENTRY_IPV4_TCP:
7087902c8dcSWojciech Macek 		case AL_ETH_FSM_ENTRY_IPV4_UDP:
7097902c8dcSWojciech Macek 		case AL_ETH_FSM_ENTRY_IPV6_TCP:
7107902c8dcSWojciech Macek 		case AL_ETH_FSM_ENTRY_IPV6_UDP:
7117902c8dcSWojciech Macek 			val = AL_ETH_FSM_DATA_OUTER_4_TUPLE |
7127902c8dcSWojciech Macek 			    AL_ETH_FSM_DATA_HASH_SEL;
7137902c8dcSWojciech Macek 			break;
7147902c8dcSWojciech Macek 		case AL_ETH_FSM_ENTRY_IPV6_NO_UDP_TCP:
7157902c8dcSWojciech Macek 		case AL_ETH_FSM_ENTRY_IPV4_NO_UDP_TCP:
7167902c8dcSWojciech Macek 			val = AL_ETH_FSM_DATA_OUTER_2_TUPLE |
7177902c8dcSWojciech Macek 			    AL_ETH_FSM_DATA_HASH_SEL;
7187902c8dcSWojciech Macek 			break;
7197902c8dcSWojciech Macek 		default:
7207902c8dcSWojciech Macek 			val = AL_ETH_FSM_DATA_DEFAULT_Q |
7217902c8dcSWojciech Macek 			    AL_ETH_FSM_DATA_DEFAULT_UDMA;
7227902c8dcSWojciech Macek 		}
7237902c8dcSWojciech Macek 		al_eth_fsm_table_set(&adapter->hal_adapter, i, val);
7247902c8dcSWojciech Macek 	}
7257902c8dcSWojciech Macek }
7267902c8dcSWojciech Macek 
7277902c8dcSWojciech Macek static void
7287902c8dcSWojciech Macek al_eth_mac_table_entry_clear(struct al_eth_adapter *adapter,
7297902c8dcSWojciech Macek     uint8_t idx)
7307902c8dcSWojciech Macek {
7317902c8dcSWojciech Macek 	struct al_eth_fwd_mac_table_entry entry = { { 0 } };
7327902c8dcSWojciech Macek 
7337902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "%s: clear entry %d\n", __func__, idx);
7347902c8dcSWojciech Macek 
7357902c8dcSWojciech Macek 	al_eth_fwd_mac_table_set(&adapter->hal_adapter, idx, &entry);
7367902c8dcSWojciech Macek }
7377902c8dcSWojciech Macek 
7387902c8dcSWojciech Macek static int
7397902c8dcSWojciech Macek al_eth_hw_init_adapter(struct al_eth_adapter *adapter)
7407902c8dcSWojciech Macek {
7417902c8dcSWojciech Macek 	struct al_eth_adapter_params *params = &adapter->eth_hal_params;
7427902c8dcSWojciech Macek 	int rc;
7437902c8dcSWojciech Macek 
7447902c8dcSWojciech Macek 	/* params->dev_id = adapter->dev_id; */
7457902c8dcSWojciech Macek 	params->rev_id = adapter->rev_id;
7467902c8dcSWojciech Macek 	params->udma_id = 0;
7477902c8dcSWojciech Macek 	params->enable_rx_parser = 1; /* enable rx epe parser*/
7487902c8dcSWojciech Macek 	params->udma_regs_base = adapter->udma_base; /* UDMA register base address */
7497902c8dcSWojciech Macek 	params->ec_regs_base = adapter->ec_base; /* Ethernet controller registers base address */
7507902c8dcSWojciech Macek 	params->mac_regs_base = adapter->mac_base; /* Ethernet MAC registers base address */
7517902c8dcSWojciech Macek 	params->name = adapter->name;
7527902c8dcSWojciech Macek 	params->serdes_lane = adapter->serdes_lane;
7537902c8dcSWojciech Macek 
7547902c8dcSWojciech Macek 	rc = al_eth_adapter_init(&adapter->hal_adapter, params);
7557902c8dcSWojciech Macek 	if (rc != 0)
7567902c8dcSWojciech Macek 		device_printf(adapter->dev, "%s failed at hal init!\n",
7577902c8dcSWojciech Macek 		    __func__);
7587902c8dcSWojciech Macek 
7597902c8dcSWojciech Macek 	if ((adapter->board_type == ALPINE_NIC) ||
7607902c8dcSWojciech Macek 	    (adapter->board_type == ALPINE_FPGA_NIC)) {
7617902c8dcSWojciech Macek 		/* in pcie NIC mode, force eth UDMA to access PCIE0 using the vmid */
7627902c8dcSWojciech Macek 		struct al_udma_gen_tgtid_conf conf;
7637902c8dcSWojciech Macek 		int i;
7647902c8dcSWojciech Macek 		for (i = 0; i < DMA_MAX_Q; i++) {
7657902c8dcSWojciech Macek 			conf.tx_q_conf[i].queue_en = AL_TRUE;
7667902c8dcSWojciech Macek 			conf.tx_q_conf[i].desc_en = AL_FALSE;
7677902c8dcSWojciech Macek 			conf.tx_q_conf[i].tgtid = 0x100; /* for access from PCIE0 */
7687902c8dcSWojciech Macek 			conf.rx_q_conf[i].queue_en = AL_TRUE;
7697902c8dcSWojciech Macek 			conf.rx_q_conf[i].desc_en = AL_FALSE;
7707902c8dcSWojciech Macek 			conf.rx_q_conf[i].tgtid = 0x100; /* for access from PCIE0 */
7717902c8dcSWojciech Macek 		}
7727902c8dcSWojciech Macek 		al_udma_gen_tgtid_conf_set(adapter->udma_base, &conf);
7737902c8dcSWojciech Macek 	}
7747902c8dcSWojciech Macek 
7757902c8dcSWojciech Macek 	return (rc);
7767902c8dcSWojciech Macek }
7777902c8dcSWojciech Macek 
7787902c8dcSWojciech Macek static void
7797902c8dcSWojciech Macek al_eth_lm_config(struct al_eth_adapter *adapter)
7807902c8dcSWojciech Macek {
7817902c8dcSWojciech Macek 	struct al_eth_lm_init_params params = {0};
7827902c8dcSWojciech Macek 
7837902c8dcSWojciech Macek 	params.adapter = &adapter->hal_adapter;
7847902c8dcSWojciech Macek 	params.serdes_obj = &adapter->serdes_obj;
7857902c8dcSWojciech Macek 	params.lane = adapter->serdes_lane;
7867902c8dcSWojciech Macek 	params.sfp_detection = adapter->sfp_detection_needed;
7877902c8dcSWojciech Macek 	if (adapter->sfp_detection_needed == true) {
7887902c8dcSWojciech Macek 		params.sfp_bus_id = adapter->i2c_adapter_id;
7897902c8dcSWojciech Macek 		params.sfp_i2c_addr = SFP_I2C_ADDR;
7907902c8dcSWojciech Macek 	}
7917902c8dcSWojciech Macek 
7927902c8dcSWojciech Macek 	if (adapter->sfp_detection_needed == false) {
7937902c8dcSWojciech Macek 		switch (adapter->mac_mode) {
7947902c8dcSWojciech Macek 		case AL_ETH_MAC_MODE_10GbE_Serial:
7957902c8dcSWojciech Macek 			if ((adapter->lt_en != 0) && (adapter->an_en != 0))
7967902c8dcSWojciech Macek 				params.default_mode = AL_ETH_LM_MODE_10G_DA;
7977902c8dcSWojciech Macek 			else
7987902c8dcSWojciech Macek 				params.default_mode = AL_ETH_LM_MODE_10G_OPTIC;
7997902c8dcSWojciech Macek 			break;
8007902c8dcSWojciech Macek 		case AL_ETH_MAC_MODE_SGMII:
8017902c8dcSWojciech Macek 			params.default_mode = AL_ETH_LM_MODE_1G;
8027902c8dcSWojciech Macek 			break;
8037902c8dcSWojciech Macek 		default:
8047902c8dcSWojciech Macek 			params.default_mode = AL_ETH_LM_MODE_10G_DA;
8057902c8dcSWojciech Macek 		}
8067902c8dcSWojciech Macek 	} else
8077902c8dcSWojciech Macek 		params.default_mode = AL_ETH_LM_MODE_10G_DA;
8087902c8dcSWojciech Macek 
8097902c8dcSWojciech Macek 	params.link_training = adapter->lt_en;
8107902c8dcSWojciech Macek 	params.rx_equal = true;
8117902c8dcSWojciech Macek 	params.static_values = !adapter->dont_override_serdes;
8127902c8dcSWojciech Macek 	params.i2c_context = adapter;
8137902c8dcSWojciech Macek 	params.kr_fec_enable = false;
8147902c8dcSWojciech Macek 
8157902c8dcSWojciech Macek 	params.retimer_exist = adapter->retimer.exist;
8167902c8dcSWojciech Macek 	params.retimer_bus_id = adapter->retimer.bus_id;
8177902c8dcSWojciech Macek 	params.retimer_i2c_addr = adapter->retimer.i2c_addr;
8187902c8dcSWojciech Macek 	params.retimer_channel = adapter->retimer.channel;
8197902c8dcSWojciech Macek 
8207902c8dcSWojciech Macek 	al_eth_lm_init(&adapter->lm_context, &params);
8217902c8dcSWojciech Macek }
8227902c8dcSWojciech Macek 
8237902c8dcSWojciech Macek static int
8247902c8dcSWojciech Macek al_eth_board_params_init(struct al_eth_adapter *adapter)
8257902c8dcSWojciech Macek {
8267902c8dcSWojciech Macek 
8277902c8dcSWojciech Macek 	if (adapter->board_type == ALPINE_NIC) {
8287902c8dcSWojciech Macek 		adapter->mac_mode = AL_ETH_MAC_MODE_10GbE_Serial;
8297902c8dcSWojciech Macek 		adapter->sfp_detection_needed = false;
8307902c8dcSWojciech Macek 		adapter->phy_exist = false;
8317902c8dcSWojciech Macek 		adapter->an_en = false;
8327902c8dcSWojciech Macek 		adapter->lt_en = false;
8337902c8dcSWojciech Macek 		adapter->ref_clk_freq = AL_ETH_REF_FREQ_375_MHZ;
8347902c8dcSWojciech Macek 		adapter->mdio_freq = AL_ETH_DEFAULT_MDIO_FREQ_KHZ;
8357902c8dcSWojciech Macek 	} else if (adapter->board_type == ALPINE_FPGA_NIC) {
8367902c8dcSWojciech Macek 		adapter->mac_mode = AL_ETH_MAC_MODE_SGMII;
8377902c8dcSWojciech Macek 		adapter->sfp_detection_needed = false;
8387902c8dcSWojciech Macek 		adapter->phy_exist = false;
8397902c8dcSWojciech Macek 		adapter->an_en = false;
8407902c8dcSWojciech Macek 		adapter->lt_en = false;
8417902c8dcSWojciech Macek 		adapter->ref_clk_freq = AL_ETH_REF_FREQ_375_MHZ;
8427902c8dcSWojciech Macek 		adapter->mdio_freq = AL_ETH_DEFAULT_MDIO_FREQ_KHZ;
8437902c8dcSWojciech Macek 	} else {
8447902c8dcSWojciech Macek 		struct al_eth_board_params params;
8457902c8dcSWojciech Macek 		int rc;
8467902c8dcSWojciech Macek 
8477902c8dcSWojciech Macek 		adapter->auto_speed = false;
8487902c8dcSWojciech Macek 
8497902c8dcSWojciech Macek 		rc = al_eth_board_params_get(adapter->mac_base, &params);
8507902c8dcSWojciech Macek 		if (rc != 0) {
8517902c8dcSWojciech Macek 			device_printf(adapter->dev,
8527902c8dcSWojciech Macek 			    "board info not available\n");
8537902c8dcSWojciech Macek 			return (-1);
8547902c8dcSWojciech Macek 		}
8557902c8dcSWojciech Macek 
856463edaf4SJohn Baldwin 		adapter->phy_exist = params.phy_exist == true;
8577902c8dcSWojciech Macek 		adapter->phy_addr = params.phy_mdio_addr;
8587902c8dcSWojciech Macek 		adapter->an_en = params.autoneg_enable;
8597902c8dcSWojciech Macek 		adapter->lt_en = params.kr_lt_enable;
8607902c8dcSWojciech Macek 		adapter->serdes_grp = params.serdes_grp;
8617902c8dcSWojciech Macek 		adapter->serdes_lane = params.serdes_lane;
8627902c8dcSWojciech Macek 		adapter->sfp_detection_needed = params.sfp_plus_module_exist;
8637902c8dcSWojciech Macek 		adapter->i2c_adapter_id = params.i2c_adapter_id;
8647902c8dcSWojciech Macek 		adapter->ref_clk_freq = params.ref_clk_freq;
8657902c8dcSWojciech Macek 		adapter->dont_override_serdes = params.dont_override_serdes;
8667902c8dcSWojciech Macek 		adapter->link_config.active_duplex = !params.half_duplex;
8677902c8dcSWojciech Macek 		adapter->link_config.autoneg = !params.an_disable;
8687902c8dcSWojciech Macek 		adapter->link_config.force_1000_base_x = params.force_1000_base_x;
8697902c8dcSWojciech Macek 		adapter->retimer.exist = params.retimer_exist;
8707902c8dcSWojciech Macek 		adapter->retimer.bus_id = params.retimer_bus_id;
8717902c8dcSWojciech Macek 		adapter->retimer.i2c_addr = params.retimer_i2c_addr;
8727902c8dcSWojciech Macek 		adapter->retimer.channel = params.retimer_channel;
8737902c8dcSWojciech Macek 
8747902c8dcSWojciech Macek 		switch (params.speed) {
8757902c8dcSWojciech Macek 		default:
8767902c8dcSWojciech Macek 			device_printf(adapter->dev,
8777902c8dcSWojciech Macek 			    "%s: invalid speed (%d)\n", __func__, params.speed);
8787902c8dcSWojciech Macek 		case AL_ETH_BOARD_1G_SPEED_1000M:
8797902c8dcSWojciech Macek 			adapter->link_config.active_speed = 1000;
8807902c8dcSWojciech Macek 			break;
8817902c8dcSWojciech Macek 		case AL_ETH_BOARD_1G_SPEED_100M:
8827902c8dcSWojciech Macek 			adapter->link_config.active_speed = 100;
8837902c8dcSWojciech Macek 			break;
8847902c8dcSWojciech Macek 		case AL_ETH_BOARD_1G_SPEED_10M:
8857902c8dcSWojciech Macek 			adapter->link_config.active_speed = 10;
8867902c8dcSWojciech Macek 			break;
8877902c8dcSWojciech Macek 		}
8887902c8dcSWojciech Macek 
8897902c8dcSWojciech Macek 		switch (params.mdio_freq) {
8907902c8dcSWojciech Macek 		default:
8917902c8dcSWojciech Macek 			device_printf(adapter->dev,
8927902c8dcSWojciech Macek 			    "%s: invalid mdio freq (%d)\n", __func__,
8937902c8dcSWojciech Macek 			    params.mdio_freq);
8947902c8dcSWojciech Macek 		case AL_ETH_BOARD_MDIO_FREQ_2_5_MHZ:
8957902c8dcSWojciech Macek 			adapter->mdio_freq = AL_ETH_DEFAULT_MDIO_FREQ_KHZ;
8967902c8dcSWojciech Macek 			break;
8977902c8dcSWojciech Macek 		case AL_ETH_BOARD_MDIO_FREQ_1_MHZ:
8987902c8dcSWojciech Macek 			adapter->mdio_freq = AL_ETH_MDIO_FREQ_1000_KHZ;
8997902c8dcSWojciech Macek 			break;
9007902c8dcSWojciech Macek 		}
9017902c8dcSWojciech Macek 
9027902c8dcSWojciech Macek 		switch (params.media_type) {
9037902c8dcSWojciech Macek 		case AL_ETH_BOARD_MEDIA_TYPE_RGMII:
904463edaf4SJohn Baldwin 			if (params.sfp_plus_module_exist == true)
9057902c8dcSWojciech Macek 				/* Backward compatibility */
9067902c8dcSWojciech Macek 				adapter->mac_mode = AL_ETH_MAC_MODE_SGMII;
9077902c8dcSWojciech Macek 			else
9087902c8dcSWojciech Macek 				adapter->mac_mode = AL_ETH_MAC_MODE_RGMII;
9097902c8dcSWojciech Macek 
9107902c8dcSWojciech Macek 			adapter->use_lm = false;
9117902c8dcSWojciech Macek 			break;
9127902c8dcSWojciech Macek 		case AL_ETH_BOARD_MEDIA_TYPE_SGMII:
9137902c8dcSWojciech Macek 			adapter->mac_mode = AL_ETH_MAC_MODE_SGMII;
9147902c8dcSWojciech Macek 			adapter->use_lm = true;
9157902c8dcSWojciech Macek 			break;
9167902c8dcSWojciech Macek 		case AL_ETH_BOARD_MEDIA_TYPE_10GBASE_SR:
9177902c8dcSWojciech Macek 			adapter->mac_mode = AL_ETH_MAC_MODE_10GbE_Serial;
9187902c8dcSWojciech Macek 			adapter->use_lm = true;
9197902c8dcSWojciech Macek 			break;
9207902c8dcSWojciech Macek 		case AL_ETH_BOARD_MEDIA_TYPE_AUTO_DETECT:
921463edaf4SJohn Baldwin 			adapter->sfp_detection_needed = true;
9227902c8dcSWojciech Macek 			adapter->auto_speed = false;
9237902c8dcSWojciech Macek 			adapter->use_lm = true;
9247902c8dcSWojciech Macek 			break;
9257902c8dcSWojciech Macek 		case AL_ETH_BOARD_MEDIA_TYPE_AUTO_DETECT_AUTO_SPEED:
926463edaf4SJohn Baldwin 			adapter->sfp_detection_needed = true;
9277902c8dcSWojciech Macek 			adapter->auto_speed = true;
9287902c8dcSWojciech Macek 			adapter->mac_mode_set = false;
9297902c8dcSWojciech Macek 			adapter->use_lm = true;
9307902c8dcSWojciech Macek 
9317902c8dcSWojciech Macek 			adapter->mac_mode = AL_ETH_MAC_MODE_10GbE_Serial;
9327902c8dcSWojciech Macek 			break;
9337902c8dcSWojciech Macek 		default:
9347902c8dcSWojciech Macek 			device_printf(adapter->dev,
9357902c8dcSWojciech Macek 			    "%s: unsupported media type %d\n",
9367902c8dcSWojciech Macek 			    __func__, params.media_type);
9377902c8dcSWojciech Macek 			return (-1);
9387902c8dcSWojciech Macek 		}
9397902c8dcSWojciech Macek 
9407902c8dcSWojciech Macek 		device_printf(adapter->dev,
9417902c8dcSWojciech Macek 		    "Board info: phy exist %s. phy addr %d. mdio freq %u Khz. "
9427902c8dcSWojciech Macek 		    "SFP connected %s. media %d\n",
943463edaf4SJohn Baldwin 		    params.phy_exist ? "Yes" : "No",
9447902c8dcSWojciech Macek 		    params.phy_mdio_addr, adapter->mdio_freq,
945463edaf4SJohn Baldwin 		    params.sfp_plus_module_exist ? "Yes" : "No",
9467902c8dcSWojciech Macek 		    params.media_type);
9477902c8dcSWojciech Macek 	}
9487902c8dcSWojciech Macek 
9497902c8dcSWojciech Macek 	al_eth_mac_addr_read(adapter->ec_base, 0, adapter->mac_addr);
9507902c8dcSWojciech Macek 
9517902c8dcSWojciech Macek 	return (0);
9527902c8dcSWojciech Macek }
9537902c8dcSWojciech Macek 
9547902c8dcSWojciech Macek static int
9557902c8dcSWojciech Macek al_eth_function_reset(struct al_eth_adapter *adapter)
9567902c8dcSWojciech Macek {
9577902c8dcSWojciech Macek 	struct al_eth_board_params params;
9587902c8dcSWojciech Macek 	int rc;
9597902c8dcSWojciech Macek 
9607902c8dcSWojciech Macek 	/* save board params so we restore it after reset */
9617902c8dcSWojciech Macek 	al_eth_board_params_get(adapter->mac_base, &params);
9627902c8dcSWojciech Macek 	al_eth_mac_addr_read(adapter->ec_base, 0, adapter->mac_addr);
9637902c8dcSWojciech Macek 	if (adapter->board_type == ALPINE_INTEGRATED)
9647902c8dcSWojciech Macek 		rc = al_eth_flr_rmn(&al_eth_read_pci_config,
9657902c8dcSWojciech Macek 		    &al_eth_write_pci_config,
9667902c8dcSWojciech Macek 		    adapter->dev, adapter->mac_base);
9677902c8dcSWojciech Macek 	else
9687902c8dcSWojciech Macek 		rc = al_eth_flr_rmn(&al_eth_fpga_read_pci_config,
9697902c8dcSWojciech Macek 		    &al_eth_fpga_write_pci_config,
9707902c8dcSWojciech Macek 		    adapter->internal_pcie_base, adapter->mac_base);
9717902c8dcSWojciech Macek 
9727902c8dcSWojciech Macek 	/* restore params */
9737902c8dcSWojciech Macek 	al_eth_board_params_set(adapter->mac_base, &params);
9747902c8dcSWojciech Macek 	al_eth_mac_addr_store(adapter->ec_base, 0, adapter->mac_addr);
9757902c8dcSWojciech Macek 
9767902c8dcSWojciech Macek 	return (rc);
9777902c8dcSWojciech Macek }
9787902c8dcSWojciech Macek 
9797902c8dcSWojciech Macek static void
9807902c8dcSWojciech Macek al_eth_init_rings(struct al_eth_adapter *adapter)
9817902c8dcSWojciech Macek {
9827902c8dcSWojciech Macek 	int i;
9837902c8dcSWojciech Macek 
9847902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_tx_queues; i++) {
9857902c8dcSWojciech Macek 		struct al_eth_ring *ring = &adapter->tx_ring[i];
9867902c8dcSWojciech Macek 
9877902c8dcSWojciech Macek 		ring->ring_id = i;
9887902c8dcSWojciech Macek 		ring->dev = adapter->dev;
9897902c8dcSWojciech Macek 		ring->adapter = adapter;
9907902c8dcSWojciech Macek 		ring->netdev = adapter->netdev;
9917902c8dcSWojciech Macek 		al_udma_q_handle_get(&adapter->hal_adapter.tx_udma, i,
9927902c8dcSWojciech Macek 		    &ring->dma_q);
9937902c8dcSWojciech Macek 		ring->sw_count = adapter->tx_ring_count;
9947902c8dcSWojciech Macek 		ring->hw_count = adapter->tx_descs_count;
9957902c8dcSWojciech Macek 		ring->unmask_reg_offset = al_udma_iofic_unmask_offset_get((struct unit_regs *)adapter->udma_base, AL_UDMA_IOFIC_LEVEL_PRIMARY, AL_INT_GROUP_C);
9967902c8dcSWojciech Macek 		ring->unmask_val = ~(1 << i);
9977902c8dcSWojciech Macek 	}
9987902c8dcSWojciech Macek 
9997902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++) {
10007902c8dcSWojciech Macek 		struct al_eth_ring *ring = &adapter->rx_ring[i];
10017902c8dcSWojciech Macek 
10027902c8dcSWojciech Macek 		ring->ring_id = i;
10037902c8dcSWojciech Macek 		ring->dev = adapter->dev;
10047902c8dcSWojciech Macek 		ring->adapter = adapter;
10057902c8dcSWojciech Macek 		ring->netdev = adapter->netdev;
10067902c8dcSWojciech Macek 		al_udma_q_handle_get(&adapter->hal_adapter.rx_udma, i, &ring->dma_q);
10077902c8dcSWojciech Macek 		ring->sw_count = adapter->rx_ring_count;
10087902c8dcSWojciech Macek 		ring->hw_count = adapter->rx_descs_count;
10097902c8dcSWojciech Macek 		ring->unmask_reg_offset = al_udma_iofic_unmask_offset_get(
10107902c8dcSWojciech Macek 		    (struct unit_regs *)adapter->udma_base,
10117902c8dcSWojciech Macek 		    AL_UDMA_IOFIC_LEVEL_PRIMARY, AL_INT_GROUP_B);
10127902c8dcSWojciech Macek 		ring->unmask_val = ~(1 << i);
10137902c8dcSWojciech Macek 	}
10147902c8dcSWojciech Macek }
10157902c8dcSWojciech Macek 
10167902c8dcSWojciech Macek static void
10177902c8dcSWojciech Macek al_init_locked(void *arg)
10187902c8dcSWojciech Macek {
10197902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = arg;
10207902c8dcSWojciech Macek 	if_t ifp = adapter->netdev;
10217902c8dcSWojciech Macek 	int rc = 0;
10227902c8dcSWojciech Macek 
10237902c8dcSWojciech Macek 	al_eth_down(adapter);
10247902c8dcSWojciech Macek 	rc = al_eth_up(adapter);
10257902c8dcSWojciech Macek 
1026bc14c73bSJustin Hibbits 	if_setdrvflagbits(ifp, 0, IFF_DRV_OACTIVE);
10277902c8dcSWojciech Macek 	if (rc == 0)
1028bc14c73bSJustin Hibbits 		if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
10297902c8dcSWojciech Macek }
10307902c8dcSWojciech Macek 
10317902c8dcSWojciech Macek static void
10327902c8dcSWojciech Macek al_init(void *arg)
10337902c8dcSWojciech Macek {
10347902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = arg;
10357902c8dcSWojciech Macek 
10367902c8dcSWojciech Macek 	al_init_locked(adapter);
10377902c8dcSWojciech Macek }
10387902c8dcSWojciech Macek 
10397902c8dcSWojciech Macek static inline int
10407902c8dcSWojciech Macek al_eth_alloc_rx_buf(struct al_eth_adapter *adapter,
10417902c8dcSWojciech Macek     struct al_eth_ring *rx_ring,
10427902c8dcSWojciech Macek     struct al_eth_rx_buffer *rx_info)
10437902c8dcSWojciech Macek {
10447902c8dcSWojciech Macek 	struct al_buf *al_buf;
10457902c8dcSWojciech Macek 	bus_dma_segment_t segs[2];
10467902c8dcSWojciech Macek 	int error;
10477902c8dcSWojciech Macek 	int nsegs;
10487902c8dcSWojciech Macek 
10497902c8dcSWojciech Macek 	if (rx_info->m != NULL)
10507902c8dcSWojciech Macek 		return (0);
10517902c8dcSWojciech Macek 
10527902c8dcSWojciech Macek 	rx_info->data_size = adapter->rx_mbuf_sz;
10537902c8dcSWojciech Macek 
10547902c8dcSWojciech Macek 	AL_RX_LOCK(adapter);
10557902c8dcSWojciech Macek 
10567902c8dcSWojciech Macek 	/* Get mbuf using UMA allocator */
10577902c8dcSWojciech Macek 	rx_info->m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR,
10587902c8dcSWojciech Macek 	    rx_info->data_size);
10597902c8dcSWojciech Macek 	AL_RX_UNLOCK(adapter);
10607902c8dcSWojciech Macek 
10617902c8dcSWojciech Macek 	if (rx_info->m == NULL)
10627902c8dcSWojciech Macek 		return (ENOMEM);
10637902c8dcSWojciech Macek 
10647902c8dcSWojciech Macek 	rx_info->m->m_pkthdr.len = rx_info->m->m_len = adapter->rx_mbuf_sz;
10657902c8dcSWojciech Macek 
10667902c8dcSWojciech Macek 	/* Map packets for DMA */
10677902c8dcSWojciech Macek 	error = bus_dmamap_load_mbuf_sg(rx_ring->dma_buf_tag, rx_info->dma_map,
10687902c8dcSWojciech Macek 	    rx_info->m, segs, &nsegs, BUS_DMA_NOWAIT);
10697902c8dcSWojciech Macek 	if (__predict_false(error)) {
10707902c8dcSWojciech Macek 		device_printf(rx_ring->dev, "failed to map mbuf, error = %d\n",
10717902c8dcSWojciech Macek 		    error);
10727902c8dcSWojciech Macek 		m_freem(rx_info->m);
10737902c8dcSWojciech Macek 		rx_info->m = NULL;
10747902c8dcSWojciech Macek 		return (EFAULT);
10757902c8dcSWojciech Macek 	}
10767902c8dcSWojciech Macek 
10777902c8dcSWojciech Macek 	al_buf = &rx_info->al_buf;
10787902c8dcSWojciech Macek 	al_buf->addr = segs[0].ds_addr + AL_IP_ALIGNMENT_OFFSET;
10797902c8dcSWojciech Macek 	al_buf->len = rx_info->data_size - AL_IP_ALIGNMENT_OFFSET;
10807902c8dcSWojciech Macek 
10817902c8dcSWojciech Macek 	return (0);
10827902c8dcSWojciech Macek }
10837902c8dcSWojciech Macek 
10847902c8dcSWojciech Macek static int
10857902c8dcSWojciech Macek al_eth_refill_rx_bufs(struct al_eth_adapter *adapter, unsigned int qid,
10867902c8dcSWojciech Macek     unsigned int num)
10877902c8dcSWojciech Macek {
10887902c8dcSWojciech Macek 	struct al_eth_ring *rx_ring = &adapter->rx_ring[qid];
10897902c8dcSWojciech Macek 	uint16_t next_to_use;
10907902c8dcSWojciech Macek 	unsigned int i;
10917902c8dcSWojciech Macek 
10927902c8dcSWojciech Macek 	next_to_use = rx_ring->next_to_use;
10937902c8dcSWojciech Macek 
10947902c8dcSWojciech Macek 	for (i = 0; i < num; i++) {
10957902c8dcSWojciech Macek 		int rc;
10967902c8dcSWojciech Macek 		struct al_eth_rx_buffer *rx_info =
10977902c8dcSWojciech Macek 		    &rx_ring->rx_buffer_info[next_to_use];
10987902c8dcSWojciech Macek 
10997902c8dcSWojciech Macek 		if (__predict_false(al_eth_alloc_rx_buf(adapter,
11007902c8dcSWojciech Macek 		    rx_ring, rx_info) < 0)) {
11017902c8dcSWojciech Macek 			device_printf(adapter->dev,
11027902c8dcSWojciech Macek 			    "failed to alloc buffer for rx queue %d\n", qid);
11037902c8dcSWojciech Macek 			break;
11047902c8dcSWojciech Macek 		}
11057902c8dcSWojciech Macek 
11067902c8dcSWojciech Macek 		rc = al_eth_rx_buffer_add(rx_ring->dma_q,
11077902c8dcSWojciech Macek 		    &rx_info->al_buf, AL_ETH_RX_FLAGS_INT, NULL);
11087902c8dcSWojciech Macek 		if (__predict_false(rc)) {
11097902c8dcSWojciech Macek 			device_printf(adapter->dev,
11107902c8dcSWojciech Macek 			    "failed to add buffer for rx queue %d\n", qid);
11117902c8dcSWojciech Macek 			break;
11127902c8dcSWojciech Macek 		}
11137902c8dcSWojciech Macek 
11147902c8dcSWojciech Macek 		next_to_use = AL_ETH_RX_RING_IDX_NEXT(rx_ring, next_to_use);
11157902c8dcSWojciech Macek 	}
11167902c8dcSWojciech Macek 
11177902c8dcSWojciech Macek 	if (__predict_false(i < num))
11187902c8dcSWojciech Macek 		device_printf(adapter->dev,
11197902c8dcSWojciech Macek 		    "refilled rx queue %d with %d pages only - available %d\n",
11207902c8dcSWojciech Macek 		    qid, i, al_udma_available_get(rx_ring->dma_q));
11217902c8dcSWojciech Macek 
11227902c8dcSWojciech Macek 	if (__predict_true(i))
11237902c8dcSWojciech Macek 		al_eth_rx_buffer_action(rx_ring->dma_q, i);
11247902c8dcSWojciech Macek 
11257902c8dcSWojciech Macek 	rx_ring->next_to_use = next_to_use;
11267902c8dcSWojciech Macek 
11277902c8dcSWojciech Macek 	return (i);
11287902c8dcSWojciech Macek }
11297902c8dcSWojciech Macek 
11307902c8dcSWojciech Macek /*
11317902c8dcSWojciech Macek  * al_eth_refill_all_rx_bufs - allocate all queues Rx buffers
11327902c8dcSWojciech Macek  * @adapter: board private structure
11337902c8dcSWojciech Macek  */
11347902c8dcSWojciech Macek static void
11357902c8dcSWojciech Macek al_eth_refill_all_rx_bufs(struct al_eth_adapter *adapter)
11367902c8dcSWojciech Macek {
11377902c8dcSWojciech Macek 	int i;
11387902c8dcSWojciech Macek 
11397902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++)
11407902c8dcSWojciech Macek 		al_eth_refill_rx_bufs(adapter, i, AL_ETH_DEFAULT_RX_DESCS - 1);
11417902c8dcSWojciech Macek }
11427902c8dcSWojciech Macek 
11437902c8dcSWojciech Macek static void
11447902c8dcSWojciech Macek al_eth_tx_do_cleanup(struct al_eth_ring *tx_ring)
11457902c8dcSWojciech Macek {
11467902c8dcSWojciech Macek 	unsigned int total_done;
11477902c8dcSWojciech Macek 	uint16_t next_to_clean;
11487902c8dcSWojciech Macek 	int qid = tx_ring->ring_id;
11497902c8dcSWojciech Macek 
11507902c8dcSWojciech Macek 	total_done = al_eth_comp_tx_get(tx_ring->dma_q);
11517902c8dcSWojciech Macek 	device_printf_dbg(tx_ring->dev,
11527902c8dcSWojciech Macek 	    "tx_poll: q %d total completed descs %x\n", qid, total_done);
11537902c8dcSWojciech Macek 	next_to_clean = tx_ring->next_to_clean;
11547902c8dcSWojciech Macek 
11557902c8dcSWojciech Macek 	while (total_done != 0) {
11567902c8dcSWojciech Macek 		struct al_eth_tx_buffer *tx_info;
11577902c8dcSWojciech Macek 		struct mbuf *mbuf;
11587902c8dcSWojciech Macek 
11597902c8dcSWojciech Macek 		tx_info = &tx_ring->tx_buffer_info[next_to_clean];
11607902c8dcSWojciech Macek 		/* stop if not all descriptors of the packet are completed */
11617902c8dcSWojciech Macek 		if (tx_info->tx_descs > total_done)
11627902c8dcSWojciech Macek 			break;
11637902c8dcSWojciech Macek 
11647902c8dcSWojciech Macek 		mbuf = tx_info->m;
11657902c8dcSWojciech Macek 
11667902c8dcSWojciech Macek 		tx_info->m = NULL;
11677902c8dcSWojciech Macek 
11687902c8dcSWojciech Macek 		device_printf_dbg(tx_ring->dev,
11697902c8dcSWojciech Macek 		    "tx_poll: q %d mbuf %p completed\n", qid, mbuf);
11707902c8dcSWojciech Macek 
11717902c8dcSWojciech Macek 		/* map is no longer required */
11727902c8dcSWojciech Macek 		bus_dmamap_unload(tx_ring->dma_buf_tag, tx_info->dma_map);
11737902c8dcSWojciech Macek 
11747902c8dcSWojciech Macek 		m_freem(mbuf);
11757902c8dcSWojciech Macek 		total_done -= tx_info->tx_descs;
11767902c8dcSWojciech Macek 		next_to_clean = AL_ETH_TX_RING_IDX_NEXT(tx_ring, next_to_clean);
11777902c8dcSWojciech Macek 	}
11787902c8dcSWojciech Macek 
11797902c8dcSWojciech Macek 	tx_ring->next_to_clean = next_to_clean;
11807902c8dcSWojciech Macek 
11817902c8dcSWojciech Macek 	device_printf_dbg(tx_ring->dev, "tx_poll: q %d done next to clean %x\n",
11827902c8dcSWojciech Macek 	    qid, next_to_clean);
11837902c8dcSWojciech Macek 
11847902c8dcSWojciech Macek 	/*
11857902c8dcSWojciech Macek 	 * need to make the rings circular update visible to
11867902c8dcSWojciech Macek 	 * al_eth_start_xmit() before checking for netif_queue_stopped().
11877902c8dcSWojciech Macek 	 */
11887902c8dcSWojciech Macek 	al_smp_data_memory_barrier();
11897902c8dcSWojciech Macek }
11907902c8dcSWojciech Macek 
11917902c8dcSWojciech Macek static void
11927902c8dcSWojciech Macek al_eth_tx_csum(struct al_eth_ring *tx_ring, struct al_eth_tx_buffer *tx_info,
11937902c8dcSWojciech Macek     struct al_eth_pkt *hal_pkt, struct mbuf *m)
11947902c8dcSWojciech Macek {
11957902c8dcSWojciech Macek 	uint32_t mss = m->m_pkthdr.tso_segsz;
11967902c8dcSWojciech Macek 	struct ether_vlan_header *eh;
11977902c8dcSWojciech Macek 	uint16_t etype;
11980b5cab4eSMarcin Wojtas #ifdef INET
11997902c8dcSWojciech Macek 	struct ip *ip;
12000b5cab4eSMarcin Wojtas #endif
12010b5cab4eSMarcin Wojtas #ifdef INET6
12027902c8dcSWojciech Macek 	struct ip6_hdr *ip6;
12030b5cab4eSMarcin Wojtas #endif
12047902c8dcSWojciech Macek 	struct tcphdr *th = NULL;
12057902c8dcSWojciech Macek 	int	ehdrlen, ip_hlen = 0;
12067902c8dcSWojciech Macek 	uint8_t	ipproto = 0;
12077902c8dcSWojciech Macek 	uint32_t offload = 0;
12087902c8dcSWojciech Macek 
12097902c8dcSWojciech Macek 	if (mss != 0)
12107902c8dcSWojciech Macek 		offload = 1;
12117902c8dcSWojciech Macek 
12127902c8dcSWojciech Macek 	if ((m->m_pkthdr.csum_flags & CSUM_TSO) != 0)
12137902c8dcSWojciech Macek 		offload = 1;
12147902c8dcSWojciech Macek 
12157902c8dcSWojciech Macek 	if ((m->m_pkthdr.csum_flags & CSUM_OFFLOAD) != 0)
12167902c8dcSWojciech Macek 		offload = 1;
12177902c8dcSWojciech Macek 
12187902c8dcSWojciech Macek 	if (offload != 0) {
12197902c8dcSWojciech Macek 		struct al_eth_meta_data *meta = &tx_ring->hal_meta;
12207902c8dcSWojciech Macek 
12217902c8dcSWojciech Macek 		if (mss != 0)
12227902c8dcSWojciech Macek 			hal_pkt->flags |= (AL_ETH_TX_FLAGS_TSO |
12237902c8dcSWojciech Macek 			    AL_ETH_TX_FLAGS_L4_CSUM);
12247902c8dcSWojciech Macek 		else
12257902c8dcSWojciech Macek 			hal_pkt->flags |= (AL_ETH_TX_FLAGS_L4_CSUM |
12267902c8dcSWojciech Macek 			    AL_ETH_TX_FLAGS_L4_PARTIAL_CSUM);
12277902c8dcSWojciech Macek 
12287902c8dcSWojciech Macek 		/*
12297902c8dcSWojciech Macek 		 * Determine where frame payload starts.
12307902c8dcSWojciech Macek 		 * Jump over vlan headers if already present,
12317902c8dcSWojciech Macek 		 * helpful for QinQ too.
12327902c8dcSWojciech Macek 		 */
12337902c8dcSWojciech Macek 		eh = mtod(m, struct ether_vlan_header *);
12347902c8dcSWojciech Macek 		if (eh->evl_encap_proto == htons(ETHERTYPE_VLAN)) {
12357902c8dcSWojciech Macek 			etype = ntohs(eh->evl_proto);
12367902c8dcSWojciech Macek 			ehdrlen = ETHER_HDR_LEN + ETHER_VLAN_ENCAP_LEN;
12377902c8dcSWojciech Macek 		} else {
12387902c8dcSWojciech Macek 			etype = ntohs(eh->evl_encap_proto);
12397902c8dcSWojciech Macek 			ehdrlen = ETHER_HDR_LEN;
12407902c8dcSWojciech Macek 		}
12417902c8dcSWojciech Macek 
12427902c8dcSWojciech Macek 		switch (etype) {
12430b5cab4eSMarcin Wojtas #ifdef INET
12447902c8dcSWojciech Macek 		case ETHERTYPE_IP:
12457902c8dcSWojciech Macek 			ip = (struct ip *)(m->m_data + ehdrlen);
12467902c8dcSWojciech Macek 			ip_hlen = ip->ip_hl << 2;
12477902c8dcSWojciech Macek 			ipproto = ip->ip_p;
12487902c8dcSWojciech Macek 			hal_pkt->l3_proto_idx = AL_ETH_PROTO_ID_IPv4;
12497902c8dcSWojciech Macek 			th = (struct tcphdr *)((caddr_t)ip + ip_hlen);
12507902c8dcSWojciech Macek 			if (mss != 0)
12517902c8dcSWojciech Macek 				hal_pkt->flags |= AL_ETH_TX_FLAGS_IPV4_L3_CSUM;
12527902c8dcSWojciech Macek 			if (ipproto == IPPROTO_TCP)
12537902c8dcSWojciech Macek 				hal_pkt->l4_proto_idx = AL_ETH_PROTO_ID_TCP;
12547902c8dcSWojciech Macek 			else
12557902c8dcSWojciech Macek 				hal_pkt->l4_proto_idx = AL_ETH_PROTO_ID_UDP;
12567902c8dcSWojciech Macek 			break;
12570b5cab4eSMarcin Wojtas #endif /* INET */
12580b5cab4eSMarcin Wojtas #ifdef INET6
12597902c8dcSWojciech Macek 		case ETHERTYPE_IPV6:
12607902c8dcSWojciech Macek 			ip6 = (struct ip6_hdr *)(m->m_data + ehdrlen);
12617902c8dcSWojciech Macek 			hal_pkt->l3_proto_idx = AL_ETH_PROTO_ID_IPv6;
12627902c8dcSWojciech Macek 			ip_hlen = sizeof(struct ip6_hdr);
12637902c8dcSWojciech Macek 			th = (struct tcphdr *)((caddr_t)ip6 + ip_hlen);
12647902c8dcSWojciech Macek 			ipproto = ip6->ip6_nxt;
12657902c8dcSWojciech Macek 			if (ipproto == IPPROTO_TCP)
12667902c8dcSWojciech Macek 				hal_pkt->l4_proto_idx = AL_ETH_PROTO_ID_TCP;
12677902c8dcSWojciech Macek 			else
12687902c8dcSWojciech Macek 				hal_pkt->l4_proto_idx = AL_ETH_PROTO_ID_UDP;
12697902c8dcSWojciech Macek 			break;
12700b5cab4eSMarcin Wojtas #endif /* INET6 */
12717902c8dcSWojciech Macek 		default:
12727902c8dcSWojciech Macek 			break;
12737902c8dcSWojciech Macek 		}
12747902c8dcSWojciech Macek 
12757902c8dcSWojciech Macek 		meta->words_valid = 4;
12767902c8dcSWojciech Macek 		meta->l3_header_len = ip_hlen;
12777902c8dcSWojciech Macek 		meta->l3_header_offset = ehdrlen;
12787902c8dcSWojciech Macek 		if (th != NULL)
12797902c8dcSWojciech Macek 			meta->l4_header_len = th->th_off; /* this param needed only for TSO */
12807902c8dcSWojciech Macek 		meta->mss_idx_sel = 0;			/* check how to select MSS */
12817902c8dcSWojciech Macek 		meta->mss_val = mss;
12827902c8dcSWojciech Macek 		hal_pkt->meta = meta;
12837902c8dcSWojciech Macek 	} else
12847902c8dcSWojciech Macek 		hal_pkt->meta = NULL;
12857902c8dcSWojciech Macek }
12867902c8dcSWojciech Macek 
12877902c8dcSWojciech Macek #define	XMIT_QUEUE_TIMEOUT	100
12887902c8dcSWojciech Macek 
12897902c8dcSWojciech Macek static void
12907902c8dcSWojciech Macek al_eth_xmit_mbuf(struct al_eth_ring *tx_ring, struct mbuf *m)
12917902c8dcSWojciech Macek {
12927902c8dcSWojciech Macek 	struct al_eth_tx_buffer *tx_info;
12937902c8dcSWojciech Macek 	int error;
12947902c8dcSWojciech Macek 	int nsegs, a;
12957902c8dcSWojciech Macek 	uint16_t next_to_use;
12967902c8dcSWojciech Macek 	bus_dma_segment_t segs[AL_ETH_PKT_MAX_BUFS + 1];
12977902c8dcSWojciech Macek 	struct al_eth_pkt *hal_pkt;
12987902c8dcSWojciech Macek 	struct al_buf *al_buf;
1299463edaf4SJohn Baldwin 	bool remap;
13007902c8dcSWojciech Macek 
13017902c8dcSWojciech Macek 	/* Check if queue is ready */
13027902c8dcSWojciech Macek 	if (unlikely(tx_ring->stall) != 0) {
13037902c8dcSWojciech Macek 		for (a = 0; a < XMIT_QUEUE_TIMEOUT; a++) {
13047902c8dcSWojciech Macek 			if (al_udma_available_get(tx_ring->dma_q) >=
13057902c8dcSWojciech Macek 			    (AL_ETH_DEFAULT_TX_HW_DESCS -
13067902c8dcSWojciech Macek 			    AL_ETH_TX_WAKEUP_THRESH)) {
13077902c8dcSWojciech Macek 				tx_ring->stall = 0;
13087902c8dcSWojciech Macek 				break;
13097902c8dcSWojciech Macek 			}
13107902c8dcSWojciech Macek 			pause("stall", 1);
13117902c8dcSWojciech Macek 		}
13127902c8dcSWojciech Macek 		if (a == XMIT_QUEUE_TIMEOUT) {
13137902c8dcSWojciech Macek 			device_printf(tx_ring->dev,
13147902c8dcSWojciech Macek 			    "timeout waiting for queue %d ready!\n",
13157902c8dcSWojciech Macek 			    tx_ring->ring_id);
13167902c8dcSWojciech Macek 			return;
13177902c8dcSWojciech Macek 		} else {
13187902c8dcSWojciech Macek 			device_printf_dbg(tx_ring->dev,
13197902c8dcSWojciech Macek 			    "queue %d is ready!\n", tx_ring->ring_id);
13207902c8dcSWojciech Macek 		}
13217902c8dcSWojciech Macek 	}
13227902c8dcSWojciech Macek 
13237902c8dcSWojciech Macek 	next_to_use = tx_ring->next_to_use;
13247902c8dcSWojciech Macek 	tx_info = &tx_ring->tx_buffer_info[next_to_use];
13257902c8dcSWojciech Macek 	tx_info->m = m;
13267902c8dcSWojciech Macek 	hal_pkt = &tx_info->hal_pkt;
13277902c8dcSWojciech Macek 
13287902c8dcSWojciech Macek 	if (m == NULL) {
13297902c8dcSWojciech Macek 		device_printf(tx_ring->dev, "mbuf is NULL\n");
13307902c8dcSWojciech Macek 		return;
13317902c8dcSWojciech Macek 	}
13327902c8dcSWojciech Macek 
1333463edaf4SJohn Baldwin 	remap = true;
13347902c8dcSWojciech Macek 	/* Map packets for DMA */
13357902c8dcSWojciech Macek retry:
13367902c8dcSWojciech Macek 	error = bus_dmamap_load_mbuf_sg(tx_ring->dma_buf_tag, tx_info->dma_map,
13377902c8dcSWojciech Macek 	    m, segs, &nsegs, BUS_DMA_NOWAIT);
13387902c8dcSWojciech Macek 	if (__predict_false(error)) {
13397902c8dcSWojciech Macek 		struct mbuf *m_new;
13407902c8dcSWojciech Macek 
13417902c8dcSWojciech Macek 		if (error == EFBIG) {
13427902c8dcSWojciech Macek 			/* Try it again? - one try */
1343463edaf4SJohn Baldwin 			if (remap == true) {
1344463edaf4SJohn Baldwin 				remap = false;
13457902c8dcSWojciech Macek 				m_new = m_defrag(m, M_NOWAIT);
13467902c8dcSWojciech Macek 				if (m_new == NULL) {
13477902c8dcSWojciech Macek 					device_printf(tx_ring->dev,
13487902c8dcSWojciech Macek 					    "failed to defrag mbuf\n");
13497902c8dcSWojciech Macek 					goto exit;
13507902c8dcSWojciech Macek 				}
13517902c8dcSWojciech Macek 				m = m_new;
13527902c8dcSWojciech Macek 				goto retry;
13537902c8dcSWojciech Macek 			} else {
13547902c8dcSWojciech Macek 				device_printf(tx_ring->dev,
13557902c8dcSWojciech Macek 				    "failed to map mbuf, error %d\n", error);
13567902c8dcSWojciech Macek 				goto exit;
13577902c8dcSWojciech Macek 			}
13587902c8dcSWojciech Macek 		} else {
13597902c8dcSWojciech Macek 			device_printf(tx_ring->dev,
13607902c8dcSWojciech Macek 			    "failed to map mbuf, error %d\n", error);
13617902c8dcSWojciech Macek 			goto exit;
13627902c8dcSWojciech Macek 		}
13637902c8dcSWojciech Macek 	}
13647902c8dcSWojciech Macek 
13657902c8dcSWojciech Macek 	/* set flags and meta data */
13667902c8dcSWojciech Macek 	hal_pkt->flags = AL_ETH_TX_FLAGS_INT;
13677902c8dcSWojciech Macek 	al_eth_tx_csum(tx_ring, tx_info, hal_pkt, m);
13687902c8dcSWojciech Macek 
13697902c8dcSWojciech Macek 	al_buf = hal_pkt->bufs;
13707902c8dcSWojciech Macek 	for (a = 0; a < nsegs; a++) {
13717902c8dcSWojciech Macek 		al_buf->addr = segs[a].ds_addr;
13727902c8dcSWojciech Macek 		al_buf->len = segs[a].ds_len;
13737902c8dcSWojciech Macek 
13747902c8dcSWojciech Macek 		al_buf++;
13757902c8dcSWojciech Macek 	}
13767902c8dcSWojciech Macek 
13777902c8dcSWojciech Macek 	hal_pkt->num_of_bufs = nsegs;
13787902c8dcSWojciech Macek 
13797902c8dcSWojciech Macek 	/* prepare the packet's descriptors to dma engine */
13807902c8dcSWojciech Macek 	tx_info->tx_descs = al_eth_tx_pkt_prepare(tx_ring->dma_q, hal_pkt);
13817902c8dcSWojciech Macek 
13827902c8dcSWojciech Macek 	if (tx_info->tx_descs == 0)
13837902c8dcSWojciech Macek 		goto exit;
13847902c8dcSWojciech Macek 
13857902c8dcSWojciech Macek 	/*
13867902c8dcSWojciech Macek 	 * stop the queue when no more space available, the packet can have up
13877902c8dcSWojciech Macek 	 * to AL_ETH_PKT_MAX_BUFS + 1 buffers and a meta descriptor
13887902c8dcSWojciech Macek 	 */
13897902c8dcSWojciech Macek 	if (unlikely(al_udma_available_get(tx_ring->dma_q) <
13907902c8dcSWojciech Macek 	    (AL_ETH_PKT_MAX_BUFS + 2))) {
13917902c8dcSWojciech Macek 		tx_ring->stall = 1;
13927902c8dcSWojciech Macek 		device_printf_dbg(tx_ring->dev, "stall, stopping queue %d...\n",
13937902c8dcSWojciech Macek 		    tx_ring->ring_id);
13947902c8dcSWojciech Macek 		al_data_memory_barrier();
13957902c8dcSWojciech Macek 	}
13967902c8dcSWojciech Macek 
13977902c8dcSWojciech Macek 	tx_ring->next_to_use = AL_ETH_TX_RING_IDX_NEXT(tx_ring, next_to_use);
13987902c8dcSWojciech Macek 
13997902c8dcSWojciech Macek 	/* trigger the dma engine */
14007902c8dcSWojciech Macek 	al_eth_tx_dma_action(tx_ring->dma_q, tx_info->tx_descs);
14017902c8dcSWojciech Macek 	return;
14027902c8dcSWojciech Macek 
14037902c8dcSWojciech Macek exit:
14047902c8dcSWojciech Macek 	m_freem(m);
14057902c8dcSWojciech Macek }
14067902c8dcSWojciech Macek 
14077902c8dcSWojciech Macek static void
14087902c8dcSWojciech Macek al_eth_tx_cmpl_work(void *arg, int pending)
14097902c8dcSWojciech Macek {
14107902c8dcSWojciech Macek 	struct al_eth_ring *tx_ring = arg;
14117902c8dcSWojciech Macek 
14127902c8dcSWojciech Macek 	if (napi != 0) {
14137902c8dcSWojciech Macek 		tx_ring->cmpl_is_running = 1;
14147902c8dcSWojciech Macek 		al_data_memory_barrier();
14157902c8dcSWojciech Macek 	}
14167902c8dcSWojciech Macek 
14177902c8dcSWojciech Macek 	al_eth_tx_do_cleanup(tx_ring);
14187902c8dcSWojciech Macek 
14197902c8dcSWojciech Macek 	if (napi != 0) {
14207902c8dcSWojciech Macek 		tx_ring->cmpl_is_running = 0;
14217902c8dcSWojciech Macek 		al_data_memory_barrier();
14227902c8dcSWojciech Macek 	}
14237902c8dcSWojciech Macek 	/* all work done, enable IRQs */
14247902c8dcSWojciech Macek 	al_eth_irq_config(tx_ring->unmask_reg_offset, tx_ring->unmask_val);
14257902c8dcSWojciech Macek }
14267902c8dcSWojciech Macek 
14277902c8dcSWojciech Macek static int
14287902c8dcSWojciech Macek al_eth_tx_cmlp_irq_filter(void *arg)
14297902c8dcSWojciech Macek {
14307902c8dcSWojciech Macek 	struct al_eth_ring *tx_ring = arg;
14317902c8dcSWojciech Macek 
14327902c8dcSWojciech Macek 	/* Interrupt should be auto-masked upon arrival */
14337902c8dcSWojciech Macek 
14347902c8dcSWojciech Macek 	device_printf_dbg(tx_ring->dev, "%s for ring ID = %d\n", __func__,
14357902c8dcSWojciech Macek 	    tx_ring->ring_id);
14367902c8dcSWojciech Macek 
14377902c8dcSWojciech Macek 	/*
14387902c8dcSWojciech Macek 	 * For napi, if work is not running, schedule it. Always schedule
14397902c8dcSWojciech Macek 	 * for casual (non-napi) packet handling.
14407902c8dcSWojciech Macek 	 */
14417902c8dcSWojciech Macek 	if ((napi == 0) || (napi && tx_ring->cmpl_is_running == 0))
14427902c8dcSWojciech Macek 		taskqueue_enqueue(tx_ring->cmpl_tq, &tx_ring->cmpl_task);
14437902c8dcSWojciech Macek 
14447902c8dcSWojciech Macek 	/* Do not run bottom half */
14457902c8dcSWojciech Macek 	return (FILTER_HANDLED);
14467902c8dcSWojciech Macek }
14477902c8dcSWojciech Macek 
14487902c8dcSWojciech Macek static int
14497902c8dcSWojciech Macek al_eth_rx_recv_irq_filter(void *arg)
14507902c8dcSWojciech Macek {
14517902c8dcSWojciech Macek 	struct al_eth_ring *rx_ring = arg;
14527902c8dcSWojciech Macek 
14537902c8dcSWojciech Macek 	/* Interrupt should be auto-masked upon arrival */
14547902c8dcSWojciech Macek 
14557902c8dcSWojciech Macek 	device_printf_dbg(rx_ring->dev, "%s for ring ID = %d\n", __func__,
14567902c8dcSWojciech Macek 	    rx_ring->ring_id);
14577902c8dcSWojciech Macek 
14587902c8dcSWojciech Macek 	/*
14597902c8dcSWojciech Macek 	 * For napi, if work is not running, schedule it. Always schedule
14607902c8dcSWojciech Macek 	 * for casual (non-napi) packet handling.
14617902c8dcSWojciech Macek 	 */
14627902c8dcSWojciech Macek 	if ((napi == 0) || (napi && rx_ring->enqueue_is_running == 0))
14637902c8dcSWojciech Macek 		taskqueue_enqueue(rx_ring->enqueue_tq, &rx_ring->enqueue_task);
14647902c8dcSWojciech Macek 
14657902c8dcSWojciech Macek 	/* Do not run bottom half */
14667902c8dcSWojciech Macek 	return (FILTER_HANDLED);
14677902c8dcSWojciech Macek }
14687902c8dcSWojciech Macek 
14697902c8dcSWojciech Macek /*
14707902c8dcSWojciech Macek  * al_eth_rx_checksum - indicate in mbuf if hw indicated a good cksum
14717902c8dcSWojciech Macek  * @adapter: structure containing adapter specific data
14727902c8dcSWojciech Macek  * @hal_pkt: HAL structure for the packet
14737902c8dcSWojciech Macek  * @mbuf: mbuf currently being received and modified
14747902c8dcSWojciech Macek  */
14757902c8dcSWojciech Macek static inline void
14767902c8dcSWojciech Macek al_eth_rx_checksum(struct al_eth_adapter *adapter,
14777902c8dcSWojciech Macek     struct al_eth_pkt *hal_pkt, struct mbuf *mbuf)
14787902c8dcSWojciech Macek {
14797902c8dcSWojciech Macek 
14807902c8dcSWojciech Macek 	/* if IPv4 and error */
1481f46a05b5SJustin Hibbits 	if (unlikely((if_getcapenable(adapter->netdev) & IFCAP_RXCSUM) &&
14827902c8dcSWojciech Macek 	    (hal_pkt->l3_proto_idx == AL_ETH_PROTO_ID_IPv4) &&
14837902c8dcSWojciech Macek 	    (hal_pkt->flags & AL_ETH_RX_FLAGS_L3_CSUM_ERR))) {
14847902c8dcSWojciech Macek 		device_printf(adapter->dev,"rx ipv4 header checksum error\n");
14857902c8dcSWojciech Macek 		return;
14867902c8dcSWojciech Macek 	}
14877902c8dcSWojciech Macek 
14887902c8dcSWojciech Macek 	/* if IPv6 and error */
1489f46a05b5SJustin Hibbits 	if (unlikely((if_getcapenable(adapter->netdev) & IFCAP_RXCSUM_IPV6) &&
14907902c8dcSWojciech Macek 	    (hal_pkt->l3_proto_idx == AL_ETH_PROTO_ID_IPv6) &&
14917902c8dcSWojciech Macek 	    (hal_pkt->flags & AL_ETH_RX_FLAGS_L3_CSUM_ERR))) {
14927902c8dcSWojciech Macek 		device_printf(adapter->dev,"rx ipv6 header checksum error\n");
14937902c8dcSWojciech Macek 		return;
14947902c8dcSWojciech Macek 	}
14957902c8dcSWojciech Macek 
14967902c8dcSWojciech Macek 	/* if TCP/UDP */
14977902c8dcSWojciech Macek 	if (likely((hal_pkt->l4_proto_idx == AL_ETH_PROTO_ID_TCP) ||
14987902c8dcSWojciech Macek 	   (hal_pkt->l4_proto_idx == AL_ETH_PROTO_ID_UDP))) {
14997902c8dcSWojciech Macek 		if (unlikely(hal_pkt->flags & AL_ETH_RX_FLAGS_L4_CSUM_ERR)) {
15007902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev, "rx L4 checksum error\n");
15017902c8dcSWojciech Macek 
15027902c8dcSWojciech Macek 			/* TCP/UDP checksum error */
15037902c8dcSWojciech Macek 			mbuf->m_pkthdr.csum_flags = 0;
15047902c8dcSWojciech Macek 		} else {
15057902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev, "rx checksum correct\n");
15067902c8dcSWojciech Macek 
15077902c8dcSWojciech Macek 			/* IP Checksum Good */
15087902c8dcSWojciech Macek 			mbuf->m_pkthdr.csum_flags = CSUM_IP_CHECKED;
15097902c8dcSWojciech Macek 			mbuf->m_pkthdr.csum_flags |= CSUM_IP_VALID;
15107902c8dcSWojciech Macek 		}
15117902c8dcSWojciech Macek 	}
15127902c8dcSWojciech Macek }
15137902c8dcSWojciech Macek 
15147902c8dcSWojciech Macek static struct mbuf*
15157902c8dcSWojciech Macek al_eth_rx_mbuf(struct al_eth_adapter *adapter,
15167902c8dcSWojciech Macek     struct al_eth_ring *rx_ring, struct al_eth_pkt *hal_pkt,
15177902c8dcSWojciech Macek     unsigned int descs, uint16_t *next_to_clean)
15187902c8dcSWojciech Macek {
15197902c8dcSWojciech Macek 	struct mbuf *mbuf;
15207902c8dcSWojciech Macek 	struct al_eth_rx_buffer *rx_info =
15217902c8dcSWojciech Macek 	    &rx_ring->rx_buffer_info[*next_to_clean];
15227902c8dcSWojciech Macek 	unsigned int len;
15237902c8dcSWojciech Macek 
15247902c8dcSWojciech Macek 	len = hal_pkt->bufs[0].len;
15257902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "rx_info %p data %p\n", rx_info,
15267902c8dcSWojciech Macek 	   rx_info->m);
15277902c8dcSWojciech Macek 
15287902c8dcSWojciech Macek 	if (rx_info->m == NULL) {
15297902c8dcSWojciech Macek 		*next_to_clean = AL_ETH_RX_RING_IDX_NEXT(rx_ring,
15307902c8dcSWojciech Macek 		    *next_to_clean);
15317902c8dcSWojciech Macek 		return (NULL);
15327902c8dcSWojciech Macek 	}
15337902c8dcSWojciech Macek 
15347902c8dcSWojciech Macek 	mbuf = rx_info->m;
15357902c8dcSWojciech Macek 	mbuf->m_pkthdr.len = len;
15367902c8dcSWojciech Macek 	mbuf->m_len = len;
15377902c8dcSWojciech Macek 	mbuf->m_pkthdr.rcvif = rx_ring->netdev;
15387902c8dcSWojciech Macek 	mbuf->m_flags |= M_PKTHDR;
15397902c8dcSWojciech Macek 
15407902c8dcSWojciech Macek 	if (len <= adapter->small_copy_len) {
15417902c8dcSWojciech Macek 		struct mbuf *smbuf;
15427902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "rx small packet. len %d\n", len);
15437902c8dcSWojciech Macek 
15447902c8dcSWojciech Macek 		AL_RX_LOCK(adapter);
15457902c8dcSWojciech Macek 		smbuf = m_gethdr(M_NOWAIT, MT_DATA);
15467902c8dcSWojciech Macek 		AL_RX_UNLOCK(adapter);
15477902c8dcSWojciech Macek 		if (__predict_false(smbuf == NULL)) {
15487902c8dcSWojciech Macek 			device_printf(adapter->dev, "smbuf is NULL\n");
15497902c8dcSWojciech Macek 			return (NULL);
15507902c8dcSWojciech Macek 		}
15517902c8dcSWojciech Macek 
15527902c8dcSWojciech Macek 		smbuf->m_data = smbuf->m_data + AL_IP_ALIGNMENT_OFFSET;
15537902c8dcSWojciech Macek 		memcpy(smbuf->m_data, mbuf->m_data + AL_IP_ALIGNMENT_OFFSET, len);
15547902c8dcSWojciech Macek 
15557902c8dcSWojciech Macek 		smbuf->m_len = len;
15567902c8dcSWojciech Macek 		smbuf->m_pkthdr.rcvif = rx_ring->netdev;
15577902c8dcSWojciech Macek 
15587902c8dcSWojciech Macek 		/* first desc of a non-ps chain */
15597902c8dcSWojciech Macek 		smbuf->m_flags |= M_PKTHDR;
15607902c8dcSWojciech Macek 		smbuf->m_pkthdr.len = smbuf->m_len;
15617902c8dcSWojciech Macek 
15627902c8dcSWojciech Macek 		*next_to_clean = AL_ETH_RX_RING_IDX_NEXT(rx_ring,
15637902c8dcSWojciech Macek 		    *next_to_clean);
15647902c8dcSWojciech Macek 
15657902c8dcSWojciech Macek 		return (smbuf);
15667902c8dcSWojciech Macek 	}
15677902c8dcSWojciech Macek 	mbuf->m_data = mbuf->m_data + AL_IP_ALIGNMENT_OFFSET;
15687902c8dcSWojciech Macek 
15697902c8dcSWojciech Macek 	/* Unmap the buffer */
15707902c8dcSWojciech Macek 	bus_dmamap_unload(rx_ring->dma_buf_tag, rx_info->dma_map);
15717902c8dcSWojciech Macek 
15727902c8dcSWojciech Macek 	rx_info->m = NULL;
15737902c8dcSWojciech Macek 	*next_to_clean = AL_ETH_RX_RING_IDX_NEXT(rx_ring, *next_to_clean);
15747902c8dcSWojciech Macek 
15757902c8dcSWojciech Macek 	return (mbuf);
15767902c8dcSWojciech Macek }
15777902c8dcSWojciech Macek 
15787902c8dcSWojciech Macek static void
15797902c8dcSWojciech Macek al_eth_rx_recv_work(void *arg, int pending)
15807902c8dcSWojciech Macek {
15817902c8dcSWojciech Macek 	struct al_eth_ring *rx_ring = arg;
15827902c8dcSWojciech Macek 	struct mbuf *mbuf;
15837902c8dcSWojciech Macek 	unsigned int qid = rx_ring->ring_id;
15847902c8dcSWojciech Macek 	struct al_eth_pkt *hal_pkt = &rx_ring->hal_pkt;
15857902c8dcSWojciech Macek 	uint16_t next_to_clean = rx_ring->next_to_clean;
15867902c8dcSWojciech Macek 	uint32_t refill_required;
15877902c8dcSWojciech Macek 	uint32_t refill_actual;
15887902c8dcSWojciech Macek 	uint32_t do_if_input;
15897902c8dcSWojciech Macek 
15907902c8dcSWojciech Macek 	if (napi != 0) {
15917902c8dcSWojciech Macek 		rx_ring->enqueue_is_running = 1;
15927902c8dcSWojciech Macek 		al_data_memory_barrier();
15937902c8dcSWojciech Macek 	}
15947902c8dcSWojciech Macek 
15957902c8dcSWojciech Macek 	do {
15967902c8dcSWojciech Macek 		unsigned int descs;
15977902c8dcSWojciech Macek 
15987902c8dcSWojciech Macek 		descs = al_eth_pkt_rx(rx_ring->dma_q, hal_pkt);
15997902c8dcSWojciech Macek 		if (unlikely(descs == 0))
16007902c8dcSWojciech Macek 			break;
16017902c8dcSWojciech Macek 
16027902c8dcSWojciech Macek 		device_printf_dbg(rx_ring->dev, "rx_poll: q %d got packet "
16037902c8dcSWojciech Macek 		    "from hal. descs %d\n", qid, descs);
16047902c8dcSWojciech Macek 		device_printf_dbg(rx_ring->dev, "rx_poll: q %d flags %x. "
16057902c8dcSWojciech Macek 		    "l3 proto %d l4 proto %d\n", qid, hal_pkt->flags,
16067902c8dcSWojciech Macek 		    hal_pkt->l3_proto_idx, hal_pkt->l4_proto_idx);
16077902c8dcSWojciech Macek 
16087902c8dcSWojciech Macek 		/* ignore if detected dma or eth controller errors */
16097902c8dcSWojciech Macek 		if ((hal_pkt->flags & (AL_ETH_RX_ERROR |
16107902c8dcSWojciech Macek 		    AL_UDMA_CDESC_ERROR)) != 0) {
16117902c8dcSWojciech Macek 			device_printf(rx_ring->dev, "receive packet with error. "
16127902c8dcSWojciech Macek 			    "flags = 0x%x\n", hal_pkt->flags);
16137902c8dcSWojciech Macek 			next_to_clean = AL_ETH_RX_RING_IDX_ADD(rx_ring,
16147902c8dcSWojciech Macek 			    next_to_clean, descs);
16157902c8dcSWojciech Macek 			continue;
16167902c8dcSWojciech Macek 		}
16177902c8dcSWojciech Macek 
16187902c8dcSWojciech Macek 		/* allocate mbuf and fill it */
16197902c8dcSWojciech Macek 		mbuf = al_eth_rx_mbuf(rx_ring->adapter, rx_ring, hal_pkt, descs,
16207902c8dcSWojciech Macek 		    &next_to_clean);
16217902c8dcSWojciech Macek 
16227902c8dcSWojciech Macek 		/* exit if we failed to retrieve a buffer */
16237902c8dcSWojciech Macek 		if (unlikely(mbuf == NULL)) {
16247902c8dcSWojciech Macek 			next_to_clean = AL_ETH_RX_RING_IDX_ADD(rx_ring,
16257902c8dcSWojciech Macek 			    next_to_clean, descs);
16267902c8dcSWojciech Macek 			break;
16277902c8dcSWojciech Macek 		}
16287902c8dcSWojciech Macek 
1629f46a05b5SJustin Hibbits 		if (__predict_true(if_getcapenable(rx_ring->netdev) & IFCAP_RXCSUM ||
1630f46a05b5SJustin Hibbits 		    if_getcapenable(rx_ring->netdev) & IFCAP_RXCSUM_IPV6)) {
16317902c8dcSWojciech Macek 			al_eth_rx_checksum(rx_ring->adapter, hal_pkt, mbuf);
16327902c8dcSWojciech Macek 		}
16337902c8dcSWojciech Macek 
16347902c8dcSWojciech Macek 		mbuf->m_pkthdr.flowid = qid;
16357902c8dcSWojciech Macek 		M_HASHTYPE_SET(mbuf, M_HASHTYPE_OPAQUE);
16367902c8dcSWojciech Macek 
16377902c8dcSWojciech Macek 		/*
16387902c8dcSWojciech Macek 		 * LRO is only for IP/TCP packets and TCP checksum of the packet
16397902c8dcSWojciech Macek 		 * should be computed by hardware.
16407902c8dcSWojciech Macek 		 */
16417902c8dcSWojciech Macek 		do_if_input = 1;
16427902c8dcSWojciech Macek 		if ((rx_ring->lro_enabled != 0) &&
16437902c8dcSWojciech Macek 		    ((mbuf->m_pkthdr.csum_flags & CSUM_IP_VALID) != 0) &&
16447902c8dcSWojciech Macek 		    hal_pkt->l4_proto_idx == AL_ETH_PROTO_ID_TCP) {
16457902c8dcSWojciech Macek 			/*
16467902c8dcSWojciech Macek 			 * Send to the stack if:
16477902c8dcSWojciech Macek 			 *  - LRO not enabled, or
16487902c8dcSWojciech Macek 			 *  - no LRO resources, or
16497902c8dcSWojciech Macek 			 *  - lro enqueue fails
16507902c8dcSWojciech Macek 			 */
16517902c8dcSWojciech Macek 			if (rx_ring->lro.lro_cnt != 0) {
16527902c8dcSWojciech Macek 				if (tcp_lro_rx(&rx_ring->lro, mbuf, 0) == 0)
16537902c8dcSWojciech Macek 					do_if_input = 0;
16547902c8dcSWojciech Macek 			}
16557902c8dcSWojciech Macek 		}
16567902c8dcSWojciech Macek 
16577902c8dcSWojciech Macek 		if (do_if_input)
1658f46a05b5SJustin Hibbits 			if_input(rx_ring->netdev, mbuf);
16597902c8dcSWojciech Macek 
16607902c8dcSWojciech Macek 	} while (1);
16617902c8dcSWojciech Macek 
16627902c8dcSWojciech Macek 	rx_ring->next_to_clean = next_to_clean;
16637902c8dcSWojciech Macek 
16647902c8dcSWojciech Macek 	refill_required = al_udma_available_get(rx_ring->dma_q);
16657902c8dcSWojciech Macek 	refill_actual = al_eth_refill_rx_bufs(rx_ring->adapter, qid,
16667902c8dcSWojciech Macek 	    refill_required);
16677902c8dcSWojciech Macek 
16687902c8dcSWojciech Macek 	if (unlikely(refill_actual < refill_required)) {
16697902c8dcSWojciech Macek 		device_printf_dbg(rx_ring->dev,
16707902c8dcSWojciech Macek 		    "%s: not filling rx queue %d\n", __func__, qid);
16717902c8dcSWojciech Macek 	}
16727902c8dcSWojciech Macek 
1673*0b45d365SMichael Tuexen 	tcp_lro_flush_all(&rx_ring->lro);
16747902c8dcSWojciech Macek 
16757902c8dcSWojciech Macek 	if (napi != 0) {
16767902c8dcSWojciech Macek 		rx_ring->enqueue_is_running = 0;
16777902c8dcSWojciech Macek 		al_data_memory_barrier();
16787902c8dcSWojciech Macek 	}
16797902c8dcSWojciech Macek 	/* unmask irq */
16807902c8dcSWojciech Macek 	al_eth_irq_config(rx_ring->unmask_reg_offset, rx_ring->unmask_val);
16817902c8dcSWojciech Macek }
16827902c8dcSWojciech Macek 
16837902c8dcSWojciech Macek static void
16847902c8dcSWojciech Macek al_eth_start_xmit(void *arg, int pending)
16857902c8dcSWojciech Macek {
16867902c8dcSWojciech Macek 	struct al_eth_ring *tx_ring = arg;
16877902c8dcSWojciech Macek 	struct mbuf *mbuf;
16887902c8dcSWojciech Macek 
16897902c8dcSWojciech Macek 	if (napi != 0) {
16907902c8dcSWojciech Macek 		tx_ring->enqueue_is_running = 1;
16917902c8dcSWojciech Macek 		al_data_memory_barrier();
16927902c8dcSWojciech Macek 	}
16937902c8dcSWojciech Macek 
16947902c8dcSWojciech Macek 	while (1) {
16957902c8dcSWojciech Macek 		mtx_lock(&tx_ring->br_mtx);
16967902c8dcSWojciech Macek 		mbuf = drbr_dequeue(NULL, tx_ring->br);
16977902c8dcSWojciech Macek 		mtx_unlock(&tx_ring->br_mtx);
16987902c8dcSWojciech Macek 
16997902c8dcSWojciech Macek 		if (mbuf == NULL)
17007902c8dcSWojciech Macek 			break;
17017902c8dcSWojciech Macek 
17027902c8dcSWojciech Macek 		al_eth_xmit_mbuf(tx_ring, mbuf);
17037902c8dcSWojciech Macek 	}
17047902c8dcSWojciech Macek 
17057902c8dcSWojciech Macek 	if (napi != 0) {
17067902c8dcSWojciech Macek 		tx_ring->enqueue_is_running = 0;
17077902c8dcSWojciech Macek 		al_data_memory_barrier();
17087902c8dcSWojciech Macek 		while (1) {
17097902c8dcSWojciech Macek 			mtx_lock(&tx_ring->br_mtx);
17107902c8dcSWojciech Macek 			mbuf = drbr_dequeue(NULL, tx_ring->br);
17117902c8dcSWojciech Macek 			mtx_unlock(&tx_ring->br_mtx);
17127902c8dcSWojciech Macek 			if (mbuf == NULL)
17137902c8dcSWojciech Macek 				break;
17147902c8dcSWojciech Macek 			al_eth_xmit_mbuf(tx_ring, mbuf);
17157902c8dcSWojciech Macek 		}
17167902c8dcSWojciech Macek 	}
17177902c8dcSWojciech Macek }
17187902c8dcSWojciech Macek 
17197902c8dcSWojciech Macek static int
1720bc14c73bSJustin Hibbits al_mq_start(if_t ifp, struct mbuf *m)
17217902c8dcSWojciech Macek {
1722bc14c73bSJustin Hibbits 	struct al_eth_adapter *adapter = if_getsoftc(ifp);
17237902c8dcSWojciech Macek 	struct al_eth_ring *tx_ring;
17247902c8dcSWojciech Macek 	int i;
17257902c8dcSWojciech Macek 	int ret;
17267902c8dcSWojciech Macek 
17277902c8dcSWojciech Macek 	/* Which queue to use */
17287902c8dcSWojciech Macek 	if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE)
17297902c8dcSWojciech Macek 		i = m->m_pkthdr.flowid % adapter->num_tx_queues;
17307902c8dcSWojciech Macek 	else
17317902c8dcSWojciech Macek 		i = curcpu % adapter->num_tx_queues;
17327902c8dcSWojciech Macek 
1733bc14c73bSJustin Hibbits 	if ((if_getdrvflags(ifp) & (IFF_DRV_RUNNING|IFF_DRV_OACTIVE)) !=
17347902c8dcSWojciech Macek 	    IFF_DRV_RUNNING) {
17357902c8dcSWojciech Macek 		return (EFAULT);
17367902c8dcSWojciech Macek 	}
17377902c8dcSWojciech Macek 
17387902c8dcSWojciech Macek 	tx_ring = &adapter->tx_ring[i];
17397902c8dcSWojciech Macek 
17407902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "dgb start() - assuming link is active, "
17417902c8dcSWojciech Macek 	    "sending packet to queue %d\n", i);
17427902c8dcSWojciech Macek 
17437902c8dcSWojciech Macek 	ret = drbr_enqueue(ifp, tx_ring->br, m);
17447902c8dcSWojciech Macek 
17457902c8dcSWojciech Macek 	/*
17467902c8dcSWojciech Macek 	 * For napi, if work is not running, schedule it. Always schedule
17477902c8dcSWojciech Macek 	 * for casual (non-napi) packet handling.
17487902c8dcSWojciech Macek 	 */
17497902c8dcSWojciech Macek 	if ((napi == 0) || ((napi != 0) && (tx_ring->enqueue_is_running == 0)))
17507902c8dcSWojciech Macek 		taskqueue_enqueue(tx_ring->enqueue_tq, &tx_ring->enqueue_task);
17517902c8dcSWojciech Macek 
17527902c8dcSWojciech Macek 	return (ret);
17537902c8dcSWojciech Macek }
17547902c8dcSWojciech Macek 
17557902c8dcSWojciech Macek static void
1756bc14c73bSJustin Hibbits al_qflush(if_t ifp)
17577902c8dcSWojciech Macek {
17587902c8dcSWojciech Macek 
17597902c8dcSWojciech Macek 	/* unused */
17607902c8dcSWojciech Macek }
17617902c8dcSWojciech Macek 
17627902c8dcSWojciech Macek static inline void
17637902c8dcSWojciech Macek al_eth_flow_ctrl_init(struct al_eth_adapter *adapter)
17647902c8dcSWojciech Macek {
17657902c8dcSWojciech Macek 	uint8_t default_flow_ctrl;
17667902c8dcSWojciech Macek 
17677902c8dcSWojciech Macek 	default_flow_ctrl = AL_ETH_FLOW_CTRL_TX_PAUSE;
17687902c8dcSWojciech Macek 	default_flow_ctrl |= AL_ETH_FLOW_CTRL_RX_PAUSE;
17697902c8dcSWojciech Macek 
17707902c8dcSWojciech Macek 	adapter->link_config.flow_ctrl_supported = default_flow_ctrl;
17717902c8dcSWojciech Macek }
17727902c8dcSWojciech Macek 
17737902c8dcSWojciech Macek static int
17747902c8dcSWojciech Macek al_eth_flow_ctrl_config(struct al_eth_adapter *adapter)
17757902c8dcSWojciech Macek {
17767902c8dcSWojciech Macek 	struct al_eth_flow_control_params *flow_ctrl_params;
17777902c8dcSWojciech Macek 	uint8_t active = adapter->link_config.flow_ctrl_active;
17787902c8dcSWojciech Macek 	int i;
17797902c8dcSWojciech Macek 
17807902c8dcSWojciech Macek 	flow_ctrl_params = &adapter->flow_ctrl_params;
17817902c8dcSWojciech Macek 
17827902c8dcSWojciech Macek 	flow_ctrl_params->type = AL_ETH_FLOW_CONTROL_TYPE_LINK_PAUSE;
17837902c8dcSWojciech Macek 	flow_ctrl_params->obay_enable =
17847902c8dcSWojciech Macek 	    ((active & AL_ETH_FLOW_CTRL_RX_PAUSE) != 0);
17857902c8dcSWojciech Macek 	flow_ctrl_params->gen_enable =
17867902c8dcSWojciech Macek 	    ((active & AL_ETH_FLOW_CTRL_TX_PAUSE) != 0);
17877902c8dcSWojciech Macek 
17887902c8dcSWojciech Macek 	flow_ctrl_params->rx_fifo_th_high = AL_ETH_FLOW_CTRL_RX_FIFO_TH_HIGH;
17897902c8dcSWojciech Macek 	flow_ctrl_params->rx_fifo_th_low = AL_ETH_FLOW_CTRL_RX_FIFO_TH_LOW;
17907902c8dcSWojciech Macek 	flow_ctrl_params->quanta = AL_ETH_FLOW_CTRL_QUANTA;
17917902c8dcSWojciech Macek 	flow_ctrl_params->quanta_th = AL_ETH_FLOW_CTRL_QUANTA_TH;
17927902c8dcSWojciech Macek 
17937902c8dcSWojciech Macek 	/* map priority to queue index, queue id = priority/2 */
17947902c8dcSWojciech Macek 	for (i = 0; i < AL_ETH_FWD_PRIO_TABLE_NUM; i++)
17957902c8dcSWojciech Macek 		flow_ctrl_params->prio_q_map[0][i] =  1 << (i >> 1);
17967902c8dcSWojciech Macek 
17977902c8dcSWojciech Macek 	al_eth_flow_control_config(&adapter->hal_adapter, flow_ctrl_params);
17987902c8dcSWojciech Macek 
17997902c8dcSWojciech Macek 	return (0);
18007902c8dcSWojciech Macek }
18017902c8dcSWojciech Macek 
18027902c8dcSWojciech Macek static void
18037902c8dcSWojciech Macek al_eth_flow_ctrl_enable(struct al_eth_adapter *adapter)
18047902c8dcSWojciech Macek {
18057902c8dcSWojciech Macek 
18067902c8dcSWojciech Macek 	/*
18077902c8dcSWojciech Macek 	 * change the active configuration to the default / force by ethtool
18087902c8dcSWojciech Macek 	 * and call to configure
18097902c8dcSWojciech Macek 	 */
18107902c8dcSWojciech Macek 	adapter->link_config.flow_ctrl_active =
18117902c8dcSWojciech Macek 	    adapter->link_config.flow_ctrl_supported;
18127902c8dcSWojciech Macek 
18137902c8dcSWojciech Macek 	al_eth_flow_ctrl_config(adapter);
18147902c8dcSWojciech Macek }
18157902c8dcSWojciech Macek 
18167902c8dcSWojciech Macek static void
18177902c8dcSWojciech Macek al_eth_flow_ctrl_disable(struct al_eth_adapter *adapter)
18187902c8dcSWojciech Macek {
18197902c8dcSWojciech Macek 
18207902c8dcSWojciech Macek 	adapter->link_config.flow_ctrl_active = 0;
18217902c8dcSWojciech Macek 	al_eth_flow_ctrl_config(adapter);
18227902c8dcSWojciech Macek }
18237902c8dcSWojciech Macek 
18247902c8dcSWojciech Macek static int
18257902c8dcSWojciech Macek al_eth_hw_init(struct al_eth_adapter *adapter)
18267902c8dcSWojciech Macek {
18277902c8dcSWojciech Macek 	int rc;
18287902c8dcSWojciech Macek 
18297902c8dcSWojciech Macek 	rc = al_eth_hw_init_adapter(adapter);
18307902c8dcSWojciech Macek 	if (rc != 0)
18317902c8dcSWojciech Macek 		return (rc);
18327902c8dcSWojciech Macek 
18337902c8dcSWojciech Macek 	rc = al_eth_mac_config(&adapter->hal_adapter, adapter->mac_mode);
18347902c8dcSWojciech Macek 	if (rc < 0) {
18357902c8dcSWojciech Macek 		device_printf(adapter->dev, "%s failed to configure mac!\n",
18367902c8dcSWojciech Macek 		    __func__);
18377902c8dcSWojciech Macek 		return (rc);
18387902c8dcSWojciech Macek 	}
18397902c8dcSWojciech Macek 
18407902c8dcSWojciech Macek 	if ((adapter->mac_mode == AL_ETH_MAC_MODE_SGMII) ||
18417902c8dcSWojciech Macek 	    (adapter->mac_mode == AL_ETH_MAC_MODE_RGMII &&
1842463edaf4SJohn Baldwin 	     adapter->phy_exist == false)) {
18437902c8dcSWojciech Macek 		rc = al_eth_mac_link_config(&adapter->hal_adapter,
18447902c8dcSWojciech Macek 		    adapter->link_config.force_1000_base_x,
18457902c8dcSWojciech Macek 		    adapter->link_config.autoneg,
18467902c8dcSWojciech Macek 		    adapter->link_config.active_speed,
18477902c8dcSWojciech Macek 		    adapter->link_config.active_duplex);
18487902c8dcSWojciech Macek 		if (rc != 0) {
18497902c8dcSWojciech Macek 			device_printf(adapter->dev,
18507902c8dcSWojciech Macek 			    "%s failed to configure link parameters!\n",
18517902c8dcSWojciech Macek 			    __func__);
18527902c8dcSWojciech Macek 			return (rc);
18537902c8dcSWojciech Macek 		}
18547902c8dcSWojciech Macek 	}
18557902c8dcSWojciech Macek 
18567902c8dcSWojciech Macek 	rc = al_eth_mdio_config(&adapter->hal_adapter,
1857463edaf4SJohn Baldwin 	    AL_ETH_MDIO_TYPE_CLAUSE_22, AL_TRUE /* shared_mdio_if */,
18587902c8dcSWojciech Macek 	    adapter->ref_clk_freq, adapter->mdio_freq);
18597902c8dcSWojciech Macek 	if (rc != 0) {
18607902c8dcSWojciech Macek 		device_printf(adapter->dev, "%s failed at mdio config!\n",
18617902c8dcSWojciech Macek 		    __func__);
18627902c8dcSWojciech Macek 		return (rc);
18637902c8dcSWojciech Macek 	}
18647902c8dcSWojciech Macek 
18657902c8dcSWojciech Macek 	al_eth_flow_ctrl_init(adapter);
18667902c8dcSWojciech Macek 
18677902c8dcSWojciech Macek 	return (rc);
18687902c8dcSWojciech Macek }
18697902c8dcSWojciech Macek 
18707902c8dcSWojciech Macek static int
18717902c8dcSWojciech Macek al_eth_hw_stop(struct al_eth_adapter *adapter)
18727902c8dcSWojciech Macek {
18737902c8dcSWojciech Macek 
18747902c8dcSWojciech Macek 	al_eth_mac_stop(&adapter->hal_adapter);
18757902c8dcSWojciech Macek 
18767902c8dcSWojciech Macek 	/*
18777902c8dcSWojciech Macek 	 * wait till pending rx packets written and UDMA becomes idle,
187845b143d2SGordon Bergling 	 * the MAC has ~10KB fifo, 10us should be enough time for the
18797902c8dcSWojciech Macek 	 * UDMA to write to the memory
18807902c8dcSWojciech Macek 	 */
18817902c8dcSWojciech Macek 	DELAY(10);
18827902c8dcSWojciech Macek 
18837902c8dcSWojciech Macek 	al_eth_adapter_stop(&adapter->hal_adapter);
18847902c8dcSWojciech Macek 
18857902c8dcSWojciech Macek 	adapter->flags |= AL_ETH_FLAG_RESET_REQUESTED;
18867902c8dcSWojciech Macek 
18877902c8dcSWojciech Macek 	/* disable flow ctrl to avoid pause packets*/
18887902c8dcSWojciech Macek 	al_eth_flow_ctrl_disable(adapter);
18897902c8dcSWojciech Macek 
18907902c8dcSWojciech Macek 	return (0);
18917902c8dcSWojciech Macek }
18927902c8dcSWojciech Macek 
18937902c8dcSWojciech Macek /*
18947902c8dcSWojciech Macek  * al_eth_intr_intx_all - Legacy Interrupt Handler for all interrupts
18957902c8dcSWojciech Macek  * @irq: interrupt number
18967902c8dcSWojciech Macek  * @data: pointer to a network interface device structure
18977902c8dcSWojciech Macek  */
18987902c8dcSWojciech Macek static int
18997902c8dcSWojciech Macek al_eth_intr_intx_all(void *data)
19007902c8dcSWojciech Macek {
19017902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = data;
19027902c8dcSWojciech Macek 
19037902c8dcSWojciech Macek 	struct unit_regs __iomem *regs_base =
19047902c8dcSWojciech Macek 	    (struct unit_regs __iomem *)adapter->udma_base;
19057902c8dcSWojciech Macek 	uint32_t reg;
19067902c8dcSWojciech Macek 
19077902c8dcSWojciech Macek 	reg = al_udma_iofic_read_cause(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
19087902c8dcSWojciech Macek 	    AL_INT_GROUP_A);
19097902c8dcSWojciech Macek 	if (likely(reg))
19107902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "%s group A cause %x\n",
19117902c8dcSWojciech Macek 		    __func__, reg);
19127902c8dcSWojciech Macek 
19137902c8dcSWojciech Macek 	if (unlikely(reg & AL_INT_GROUP_A_GROUP_D_SUM)) {
19147902c8dcSWojciech Macek 		struct al_iofic_grp_ctrl __iomem *sec_ints_base;
19157902c8dcSWojciech Macek 		uint32_t cause_d =  al_udma_iofic_read_cause(regs_base,
19167902c8dcSWojciech Macek 		    AL_UDMA_IOFIC_LEVEL_PRIMARY, AL_INT_GROUP_D);
19177902c8dcSWojciech Macek 
19187902c8dcSWojciech Macek 		sec_ints_base =
19197902c8dcSWojciech Macek 		    &regs_base->gen.interrupt_regs.secondary_iofic_ctrl[0];
19207902c8dcSWojciech Macek 		if (cause_d != 0) {
19217902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev,
19227902c8dcSWojciech Macek 			    "got interrupt from group D. cause %x\n", cause_d);
19237902c8dcSWojciech Macek 
19247902c8dcSWojciech Macek 			cause_d = al_iofic_read_cause(sec_ints_base,
19257902c8dcSWojciech Macek 			    AL_INT_GROUP_A);
19267902c8dcSWojciech Macek 			device_printf(adapter->dev,
19277902c8dcSWojciech Macek 			    "secondary A cause %x\n", cause_d);
19287902c8dcSWojciech Macek 
19297902c8dcSWojciech Macek 			cause_d = al_iofic_read_cause(sec_ints_base,
19307902c8dcSWojciech Macek 			    AL_INT_GROUP_B);
19317902c8dcSWojciech Macek 
19327902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev,
19337902c8dcSWojciech Macek 			    "secondary B cause %x\n", cause_d);
19347902c8dcSWojciech Macek 		}
19357902c8dcSWojciech Macek 	}
19367902c8dcSWojciech Macek 	if ((reg & AL_INT_GROUP_A_GROUP_B_SUM) != 0 ) {
19377902c8dcSWojciech Macek 		uint32_t cause_b = al_udma_iofic_read_cause(regs_base,
19387902c8dcSWojciech Macek 		    AL_UDMA_IOFIC_LEVEL_PRIMARY, AL_INT_GROUP_B);
19397902c8dcSWojciech Macek 		int qid;
19407902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "secondary B cause %x\n",
19417902c8dcSWojciech Macek 		    cause_b);
19427902c8dcSWojciech Macek 		for (qid = 0; qid < adapter->num_rx_queues; qid++) {
19437902c8dcSWojciech Macek 			if (cause_b & (1 << qid)) {
19447902c8dcSWojciech Macek 				/* mask */
19457902c8dcSWojciech Macek 				al_udma_iofic_mask(
19467902c8dcSWojciech Macek 				    (struct unit_regs __iomem *)adapter->udma_base,
19477902c8dcSWojciech Macek 				    AL_UDMA_IOFIC_LEVEL_PRIMARY,
19487902c8dcSWojciech Macek 				    AL_INT_GROUP_B, 1 << qid);
19497902c8dcSWojciech Macek 			}
19507902c8dcSWojciech Macek 		}
19517902c8dcSWojciech Macek 	}
19527902c8dcSWojciech Macek 	if ((reg & AL_INT_GROUP_A_GROUP_C_SUM) != 0) {
19537902c8dcSWojciech Macek 		uint32_t cause_c = al_udma_iofic_read_cause(regs_base,
19547902c8dcSWojciech Macek 		    AL_UDMA_IOFIC_LEVEL_PRIMARY, AL_INT_GROUP_C);
19557902c8dcSWojciech Macek 		int qid;
19567902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "secondary C cause %x\n", cause_c);
19577902c8dcSWojciech Macek 		for (qid = 0; qid < adapter->num_tx_queues; qid++) {
19587902c8dcSWojciech Macek 			if ((cause_c & (1 << qid)) != 0) {
19597902c8dcSWojciech Macek 				al_udma_iofic_mask(
19607902c8dcSWojciech Macek 				    (struct unit_regs __iomem *)adapter->udma_base,
19617902c8dcSWojciech Macek 				    AL_UDMA_IOFIC_LEVEL_PRIMARY,
19627902c8dcSWojciech Macek 				    AL_INT_GROUP_C, 1 << qid);
19637902c8dcSWojciech Macek 			}
19647902c8dcSWojciech Macek 		}
19657902c8dcSWojciech Macek 	}
19667902c8dcSWojciech Macek 
19677902c8dcSWojciech Macek 	al_eth_tx_cmlp_irq_filter(adapter->tx_ring);
19687902c8dcSWojciech Macek 
19697902c8dcSWojciech Macek 	return (0);
19707902c8dcSWojciech Macek }
19717902c8dcSWojciech Macek 
19727902c8dcSWojciech Macek static int
19737902c8dcSWojciech Macek al_eth_intr_msix_all(void *data)
19747902c8dcSWojciech Macek {
19757902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = data;
19767902c8dcSWojciech Macek 
19777902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "%s\n", __func__);
19787902c8dcSWojciech Macek 	return (0);
19797902c8dcSWojciech Macek }
19807902c8dcSWojciech Macek 
19817902c8dcSWojciech Macek static int
19827902c8dcSWojciech Macek al_eth_intr_msix_mgmt(void *data)
19837902c8dcSWojciech Macek {
19847902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = data;
19857902c8dcSWojciech Macek 
19867902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "%s\n", __func__);
19877902c8dcSWojciech Macek 	return (0);
19887902c8dcSWojciech Macek }
19897902c8dcSWojciech Macek 
19907902c8dcSWojciech Macek static int
19917902c8dcSWojciech Macek al_eth_enable_msix(struct al_eth_adapter *adapter)
19927902c8dcSWojciech Macek {
19937902c8dcSWojciech Macek 	int i, msix_vecs, rc, count;
19947902c8dcSWojciech Macek 
19957902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "%s\n", __func__);
19967902c8dcSWojciech Macek 	msix_vecs = 1 + adapter->num_rx_queues + adapter->num_tx_queues;
19977902c8dcSWojciech Macek 
19987902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev,
19997902c8dcSWojciech Macek 	    "Try to enable MSIX, vector numbers = %d\n", msix_vecs);
20007902c8dcSWojciech Macek 
20017902c8dcSWojciech Macek 	adapter->msix_entries = malloc(msix_vecs*sizeof(*adapter->msix_entries),
20027902c8dcSWojciech Macek 	    M_IFAL, M_ZERO | M_WAITOK);
20037902c8dcSWojciech Macek 	/* management vector (GROUP_A) @2*/
20047902c8dcSWojciech Macek 	adapter->msix_entries[AL_ETH_MGMT_IRQ_IDX].entry = 2;
20057902c8dcSWojciech Macek 	adapter->msix_entries[AL_ETH_MGMT_IRQ_IDX].vector = 0;
20067902c8dcSWojciech Macek 
20077902c8dcSWojciech Macek 	/* rx queues start @3 */
20087902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++) {
20097902c8dcSWojciech Macek 		int irq_idx = AL_ETH_RXQ_IRQ_IDX(adapter, i);
20107902c8dcSWojciech Macek 
20117902c8dcSWojciech Macek 		adapter->msix_entries[irq_idx].entry = 3 + i;
20127902c8dcSWojciech Macek 		adapter->msix_entries[irq_idx].vector = 0;
20137902c8dcSWojciech Macek 	}
20147902c8dcSWojciech Macek 	/* tx queues start @7 */
20157902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_tx_queues; i++) {
20167902c8dcSWojciech Macek 		int irq_idx = AL_ETH_TXQ_IRQ_IDX(adapter, i);
20177902c8dcSWojciech Macek 
20187902c8dcSWojciech Macek 		adapter->msix_entries[irq_idx].entry = 3 +
20197902c8dcSWojciech Macek 		    AL_ETH_MAX_HW_QUEUES + i;
20207902c8dcSWojciech Macek 		adapter->msix_entries[irq_idx].vector = 0;
20217902c8dcSWojciech Macek 	}
20227902c8dcSWojciech Macek 
20237902c8dcSWojciech Macek 	count = msix_vecs + 2; /* entries start from 2 */
20247902c8dcSWojciech Macek 	rc = pci_alloc_msix(adapter->dev, &count);
20257902c8dcSWojciech Macek 
20267902c8dcSWojciech Macek 	if (rc != 0) {
20277902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "failed to allocate MSIX "
20287902c8dcSWojciech Macek 		    "vectors %d\n", msix_vecs+2);
20297902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "ret = %d\n", rc);
20307902c8dcSWojciech Macek 		goto msix_entries_exit;
20317902c8dcSWojciech Macek 	}
20327902c8dcSWojciech Macek 
20337902c8dcSWojciech Macek 	if (count != msix_vecs + 2) {
20347902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "failed to allocate all MSIX "
20357902c8dcSWojciech Macek 		    "vectors %d, allocated %d\n", msix_vecs+2, count);
20367902c8dcSWojciech Macek 		rc = ENOSPC;
20377902c8dcSWojciech Macek 		goto msix_entries_exit;
20387902c8dcSWojciech Macek 	}
20397902c8dcSWojciech Macek 
20407902c8dcSWojciech Macek 	for (i = 0; i < msix_vecs; i++)
20417902c8dcSWojciech Macek 	    adapter->msix_entries[i].vector = 2 + 1 + i;
20427902c8dcSWojciech Macek 
20437902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "successfully enabled MSIX,"
20447902c8dcSWojciech Macek 	    " vectors %d\n", msix_vecs);
20457902c8dcSWojciech Macek 
20467902c8dcSWojciech Macek 	adapter->msix_vecs = msix_vecs;
20477902c8dcSWojciech Macek 	adapter->flags |= AL_ETH_FLAG_MSIX_ENABLED;
20487902c8dcSWojciech Macek 	goto exit;
20497902c8dcSWojciech Macek 
20507902c8dcSWojciech Macek msix_entries_exit:
20517902c8dcSWojciech Macek 	adapter->msix_vecs = 0;
20527902c8dcSWojciech Macek 	free(adapter->msix_entries, M_IFAL);
20537902c8dcSWojciech Macek 	adapter->msix_entries = NULL;
20547902c8dcSWojciech Macek 
20557902c8dcSWojciech Macek exit:
20567902c8dcSWojciech Macek 	return (rc);
20577902c8dcSWojciech Macek }
20587902c8dcSWojciech Macek 
20597902c8dcSWojciech Macek static int
20607902c8dcSWojciech Macek al_eth_setup_int_mode(struct al_eth_adapter *adapter)
20617902c8dcSWojciech Macek {
20627902c8dcSWojciech Macek 	int i, rc;
20637902c8dcSWojciech Macek 
20647902c8dcSWojciech Macek 	rc = al_eth_enable_msix(adapter);
20657902c8dcSWojciech Macek 	if (rc != 0) {
20667902c8dcSWojciech Macek 		device_printf(adapter->dev, "Failed to enable MSIX mode.\n");
20677902c8dcSWojciech Macek 		return (rc);
20687902c8dcSWojciech Macek 	}
20697902c8dcSWojciech Macek 
20707902c8dcSWojciech Macek 	adapter->irq_vecs = max(1, adapter->msix_vecs);
20717902c8dcSWojciech Macek 	/* single INTX mode */
20727902c8dcSWojciech Macek 	if (adapter->msix_vecs == 0) {
20737902c8dcSWojciech Macek 		snprintf(adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].name,
20747902c8dcSWojciech Macek 		    AL_ETH_IRQNAME_SIZE, "al-eth-intx-all@pci:%s",
20757902c8dcSWojciech Macek 		    device_get_name(adapter->dev));
20767902c8dcSWojciech Macek 		adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].handler =
20777902c8dcSWojciech Macek 		    al_eth_intr_intx_all;
20787902c8dcSWojciech Macek 		/* IRQ vector will be resolved from device resources */
20797902c8dcSWojciech Macek 		adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].vector = 0;
20807902c8dcSWojciech Macek 		adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].data = adapter;
20817902c8dcSWojciech Macek 
20827902c8dcSWojciech Macek 		device_printf(adapter->dev, "%s and vector %d \n", __func__,
20837902c8dcSWojciech Macek 		    adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].vector);
20847902c8dcSWojciech Macek 
20857902c8dcSWojciech Macek 		return (0);
20867902c8dcSWojciech Macek 	}
20877902c8dcSWojciech Macek 	/* single MSI-X mode */
20887902c8dcSWojciech Macek 	if (adapter->msix_vecs == 1) {
20897902c8dcSWojciech Macek 		snprintf(adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].name,
20907902c8dcSWojciech Macek 		    AL_ETH_IRQNAME_SIZE, "al-eth-msix-all@pci:%s",
20917902c8dcSWojciech Macek 		    device_get_name(adapter->dev));
20927902c8dcSWojciech Macek 		adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].handler =
20937902c8dcSWojciech Macek 		    al_eth_intr_msix_all;
20947902c8dcSWojciech Macek 		adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].vector =
20957902c8dcSWojciech Macek 		    adapter->msix_entries[AL_ETH_MGMT_IRQ_IDX].vector;
20967902c8dcSWojciech Macek 		adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].data = adapter;
20977902c8dcSWojciech Macek 
20987902c8dcSWojciech Macek 		return (0);
20997902c8dcSWojciech Macek 	}
21007902c8dcSWojciech Macek 	/* MSI-X per queue */
21017902c8dcSWojciech Macek 	snprintf(adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].name, AL_ETH_IRQNAME_SIZE,
21027902c8dcSWojciech Macek 	    "al-eth-msix-mgmt@pci:%s", device_get_name(adapter->dev));
21037902c8dcSWojciech Macek 	adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].handler = al_eth_intr_msix_mgmt;
21047902c8dcSWojciech Macek 
21057902c8dcSWojciech Macek 	adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].data = adapter;
21067902c8dcSWojciech Macek 	adapter->irq_tbl[AL_ETH_MGMT_IRQ_IDX].vector =
21077902c8dcSWojciech Macek 	    adapter->msix_entries[AL_ETH_MGMT_IRQ_IDX].vector;
21087902c8dcSWojciech Macek 
21097902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++) {
21107902c8dcSWojciech Macek 		int irq_idx = AL_ETH_RXQ_IRQ_IDX(adapter, i);
21117902c8dcSWojciech Macek 
21127902c8dcSWojciech Macek 		snprintf(adapter->irq_tbl[irq_idx].name, AL_ETH_IRQNAME_SIZE,
21137902c8dcSWojciech Macek 		    "al-eth-rx-comp-%d@pci:%s", i,
21147902c8dcSWojciech Macek 		    device_get_name(adapter->dev));
21157902c8dcSWojciech Macek 		adapter->irq_tbl[irq_idx].handler = al_eth_rx_recv_irq_filter;
21167902c8dcSWojciech Macek 		adapter->irq_tbl[irq_idx].data = &adapter->rx_ring[i];
21177902c8dcSWojciech Macek 		adapter->irq_tbl[irq_idx].vector =
21187902c8dcSWojciech Macek 		    adapter->msix_entries[irq_idx].vector;
21197902c8dcSWojciech Macek 	}
21207902c8dcSWojciech Macek 
21217902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_tx_queues; i++) {
21227902c8dcSWojciech Macek 		int irq_idx = AL_ETH_TXQ_IRQ_IDX(adapter, i);
21237902c8dcSWojciech Macek 
21247902c8dcSWojciech Macek 		snprintf(adapter->irq_tbl[irq_idx].name,
21257902c8dcSWojciech Macek 		    AL_ETH_IRQNAME_SIZE, "al-eth-tx-comp-%d@pci:%s", i,
21267902c8dcSWojciech Macek 		    device_get_name(adapter->dev));
21277902c8dcSWojciech Macek 		adapter->irq_tbl[irq_idx].handler = al_eth_tx_cmlp_irq_filter;
21287902c8dcSWojciech Macek 		adapter->irq_tbl[irq_idx].data = &adapter->tx_ring[i];
21297902c8dcSWojciech Macek 		adapter->irq_tbl[irq_idx].vector =
21307902c8dcSWojciech Macek 		    adapter->msix_entries[irq_idx].vector;
21317902c8dcSWojciech Macek 	}
21327902c8dcSWojciech Macek 
21337902c8dcSWojciech Macek 	return (0);
21347902c8dcSWojciech Macek }
21357902c8dcSWojciech Macek 
21367902c8dcSWojciech Macek static void
21377902c8dcSWojciech Macek __al_eth_free_irq(struct al_eth_adapter *adapter)
21387902c8dcSWojciech Macek {
21397902c8dcSWojciech Macek 	struct al_eth_irq *irq;
21407902c8dcSWojciech Macek 	int i, rc;
21417902c8dcSWojciech Macek 
21427902c8dcSWojciech Macek 	for (i = 0; i < adapter->irq_vecs; i++) {
21437902c8dcSWojciech Macek 		irq = &adapter->irq_tbl[i];
21447902c8dcSWojciech Macek 		if (irq->requested != 0) {
21457902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev, "tear down irq: %d\n",
21467902c8dcSWojciech Macek 			    irq->vector);
21477902c8dcSWojciech Macek 			rc = bus_teardown_intr(adapter->dev, irq->res,
21487902c8dcSWojciech Macek 			    irq->cookie);
21497902c8dcSWojciech Macek 			if (rc != 0)
21507902c8dcSWojciech Macek 				device_printf(adapter->dev, "failed to tear "
21517902c8dcSWojciech Macek 				    "down irq: %d\n", irq->vector);
21527902c8dcSWojciech Macek 		}
21537902c8dcSWojciech Macek 		irq->requested = 0;
21547902c8dcSWojciech Macek 	}
21557902c8dcSWojciech Macek }
21567902c8dcSWojciech Macek 
21577902c8dcSWojciech Macek static void
21587902c8dcSWojciech Macek al_eth_free_irq(struct al_eth_adapter *adapter)
21597902c8dcSWojciech Macek {
21607902c8dcSWojciech Macek 	struct al_eth_irq *irq;
21617902c8dcSWojciech Macek 	int i, rc;
21627902c8dcSWojciech Macek #ifdef CONFIG_RFS_ACCEL
21637902c8dcSWojciech Macek 	if (adapter->msix_vecs >= 1) {
21647902c8dcSWojciech Macek 		free_irq_cpu_rmap(adapter->netdev->rx_cpu_rmap);
21657902c8dcSWojciech Macek 		adapter->netdev->rx_cpu_rmap = NULL;
21667902c8dcSWojciech Macek 	}
21677902c8dcSWojciech Macek #endif
21687902c8dcSWojciech Macek 
21697902c8dcSWojciech Macek 	__al_eth_free_irq(adapter);
21707902c8dcSWojciech Macek 
21717902c8dcSWojciech Macek 	for (i = 0; i < adapter->irq_vecs; i++) {
21727902c8dcSWojciech Macek 		irq = &adapter->irq_tbl[i];
21737902c8dcSWojciech Macek 		if (irq->res == NULL)
21747902c8dcSWojciech Macek 			continue;
21757902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "release resource irq: %d\n",
21767902c8dcSWojciech Macek 		    irq->vector);
21777902c8dcSWojciech Macek 		rc = bus_release_resource(adapter->dev, SYS_RES_IRQ, irq->vector,
21787902c8dcSWojciech Macek 		    irq->res);
21797902c8dcSWojciech Macek 		irq->res = NULL;
21807902c8dcSWojciech Macek 		if (rc != 0)
21817902c8dcSWojciech Macek 			device_printf(adapter->dev, "dev has no parent while "
21827902c8dcSWojciech Macek 			    "releasing res for irq: %d\n", irq->vector);
21837902c8dcSWojciech Macek 	}
21847902c8dcSWojciech Macek 
21857902c8dcSWojciech Macek 	pci_release_msi(adapter->dev);
21867902c8dcSWojciech Macek 
21877902c8dcSWojciech Macek 	adapter->flags &= ~AL_ETH_FLAG_MSIX_ENABLED;
21887902c8dcSWojciech Macek 
21897902c8dcSWojciech Macek 	adapter->msix_vecs = 0;
21907902c8dcSWojciech Macek 	free(adapter->msix_entries, M_IFAL);
21917902c8dcSWojciech Macek 	adapter->msix_entries = NULL;
21927902c8dcSWojciech Macek }
21937902c8dcSWojciech Macek 
21947902c8dcSWojciech Macek static int
21957902c8dcSWojciech Macek al_eth_request_irq(struct al_eth_adapter *adapter)
21967902c8dcSWojciech Macek {
21977902c8dcSWojciech Macek 	unsigned long flags;
21987902c8dcSWojciech Macek 	struct al_eth_irq *irq;
21997902c8dcSWojciech Macek 	int rc = 0, i, v;
22007902c8dcSWojciech Macek 
22017902c8dcSWojciech Macek 	if ((adapter->flags & AL_ETH_FLAG_MSIX_ENABLED) != 0)
22027902c8dcSWojciech Macek 		flags = RF_ACTIVE;
22037902c8dcSWojciech Macek 	else
22047902c8dcSWojciech Macek 		flags = RF_ACTIVE | RF_SHAREABLE;
22057902c8dcSWojciech Macek 
22067902c8dcSWojciech Macek 	for (i = 0; i < adapter->irq_vecs; i++) {
22077902c8dcSWojciech Macek 		irq = &adapter->irq_tbl[i];
22087902c8dcSWojciech Macek 
22097902c8dcSWojciech Macek 		if (irq->requested != 0)
22107902c8dcSWojciech Macek 			continue;
22117902c8dcSWojciech Macek 
22127902c8dcSWojciech Macek 		irq->res = bus_alloc_resource_any(adapter->dev, SYS_RES_IRQ,
22137902c8dcSWojciech Macek 		    &irq->vector, flags);
22147902c8dcSWojciech Macek 		if (irq->res == NULL) {
22157902c8dcSWojciech Macek 			device_printf(adapter->dev, "could not allocate "
22167902c8dcSWojciech Macek 			    "irq vector=%d\n", irq->vector);
22177902c8dcSWojciech Macek 			rc = ENXIO;
22187902c8dcSWojciech Macek 			goto exit_res;
22197902c8dcSWojciech Macek 		}
22207902c8dcSWojciech Macek 
22217902c8dcSWojciech Macek 		if ((rc = bus_setup_intr(adapter->dev, irq->res,
22227902c8dcSWojciech Macek 		    INTR_TYPE_NET | INTR_MPSAFE, irq->handler,
22237902c8dcSWojciech Macek 		    NULL, irq->data, &irq->cookie)) != 0) {
22247902c8dcSWojciech Macek 			device_printf(adapter->dev, "failed to register "
22257902c8dcSWojciech Macek 			    "interrupt handler for irq %ju: %d\n",
22267902c8dcSWojciech Macek 			    (uintmax_t)rman_get_start(irq->res), rc);
22277902c8dcSWojciech Macek 			goto exit_intr;
22287902c8dcSWojciech Macek 		}
22297902c8dcSWojciech Macek 		irq->requested = 1;
22307902c8dcSWojciech Macek 	}
22317902c8dcSWojciech Macek 	goto exit;
22327902c8dcSWojciech Macek 
22337902c8dcSWojciech Macek exit_intr:
22347902c8dcSWojciech Macek 	v = i - 1; /* -1 because we omit the operation that failed */
22357902c8dcSWojciech Macek 	while (v-- >= 0) {
22367902c8dcSWojciech Macek 		int bti;
22377902c8dcSWojciech Macek 		irq = &adapter->irq_tbl[v];
22387902c8dcSWojciech Macek 		bti = bus_teardown_intr(adapter->dev, irq->res, irq->cookie);
22397902c8dcSWojciech Macek 		if (bti != 0) {
22407902c8dcSWojciech Macek 			device_printf(adapter->dev, "failed to tear "
22417902c8dcSWojciech Macek 			    "down irq: %d\n", irq->vector);
22427902c8dcSWojciech Macek 		}
22437902c8dcSWojciech Macek 
22447902c8dcSWojciech Macek 		irq->requested = 0;
22457902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "exit_intr: releasing irq %d\n",
22467902c8dcSWojciech Macek 		    irq->vector);
22477902c8dcSWojciech Macek 	}
22487902c8dcSWojciech Macek 
22497902c8dcSWojciech Macek exit_res:
22507902c8dcSWojciech Macek 	v = i - 1; /* -1 because we omit the operation that failed */
22517902c8dcSWojciech Macek 	while (v-- >= 0) {
22527902c8dcSWojciech Macek 		int brr;
22537902c8dcSWojciech Macek 		irq = &adapter->irq_tbl[v];
22547902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev, "exit_res: releasing resource"
22557902c8dcSWojciech Macek 		    " for irq %d\n", irq->vector);
22567902c8dcSWojciech Macek 		brr = bus_release_resource(adapter->dev, SYS_RES_IRQ,
22577902c8dcSWojciech Macek 		    irq->vector, irq->res);
22587902c8dcSWojciech Macek 		if (brr != 0)
22597902c8dcSWojciech Macek 			device_printf(adapter->dev, "dev has no parent while "
22607902c8dcSWojciech Macek 			    "releasing res for irq: %d\n", irq->vector);
22617902c8dcSWojciech Macek 		irq->res = NULL;
22627902c8dcSWojciech Macek 	}
22637902c8dcSWojciech Macek 
22647902c8dcSWojciech Macek exit:
22657902c8dcSWojciech Macek 	return (rc);
22667902c8dcSWojciech Macek }
22677902c8dcSWojciech Macek 
22687902c8dcSWojciech Macek /**
22697902c8dcSWojciech Macek  * al_eth_setup_tx_resources - allocate Tx resources (Descriptors)
22707902c8dcSWojciech Macek  * @adapter: network interface device structure
22717902c8dcSWojciech Macek  * @qid: queue index
22727902c8dcSWojciech Macek  *
22737902c8dcSWojciech Macek  * Return 0 on success, negative on failure
22747902c8dcSWojciech Macek  **/
22757902c8dcSWojciech Macek static int
22767902c8dcSWojciech Macek al_eth_setup_tx_resources(struct al_eth_adapter *adapter, int qid)
22777902c8dcSWojciech Macek {
22787902c8dcSWojciech Macek 	struct al_eth_ring *tx_ring = &adapter->tx_ring[qid];
2279d8b1601dSMark Johnston 	device_t dev = tx_ring->dev;
22807902c8dcSWojciech Macek 	struct al_udma_q_params *q_params = &tx_ring->q_params;
22817902c8dcSWojciech Macek 	int size;
22827902c8dcSWojciech Macek 	int ret;
22837902c8dcSWojciech Macek 
22847902c8dcSWojciech Macek 	if (adapter->up)
22857902c8dcSWojciech Macek 		return (0);
22867902c8dcSWojciech Macek 
22877902c8dcSWojciech Macek 	size = sizeof(struct al_eth_tx_buffer) * tx_ring->sw_count;
22887902c8dcSWojciech Macek 
22897902c8dcSWojciech Macek 	tx_ring->tx_buffer_info = malloc(size, M_IFAL, M_ZERO | M_WAITOK);
22907902c8dcSWojciech Macek 	tx_ring->descs_size = tx_ring->hw_count * sizeof(union al_udma_desc);
22917902c8dcSWojciech Macek 	q_params->size = tx_ring->hw_count;
22927902c8dcSWojciech Macek 
22937902c8dcSWojciech Macek 	ret = al_dma_alloc_coherent(dev, &q_params->desc_phy_base_tag,
22947902c8dcSWojciech Macek 	    (bus_dmamap_t *)&q_params->desc_phy_base_map,
22957902c8dcSWojciech Macek 	    (bus_addr_t *)&q_params->desc_phy_base,
22967902c8dcSWojciech Macek 	    (void**)&q_params->desc_base, tx_ring->descs_size);
22977902c8dcSWojciech Macek 	if (ret != 0) {
22987902c8dcSWojciech Macek 		device_printf(dev, "failed to al_dma_alloc_coherent,"
22997902c8dcSWojciech Macek 		    " ret = %d\n", ret);
23007902c8dcSWojciech Macek 		return (ENOMEM);
23017902c8dcSWojciech Macek 	}
23027902c8dcSWojciech Macek 
23037902c8dcSWojciech Macek 	if (q_params->desc_base == NULL)
23047902c8dcSWojciech Macek 		return (ENOMEM);
23057902c8dcSWojciech Macek 
23067902c8dcSWojciech Macek 	device_printf_dbg(dev, "Initializing ring queues %d\n", qid);
23077902c8dcSWojciech Macek 
23087902c8dcSWojciech Macek 	/* Allocate Ring Queue */
23097902c8dcSWojciech Macek 	mtx_init(&tx_ring->br_mtx, "AlRingMtx", NULL, MTX_DEF);
23107902c8dcSWojciech Macek 	tx_ring->br = buf_ring_alloc(AL_BR_SIZE, M_DEVBUF, M_WAITOK,
23117902c8dcSWojciech Macek 	    &tx_ring->br_mtx);
23127902c8dcSWojciech Macek 
23137902c8dcSWojciech Macek 	/* Allocate taskqueues */
23147902c8dcSWojciech Macek 	TASK_INIT(&tx_ring->enqueue_task, 0, al_eth_start_xmit, tx_ring);
23157902c8dcSWojciech Macek 	tx_ring->enqueue_tq = taskqueue_create_fast("al_tx_enque", M_NOWAIT,
23167902c8dcSWojciech Macek 	    taskqueue_thread_enqueue, &tx_ring->enqueue_tq);
23177902c8dcSWojciech Macek 	taskqueue_start_threads(&tx_ring->enqueue_tq, 1, PI_NET, "%s txeq",
23187902c8dcSWojciech Macek 	    device_get_nameunit(adapter->dev));
23197902c8dcSWojciech Macek 	TASK_INIT(&tx_ring->cmpl_task, 0, al_eth_tx_cmpl_work, tx_ring);
23207902c8dcSWojciech Macek 	tx_ring->cmpl_tq = taskqueue_create_fast("al_tx_cmpl", M_NOWAIT,
23217902c8dcSWojciech Macek 	    taskqueue_thread_enqueue, &tx_ring->cmpl_tq);
23227902c8dcSWojciech Macek 	taskqueue_start_threads(&tx_ring->cmpl_tq, 1, PI_REALTIME, "%s txcq",
23237902c8dcSWojciech Macek 	    device_get_nameunit(adapter->dev));
23247902c8dcSWojciech Macek 
23257902c8dcSWojciech Macek 	/* Setup DMA descriptor areas. */
23267902c8dcSWojciech Macek 	ret = bus_dma_tag_create(bus_get_dma_tag(dev),
23277902c8dcSWojciech Macek 	    1, 0,			/* alignment, bounds */
23287902c8dcSWojciech Macek 	    BUS_SPACE_MAXADDR,		/* lowaddr */
23297902c8dcSWojciech Macek 	    BUS_SPACE_MAXADDR,		/* highaddr */
23307902c8dcSWojciech Macek 	    NULL, NULL,			/* filter, filterarg */
23317902c8dcSWojciech Macek 	    AL_TSO_SIZE,		/* maxsize */
23327902c8dcSWojciech Macek 	    AL_ETH_PKT_MAX_BUFS,	/* nsegments */
23337902c8dcSWojciech Macek 	    PAGE_SIZE,			/* maxsegsize */
23347902c8dcSWojciech Macek 	    0,				/* flags */
23357902c8dcSWojciech Macek 	    NULL,			/* lockfunc */
23367902c8dcSWojciech Macek 	    NULL,			/* lockfuncarg */
23377902c8dcSWojciech Macek 	    &tx_ring->dma_buf_tag);
23387902c8dcSWojciech Macek 
23397902c8dcSWojciech Macek 	if (ret != 0) {
23407902c8dcSWojciech Macek 		device_printf(dev,"Unable to allocate dma_buf_tag, ret = %d\n",
23417902c8dcSWojciech Macek 		    ret);
23427902c8dcSWojciech Macek 		return (ret);
23437902c8dcSWojciech Macek 	}
23447902c8dcSWojciech Macek 
23457902c8dcSWojciech Macek 	for (size = 0; size < tx_ring->sw_count; size++) {
23467902c8dcSWojciech Macek 		ret = bus_dmamap_create(tx_ring->dma_buf_tag, 0,
23477902c8dcSWojciech Macek 		    &tx_ring->tx_buffer_info[size].dma_map);
23487902c8dcSWojciech Macek 		if (ret != 0) {
23497902c8dcSWojciech Macek 			device_printf(dev, "Unable to map DMA TX "
23507902c8dcSWojciech Macek 			    "buffer memory [iter=%d]\n", size);
23517902c8dcSWojciech Macek 			return (ret);
23527902c8dcSWojciech Macek 		}
23537902c8dcSWojciech Macek 	}
23547902c8dcSWojciech Macek 
23557902c8dcSWojciech Macek 	/* completion queue not used for tx */
23567902c8dcSWojciech Macek 	q_params->cdesc_base = NULL;
23577902c8dcSWojciech Macek 	/* size in bytes of the udma completion ring descriptor */
23587902c8dcSWojciech Macek 	q_params->cdesc_size = 8;
23597902c8dcSWojciech Macek 	tx_ring->next_to_use = 0;
23607902c8dcSWojciech Macek 	tx_ring->next_to_clean = 0;
23617902c8dcSWojciech Macek 
23627902c8dcSWojciech Macek 	return (0);
23637902c8dcSWojciech Macek }
23647902c8dcSWojciech Macek 
23657902c8dcSWojciech Macek /*
23667902c8dcSWojciech Macek  * al_eth_free_tx_resources - Free Tx Resources per Queue
23677902c8dcSWojciech Macek  * @adapter: network interface device structure
23687902c8dcSWojciech Macek  * @qid: queue index
23697902c8dcSWojciech Macek  *
23707902c8dcSWojciech Macek  * Free all transmit software resources
23717902c8dcSWojciech Macek  */
23727902c8dcSWojciech Macek static void
23737902c8dcSWojciech Macek al_eth_free_tx_resources(struct al_eth_adapter *adapter, int qid)
23747902c8dcSWojciech Macek {
23757902c8dcSWojciech Macek 	struct al_eth_ring *tx_ring = &adapter->tx_ring[qid];
23767902c8dcSWojciech Macek 	struct al_udma_q_params *q_params = &tx_ring->q_params;
23777902c8dcSWojciech Macek 	int size;
23787902c8dcSWojciech Macek 
23797902c8dcSWojciech Macek 	/* At this point interrupts' handlers must be deactivated */
23807902c8dcSWojciech Macek 	while (taskqueue_cancel(tx_ring->cmpl_tq, &tx_ring->cmpl_task, NULL))
23817902c8dcSWojciech Macek 		taskqueue_drain(tx_ring->cmpl_tq, &tx_ring->cmpl_task);
23827902c8dcSWojciech Macek 
23837902c8dcSWojciech Macek 	taskqueue_free(tx_ring->cmpl_tq);
23847902c8dcSWojciech Macek 	while (taskqueue_cancel(tx_ring->enqueue_tq,
23857902c8dcSWojciech Macek 	    &tx_ring->enqueue_task, NULL)) {
23867902c8dcSWojciech Macek 		taskqueue_drain(tx_ring->enqueue_tq, &tx_ring->enqueue_task);
23877902c8dcSWojciech Macek 	}
23887902c8dcSWojciech Macek 
23897902c8dcSWojciech Macek 	taskqueue_free(tx_ring->enqueue_tq);
23907902c8dcSWojciech Macek 
23917902c8dcSWojciech Macek 	if (tx_ring->br != NULL) {
23927902c8dcSWojciech Macek 		drbr_flush(adapter->netdev, tx_ring->br);
23937902c8dcSWojciech Macek 		buf_ring_free(tx_ring->br, M_DEVBUF);
23947902c8dcSWojciech Macek 	}
23957902c8dcSWojciech Macek 
23967902c8dcSWojciech Macek 	for (size = 0; size < tx_ring->sw_count; size++) {
23977902c8dcSWojciech Macek 		m_freem(tx_ring->tx_buffer_info[size].m);
23987902c8dcSWojciech Macek 		tx_ring->tx_buffer_info[size].m = NULL;
23997902c8dcSWojciech Macek 
24007902c8dcSWojciech Macek 		bus_dmamap_unload(tx_ring->dma_buf_tag,
24017902c8dcSWojciech Macek 		    tx_ring->tx_buffer_info[size].dma_map);
24027902c8dcSWojciech Macek 		bus_dmamap_destroy(tx_ring->dma_buf_tag,
24037902c8dcSWojciech Macek 		    tx_ring->tx_buffer_info[size].dma_map);
24047902c8dcSWojciech Macek 	}
24057902c8dcSWojciech Macek 	bus_dma_tag_destroy(tx_ring->dma_buf_tag);
24067902c8dcSWojciech Macek 
24077902c8dcSWojciech Macek 	free(tx_ring->tx_buffer_info, M_IFAL);
24087902c8dcSWojciech Macek 	tx_ring->tx_buffer_info = NULL;
24097902c8dcSWojciech Macek 
24107902c8dcSWojciech Macek 	mtx_destroy(&tx_ring->br_mtx);
24117902c8dcSWojciech Macek 
24127902c8dcSWojciech Macek 	/* if not set, then don't free */
24137902c8dcSWojciech Macek 	if (q_params->desc_base == NULL)
24147902c8dcSWojciech Macek 		return;
24157902c8dcSWojciech Macek 
24167902c8dcSWojciech Macek 	al_dma_free_coherent(q_params->desc_phy_base_tag,
24177902c8dcSWojciech Macek 	    q_params->desc_phy_base_map, q_params->desc_base);
24187902c8dcSWojciech Macek 
24197902c8dcSWojciech Macek 	q_params->desc_base = NULL;
24207902c8dcSWojciech Macek }
24217902c8dcSWojciech Macek 
24227902c8dcSWojciech Macek /*
24237902c8dcSWojciech Macek  * al_eth_free_all_tx_resources - Free Tx Resources for All Queues
24247902c8dcSWojciech Macek  * @adapter: board private structure
24257902c8dcSWojciech Macek  *
24267902c8dcSWojciech Macek  * Free all transmit software resources
24277902c8dcSWojciech Macek  */
24287902c8dcSWojciech Macek static void
24297902c8dcSWojciech Macek al_eth_free_all_tx_resources(struct al_eth_adapter *adapter)
24307902c8dcSWojciech Macek {
24317902c8dcSWojciech Macek 	int i;
24327902c8dcSWojciech Macek 
24337902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_tx_queues; i++)
24347902c8dcSWojciech Macek 		if (adapter->tx_ring[i].q_params.desc_base)
24357902c8dcSWojciech Macek 			al_eth_free_tx_resources(adapter, i);
24367902c8dcSWojciech Macek }
24377902c8dcSWojciech Macek 
24387902c8dcSWojciech Macek /*
24397902c8dcSWojciech Macek  * al_eth_setup_rx_resources - allocate Rx resources (Descriptors)
24407902c8dcSWojciech Macek  * @adapter: network interface device structure
24417902c8dcSWojciech Macek  * @qid: queue index
24427902c8dcSWojciech Macek  *
24437902c8dcSWojciech Macek  * Returns 0 on success, negative on failure
24447902c8dcSWojciech Macek  */
24457902c8dcSWojciech Macek static int
24467902c8dcSWojciech Macek al_eth_setup_rx_resources(struct al_eth_adapter *adapter, unsigned int qid)
24477902c8dcSWojciech Macek {
24487902c8dcSWojciech Macek 	struct al_eth_ring *rx_ring = &adapter->rx_ring[qid];
2449d8b1601dSMark Johnston 	device_t dev = rx_ring->dev;
24507902c8dcSWojciech Macek 	struct al_udma_q_params *q_params = &rx_ring->q_params;
24517902c8dcSWojciech Macek 	int size;
24527902c8dcSWojciech Macek 	int ret;
24537902c8dcSWojciech Macek 
24547902c8dcSWojciech Macek 	size = sizeof(struct al_eth_rx_buffer) * rx_ring->sw_count;
24557902c8dcSWojciech Macek 
24567902c8dcSWojciech Macek 	/* alloc extra element so in rx path we can always prefetch rx_info + 1 */
24577902c8dcSWojciech Macek 	size += 1;
24587902c8dcSWojciech Macek 
24597902c8dcSWojciech Macek 	rx_ring->rx_buffer_info = malloc(size, M_IFAL, M_ZERO | M_WAITOK);
24607902c8dcSWojciech Macek 	rx_ring->descs_size = rx_ring->hw_count * sizeof(union al_udma_desc);
24617902c8dcSWojciech Macek 	q_params->size = rx_ring->hw_count;
24627902c8dcSWojciech Macek 
24637902c8dcSWojciech Macek 	ret = al_dma_alloc_coherent(dev, &q_params->desc_phy_base_tag,
24647902c8dcSWojciech Macek 	    &q_params->desc_phy_base_map,
24657902c8dcSWojciech Macek 	    (bus_addr_t *)&q_params->desc_phy_base,
24667902c8dcSWojciech Macek 	    (void**)&q_params->desc_base, rx_ring->descs_size);
24677902c8dcSWojciech Macek 
24687902c8dcSWojciech Macek 	if ((q_params->desc_base == NULL) || (ret != 0))
24697902c8dcSWojciech Macek 		return (ENOMEM);
24707902c8dcSWojciech Macek 
24717902c8dcSWojciech Macek 	/* size in bytes of the udma completion ring descriptor */
24727902c8dcSWojciech Macek 	q_params->cdesc_size = 16;
24737902c8dcSWojciech Macek 	rx_ring->cdescs_size = rx_ring->hw_count * q_params->cdesc_size;
24747902c8dcSWojciech Macek 	ret = al_dma_alloc_coherent(dev, &q_params->cdesc_phy_base_tag,
24757902c8dcSWojciech Macek 	    &q_params->cdesc_phy_base_map,
24767902c8dcSWojciech Macek 	    (bus_addr_t *)&q_params->cdesc_phy_base,
24777902c8dcSWojciech Macek 	    (void**)&q_params->cdesc_base, rx_ring->cdescs_size);
24787902c8dcSWojciech Macek 
24797902c8dcSWojciech Macek 	if ((q_params->cdesc_base == NULL) || (ret != 0))
24807902c8dcSWojciech Macek 		return (ENOMEM);
24817902c8dcSWojciech Macek 
24827902c8dcSWojciech Macek 	/* Allocate taskqueues */
24836c3e93cbSGleb Smirnoff 	NET_TASK_INIT(&rx_ring->enqueue_task, 0, al_eth_rx_recv_work, rx_ring);
24847902c8dcSWojciech Macek 	rx_ring->enqueue_tq = taskqueue_create_fast("al_rx_enque", M_NOWAIT,
24857902c8dcSWojciech Macek 	    taskqueue_thread_enqueue, &rx_ring->enqueue_tq);
24867902c8dcSWojciech Macek 	taskqueue_start_threads(&rx_ring->enqueue_tq, 1, PI_NET, "%s rxeq",
24877902c8dcSWojciech Macek 	    device_get_nameunit(adapter->dev));
24887902c8dcSWojciech Macek 
24897902c8dcSWojciech Macek 	/* Setup DMA descriptor areas. */
24907902c8dcSWojciech Macek 	ret = bus_dma_tag_create(bus_get_dma_tag(dev),
24917902c8dcSWojciech Macek 	    1, 0,			/* alignment, bounds */
24927902c8dcSWojciech Macek 	    BUS_SPACE_MAXADDR,		/* lowaddr */
24937902c8dcSWojciech Macek 	    BUS_SPACE_MAXADDR,		/* highaddr */
24947902c8dcSWojciech Macek 	    NULL, NULL,			/* filter, filterarg */
24957902c8dcSWojciech Macek 	    AL_TSO_SIZE,		/* maxsize */
24967902c8dcSWojciech Macek 	    1,				/* nsegments */
24977902c8dcSWojciech Macek 	    AL_TSO_SIZE,		/* maxsegsize */
24987902c8dcSWojciech Macek 	    0,				/* flags */
24997902c8dcSWojciech Macek 	    NULL,			/* lockfunc */
25007902c8dcSWojciech Macek 	    NULL,			/* lockfuncarg */
25017902c8dcSWojciech Macek 	    &rx_ring->dma_buf_tag);
25027902c8dcSWojciech Macek 
25037902c8dcSWojciech Macek 	if (ret != 0) {
25047902c8dcSWojciech Macek 		device_printf(dev,"Unable to allocate RX dma_buf_tag\n");
25057902c8dcSWojciech Macek 		return (ret);
25067902c8dcSWojciech Macek 	}
25077902c8dcSWojciech Macek 
25087902c8dcSWojciech Macek 	for (size = 0; size < rx_ring->sw_count; size++) {
25097902c8dcSWojciech Macek 		ret = bus_dmamap_create(rx_ring->dma_buf_tag, 0,
25107902c8dcSWojciech Macek 		    &rx_ring->rx_buffer_info[size].dma_map);
25117902c8dcSWojciech Macek 		if (ret != 0) {
25127902c8dcSWojciech Macek 			device_printf(dev,"Unable to map DMA RX buffer memory\n");
25137902c8dcSWojciech Macek 			return (ret);
25147902c8dcSWojciech Macek 		}
25157902c8dcSWojciech Macek 	}
25167902c8dcSWojciech Macek 
25177902c8dcSWojciech Macek 	/* Zero out the descriptor ring */
25187902c8dcSWojciech Macek 	memset(q_params->cdesc_base, 0, rx_ring->cdescs_size);
25197902c8dcSWojciech Macek 
25207902c8dcSWojciech Macek 	/* Create LRO for the ring */
2521f46a05b5SJustin Hibbits 	if ((if_getcapenable(adapter->netdev) & IFCAP_LRO) != 0) {
25227902c8dcSWojciech Macek 		int err = tcp_lro_init(&rx_ring->lro);
25237902c8dcSWojciech Macek 		if (err != 0) {
25247902c8dcSWojciech Macek 			device_printf(adapter->dev,
25257902c8dcSWojciech Macek 			    "LRO[%d] Initialization failed!\n", qid);
25267902c8dcSWojciech Macek 		} else {
25277902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev,
25287902c8dcSWojciech Macek 			    "RX Soft LRO[%d] Initialized\n", qid);
2529463edaf4SJohn Baldwin 			rx_ring->lro_enabled = true;
25307902c8dcSWojciech Macek 			rx_ring->lro.ifp = adapter->netdev;
25317902c8dcSWojciech Macek 		}
25327902c8dcSWojciech Macek 	}
25337902c8dcSWojciech Macek 
25347902c8dcSWojciech Macek 	rx_ring->next_to_clean = 0;
25357902c8dcSWojciech Macek 	rx_ring->next_to_use = 0;
25367902c8dcSWojciech Macek 
25377902c8dcSWojciech Macek 	return (0);
25387902c8dcSWojciech Macek }
25397902c8dcSWojciech Macek 
25407902c8dcSWojciech Macek /*
25417902c8dcSWojciech Macek  * al_eth_free_rx_resources - Free Rx Resources
25427902c8dcSWojciech Macek  * @adapter: network interface device structure
25437902c8dcSWojciech Macek  * @qid: queue index
25447902c8dcSWojciech Macek  *
25457902c8dcSWojciech Macek  * Free all receive software resources
25467902c8dcSWojciech Macek  */
25477902c8dcSWojciech Macek static void
25487902c8dcSWojciech Macek al_eth_free_rx_resources(struct al_eth_adapter *adapter, unsigned int qid)
25497902c8dcSWojciech Macek {
25507902c8dcSWojciech Macek 	struct al_eth_ring *rx_ring = &adapter->rx_ring[qid];
25517902c8dcSWojciech Macek 	struct al_udma_q_params *q_params = &rx_ring->q_params;
25527902c8dcSWojciech Macek 	int size;
25537902c8dcSWojciech Macek 
25547902c8dcSWojciech Macek 	/* At this point interrupts' handlers must be deactivated */
25557902c8dcSWojciech Macek 	while (taskqueue_cancel(rx_ring->enqueue_tq,
25567902c8dcSWojciech Macek 	    &rx_ring->enqueue_task, NULL)) {
25577902c8dcSWojciech Macek 		taskqueue_drain(rx_ring->enqueue_tq, &rx_ring->enqueue_task);
25587902c8dcSWojciech Macek 	}
25597902c8dcSWojciech Macek 
25607902c8dcSWojciech Macek 	taskqueue_free(rx_ring->enqueue_tq);
25617902c8dcSWojciech Macek 
25627902c8dcSWojciech Macek 	for (size = 0; size < rx_ring->sw_count; size++) {
25637902c8dcSWojciech Macek 		m_freem(rx_ring->rx_buffer_info[size].m);
25647902c8dcSWojciech Macek 		rx_ring->rx_buffer_info[size].m = NULL;
25657902c8dcSWojciech Macek 		bus_dmamap_unload(rx_ring->dma_buf_tag,
25667902c8dcSWojciech Macek 		    rx_ring->rx_buffer_info[size].dma_map);
25677902c8dcSWojciech Macek 		bus_dmamap_destroy(rx_ring->dma_buf_tag,
25687902c8dcSWojciech Macek 		    rx_ring->rx_buffer_info[size].dma_map);
25697902c8dcSWojciech Macek 	}
25707902c8dcSWojciech Macek 	bus_dma_tag_destroy(rx_ring->dma_buf_tag);
25717902c8dcSWojciech Macek 
25727902c8dcSWojciech Macek 	free(rx_ring->rx_buffer_info, M_IFAL);
25737902c8dcSWojciech Macek 	rx_ring->rx_buffer_info = NULL;
25747902c8dcSWojciech Macek 
25757902c8dcSWojciech Macek 	/* if not set, then don't free */
25767902c8dcSWojciech Macek 	if (q_params->desc_base == NULL)
25777902c8dcSWojciech Macek 		return;
25787902c8dcSWojciech Macek 
25797902c8dcSWojciech Macek 	al_dma_free_coherent(q_params->desc_phy_base_tag,
25807902c8dcSWojciech Macek 	    q_params->desc_phy_base_map, q_params->desc_base);
25817902c8dcSWojciech Macek 
25827902c8dcSWojciech Macek 	q_params->desc_base = NULL;
25837902c8dcSWojciech Macek 
25847902c8dcSWojciech Macek 	/* if not set, then don't free */
25857902c8dcSWojciech Macek 	if (q_params->cdesc_base == NULL)
25867902c8dcSWojciech Macek 		return;
25877902c8dcSWojciech Macek 
25887902c8dcSWojciech Macek 	al_dma_free_coherent(q_params->cdesc_phy_base_tag,
25897902c8dcSWojciech Macek 	    q_params->cdesc_phy_base_map, q_params->cdesc_base);
25907902c8dcSWojciech Macek 
25917902c8dcSWojciech Macek 	q_params->cdesc_phy_base = 0;
25927902c8dcSWojciech Macek 
25937902c8dcSWojciech Macek 	/* Free LRO resources */
25947902c8dcSWojciech Macek 	tcp_lro_free(&rx_ring->lro);
25957902c8dcSWojciech Macek }
25967902c8dcSWojciech Macek 
25977902c8dcSWojciech Macek /*
25987902c8dcSWojciech Macek  * al_eth_free_all_rx_resources - Free Rx Resources for All Queues
25997902c8dcSWojciech Macek  * @adapter: board private structure
26007902c8dcSWojciech Macek  *
26017902c8dcSWojciech Macek  * Free all receive software resources
26027902c8dcSWojciech Macek  */
26037902c8dcSWojciech Macek static void
26047902c8dcSWojciech Macek al_eth_free_all_rx_resources(struct al_eth_adapter *adapter)
26057902c8dcSWojciech Macek {
26067902c8dcSWojciech Macek 	int i;
26077902c8dcSWojciech Macek 
26087902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++)
26097902c8dcSWojciech Macek 		if (adapter->rx_ring[i].q_params.desc_base != 0)
26107902c8dcSWojciech Macek 			al_eth_free_rx_resources(adapter, i);
26117902c8dcSWojciech Macek }
26127902c8dcSWojciech Macek 
26137902c8dcSWojciech Macek /*
26147902c8dcSWojciech Macek  * al_eth_setup_all_rx_resources - allocate all queues Rx resources
26157902c8dcSWojciech Macek  * @adapter: board private structure
26167902c8dcSWojciech Macek  *
26177902c8dcSWojciech Macek  * Return 0 on success, negative on failure
26187902c8dcSWojciech Macek  */
26197902c8dcSWojciech Macek static int
26207902c8dcSWojciech Macek al_eth_setup_all_rx_resources(struct al_eth_adapter *adapter)
26217902c8dcSWojciech Macek {
26227902c8dcSWojciech Macek 	int i, rc = 0;
26237902c8dcSWojciech Macek 
26247902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++) {
26257902c8dcSWojciech Macek 		rc = al_eth_setup_rx_resources(adapter, i);
26267902c8dcSWojciech Macek 		if (rc == 0)
26277902c8dcSWojciech Macek 			continue;
26287902c8dcSWojciech Macek 
26297902c8dcSWojciech Macek 		device_printf(adapter->dev, "Allocation for Rx Queue %u failed\n", i);
26307902c8dcSWojciech Macek 		goto err_setup_rx;
26317902c8dcSWojciech Macek 	}
26327902c8dcSWojciech Macek 	return (0);
26337902c8dcSWojciech Macek 
26347902c8dcSWojciech Macek err_setup_rx:
26357902c8dcSWojciech Macek 	/* rewind the index freeing the rings as we go */
26367902c8dcSWojciech Macek 	while (i--)
26377902c8dcSWojciech Macek 		al_eth_free_rx_resources(adapter, i);
26387902c8dcSWojciech Macek 	return (rc);
26397902c8dcSWojciech Macek }
26407902c8dcSWojciech Macek 
26417902c8dcSWojciech Macek /*
26427902c8dcSWojciech Macek  * al_eth_setup_all_tx_resources - allocate all queues Tx resources
26437902c8dcSWojciech Macek  * @adapter: private structure
26447902c8dcSWojciech Macek  *
26457902c8dcSWojciech Macek  * Return 0 on success, negative on failure
26467902c8dcSWojciech Macek  */
26477902c8dcSWojciech Macek static int
26487902c8dcSWojciech Macek al_eth_setup_all_tx_resources(struct al_eth_adapter *adapter)
26497902c8dcSWojciech Macek {
26507902c8dcSWojciech Macek 	int i, rc = 0;
26517902c8dcSWojciech Macek 
26527902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_tx_queues; i++) {
26537902c8dcSWojciech Macek 		rc = al_eth_setup_tx_resources(adapter, i);
26547902c8dcSWojciech Macek 		if (rc == 0)
26557902c8dcSWojciech Macek 			continue;
26567902c8dcSWojciech Macek 
26577902c8dcSWojciech Macek 		device_printf(adapter->dev,
26587902c8dcSWojciech Macek 		    "Allocation for Tx Queue %u failed\n", i);
26597902c8dcSWojciech Macek 		goto err_setup_tx;
26607902c8dcSWojciech Macek 	}
26617902c8dcSWojciech Macek 
26627902c8dcSWojciech Macek 	return (0);
26637902c8dcSWojciech Macek 
26647902c8dcSWojciech Macek err_setup_tx:
26657902c8dcSWojciech Macek 	/* rewind the index freeing the rings as we go */
26667902c8dcSWojciech Macek 	while (i--)
26677902c8dcSWojciech Macek 		al_eth_free_tx_resources(adapter, i);
26687902c8dcSWojciech Macek 
26697902c8dcSWojciech Macek 	return (rc);
26707902c8dcSWojciech Macek }
26717902c8dcSWojciech Macek 
26727902c8dcSWojciech Macek static void
26737902c8dcSWojciech Macek al_eth_disable_int_sync(struct al_eth_adapter *adapter)
26747902c8dcSWojciech Macek {
26757902c8dcSWojciech Macek 
26767902c8dcSWojciech Macek 	/* disable forwarding interrupts from eth through pci end point */
26777902c8dcSWojciech Macek 	if ((adapter->board_type == ALPINE_FPGA_NIC) ||
26787902c8dcSWojciech Macek 	    (adapter->board_type == ALPINE_NIC)) {
26797902c8dcSWojciech Macek 		al_eth_forward_int_config((uint32_t*)adapter->internal_pcie_base +
26807902c8dcSWojciech Macek 		    AL_REG_OFFSET_FORWARD_INTR, AL_DIS_FORWARD_INTR);
26817902c8dcSWojciech Macek 	}
26827902c8dcSWojciech Macek 
26837902c8dcSWojciech Macek 	/* mask hw interrupts */
26847902c8dcSWojciech Macek 	al_eth_interrupts_mask(adapter);
26857902c8dcSWojciech Macek }
26867902c8dcSWojciech Macek 
26877902c8dcSWojciech Macek static void
26887902c8dcSWojciech Macek al_eth_interrupts_unmask(struct al_eth_adapter *adapter)
26897902c8dcSWojciech Macek {
26907902c8dcSWojciech Macek 	uint32_t group_a_mask = AL_INT_GROUP_A_GROUP_D_SUM; /* enable group D summery */
26917902c8dcSWojciech Macek 	uint32_t group_b_mask = (1 << adapter->num_rx_queues) - 1;/* bit per Rx q*/
26927902c8dcSWojciech Macek 	uint32_t group_c_mask = (1 << adapter->num_tx_queues) - 1;/* bit per Tx q*/
26937902c8dcSWojciech Macek 	uint32_t group_d_mask = 3 << 8;
26947902c8dcSWojciech Macek 	struct unit_regs __iomem *regs_base =
26957902c8dcSWojciech Macek 	    (struct unit_regs __iomem *)adapter->udma_base;
26967902c8dcSWojciech Macek 
26977902c8dcSWojciech Macek 	if (adapter->int_mode == AL_IOFIC_MODE_LEGACY)
26987902c8dcSWojciech Macek 		group_a_mask |= AL_INT_GROUP_A_GROUP_B_SUM |
26997902c8dcSWojciech Macek 		    AL_INT_GROUP_A_GROUP_C_SUM |
27007902c8dcSWojciech Macek 		    AL_INT_GROUP_A_GROUP_D_SUM;
27017902c8dcSWojciech Macek 
27027902c8dcSWojciech Macek 	al_udma_iofic_unmask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27037902c8dcSWojciech Macek 	    AL_INT_GROUP_A, group_a_mask);
27047902c8dcSWojciech Macek 	al_udma_iofic_unmask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27057902c8dcSWojciech Macek 	    AL_INT_GROUP_B, group_b_mask);
27067902c8dcSWojciech Macek 	al_udma_iofic_unmask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27077902c8dcSWojciech Macek 	    AL_INT_GROUP_C, group_c_mask);
27087902c8dcSWojciech Macek 	al_udma_iofic_unmask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27097902c8dcSWojciech Macek 	    AL_INT_GROUP_D, group_d_mask);
27107902c8dcSWojciech Macek }
27117902c8dcSWojciech Macek 
27127902c8dcSWojciech Macek static void
27137902c8dcSWojciech Macek al_eth_interrupts_mask(struct al_eth_adapter *adapter)
27147902c8dcSWojciech Macek {
27157902c8dcSWojciech Macek 	struct unit_regs __iomem *regs_base =
27167902c8dcSWojciech Macek 	    (struct unit_regs __iomem *)adapter->udma_base;
27177902c8dcSWojciech Macek 
27187902c8dcSWojciech Macek 	/* mask all interrupts */
27197902c8dcSWojciech Macek 	al_udma_iofic_mask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27207902c8dcSWojciech Macek 	    AL_INT_GROUP_A, AL_MASK_GROUP_A_INT);
27217902c8dcSWojciech Macek 	al_udma_iofic_mask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27227902c8dcSWojciech Macek 	    AL_INT_GROUP_B, AL_MASK_GROUP_B_INT);
27237902c8dcSWojciech Macek 	al_udma_iofic_mask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27247902c8dcSWojciech Macek 	    AL_INT_GROUP_C, AL_MASK_GROUP_C_INT);
27257902c8dcSWojciech Macek 	al_udma_iofic_mask(regs_base, AL_UDMA_IOFIC_LEVEL_PRIMARY,
27267902c8dcSWojciech Macek 	    AL_INT_GROUP_D, AL_MASK_GROUP_D_INT);
27277902c8dcSWojciech Macek }
27287902c8dcSWojciech Macek 
27297902c8dcSWojciech Macek static int
27307902c8dcSWojciech Macek al_eth_configure_int_mode(struct al_eth_adapter *adapter)
27317902c8dcSWojciech Macek {
27327902c8dcSWojciech Macek 	enum al_iofic_mode int_mode;
27337902c8dcSWojciech Macek 	uint32_t m2s_errors_disable = AL_M2S_MASK_INIT;
27347902c8dcSWojciech Macek 	uint32_t m2s_aborts_disable = AL_M2S_MASK_INIT;
27357902c8dcSWojciech Macek 	uint32_t s2m_errors_disable = AL_S2M_MASK_INIT;
27367902c8dcSWojciech Macek 	uint32_t s2m_aborts_disable = AL_S2M_MASK_INIT;
27377902c8dcSWojciech Macek 
27387902c8dcSWojciech Macek 	/* single INTX mode */
27397902c8dcSWojciech Macek 	if (adapter->msix_vecs == 0)
27407902c8dcSWojciech Macek 		int_mode = AL_IOFIC_MODE_LEGACY;
27417902c8dcSWojciech Macek 	else if (adapter->msix_vecs > 1)
27427902c8dcSWojciech Macek 		int_mode = AL_IOFIC_MODE_MSIX_PER_Q;
27437902c8dcSWojciech Macek 	else {
27447902c8dcSWojciech Macek 		device_printf(adapter->dev,
27457902c8dcSWojciech Macek 		    "udma doesn't support single MSI-X mode yet.\n");
27467902c8dcSWojciech Macek 		return (EIO);
27477902c8dcSWojciech Macek 	}
27487902c8dcSWojciech Macek 
27497902c8dcSWojciech Macek 	if (adapter->board_type != ALPINE_INTEGRATED) {
27507902c8dcSWojciech Macek 		m2s_errors_disable |= AL_M2S_S2M_MASK_NOT_INT;
27517902c8dcSWojciech Macek 		m2s_errors_disable |= AL_M2S_S2M_MASK_NOT_INT;
27527902c8dcSWojciech Macek 		s2m_aborts_disable |= AL_M2S_S2M_MASK_NOT_INT;
27537902c8dcSWojciech Macek 		s2m_aborts_disable |= AL_M2S_S2M_MASK_NOT_INT;
27547902c8dcSWojciech Macek 	}
27557902c8dcSWojciech Macek 
27567902c8dcSWojciech Macek 	if (al_udma_iofic_config((struct unit_regs __iomem *)adapter->udma_base,
27577902c8dcSWojciech Macek 	    int_mode, m2s_errors_disable, m2s_aborts_disable,
27587902c8dcSWojciech Macek 	    s2m_errors_disable, s2m_aborts_disable)) {
27597902c8dcSWojciech Macek 		device_printf(adapter->dev,
27607902c8dcSWojciech Macek 		    "al_udma_unit_int_config failed!.\n");
27617902c8dcSWojciech Macek 		return (EIO);
27627902c8dcSWojciech Macek 	}
27637902c8dcSWojciech Macek 	adapter->int_mode = int_mode;
27647902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "using %s interrupt mode\n",
27657902c8dcSWojciech Macek 	    int_mode == AL_IOFIC_MODE_LEGACY ? "INTx" :
27667902c8dcSWojciech Macek 	    int_mode == AL_IOFIC_MODE_MSIX_PER_Q ? "MSI-X per Queue" : "Unknown");
27677902c8dcSWojciech Macek 	/* set interrupt moderation resolution to 15us */
27687902c8dcSWojciech Macek 	al_iofic_moder_res_config(&((struct unit_regs *)(adapter->udma_base))->gen.interrupt_regs.main_iofic, AL_INT_GROUP_B, 15);
27697902c8dcSWojciech Macek 	al_iofic_moder_res_config(&((struct unit_regs *)(adapter->udma_base))->gen.interrupt_regs.main_iofic, AL_INT_GROUP_C, 15);
27707902c8dcSWojciech Macek 	/* by default interrupt coalescing is disabled */
27717902c8dcSWojciech Macek 	adapter->tx_usecs = 0;
27727902c8dcSWojciech Macek 	adapter->rx_usecs = 0;
27737902c8dcSWojciech Macek 
27747902c8dcSWojciech Macek 	return (0);
27757902c8dcSWojciech Macek }
27767902c8dcSWojciech Macek 
27777902c8dcSWojciech Macek /*
27787902c8dcSWojciech Macek  * ethtool_rxfh_indir_default - get default value for RX flow hash indirection
27797902c8dcSWojciech Macek  * @index: Index in RX flow hash indirection table
27807902c8dcSWojciech Macek  * @n_rx_rings: Number of RX rings to use
27817902c8dcSWojciech Macek  *
27827902c8dcSWojciech Macek  * This function provides the default policy for RX flow hash indirection.
27837902c8dcSWojciech Macek  */
27847902c8dcSWojciech Macek static inline uint32_t
27857902c8dcSWojciech Macek ethtool_rxfh_indir_default(uint32_t index, uint32_t n_rx_rings)
27867902c8dcSWojciech Macek {
27877902c8dcSWojciech Macek 
27887902c8dcSWojciech Macek 	return (index % n_rx_rings);
27897902c8dcSWojciech Macek }
27907902c8dcSWojciech Macek 
27917902c8dcSWojciech Macek static void*
27927902c8dcSWojciech Macek al_eth_update_stats(struct al_eth_adapter *adapter)
27937902c8dcSWojciech Macek {
27947902c8dcSWojciech Macek 	struct al_eth_mac_stats *mac_stats = &adapter->mac_stats;
27957902c8dcSWojciech Macek 
27967902c8dcSWojciech Macek 	if (adapter->up == 0)
27977902c8dcSWojciech Macek 		return (NULL);
27987902c8dcSWojciech Macek 
27997902c8dcSWojciech Macek 	al_eth_mac_stats_get(&adapter->hal_adapter, mac_stats);
28007902c8dcSWojciech Macek 
28017902c8dcSWojciech Macek 	return (NULL);
28027902c8dcSWojciech Macek }
28037902c8dcSWojciech Macek 
28047902c8dcSWojciech Macek static uint64_t
2805bc14c73bSJustin Hibbits al_get_counter(if_t ifp, ift_counter cnt)
28067902c8dcSWojciech Macek {
28077902c8dcSWojciech Macek 	struct al_eth_adapter *adapter;
28087902c8dcSWojciech Macek 	struct al_eth_mac_stats *mac_stats;
28097902c8dcSWojciech Macek 	uint64_t rv;
28107902c8dcSWojciech Macek 
28117902c8dcSWojciech Macek 	adapter = if_getsoftc(ifp);
28127902c8dcSWojciech Macek 	mac_stats = &adapter->mac_stats;
28137902c8dcSWojciech Macek 
28147902c8dcSWojciech Macek 	switch (cnt) {
28157902c8dcSWojciech Macek 	case IFCOUNTER_IPACKETS:
28167902c8dcSWojciech Macek 		return (mac_stats->aFramesReceivedOK); /* including pause frames */
28177902c8dcSWojciech Macek 	case IFCOUNTER_OPACKETS:
28187902c8dcSWojciech Macek 		return (mac_stats->aFramesTransmittedOK);
28197902c8dcSWojciech Macek 	case IFCOUNTER_IBYTES:
28207902c8dcSWojciech Macek 		return (mac_stats->aOctetsReceivedOK);
28217902c8dcSWojciech Macek 	case IFCOUNTER_OBYTES:
28227902c8dcSWojciech Macek 		return (mac_stats->aOctetsTransmittedOK);
28237902c8dcSWojciech Macek 	case IFCOUNTER_IMCASTS:
28247902c8dcSWojciech Macek 		return (mac_stats->ifInMulticastPkts);
28257902c8dcSWojciech Macek 	case IFCOUNTER_OMCASTS:
28267902c8dcSWojciech Macek 		return (mac_stats->ifOutMulticastPkts);
28277902c8dcSWojciech Macek 	case IFCOUNTER_COLLISIONS:
28287902c8dcSWojciech Macek 		return (0);
28297902c8dcSWojciech Macek 	case IFCOUNTER_IQDROPS:
28307902c8dcSWojciech Macek 		return (mac_stats->etherStatsDropEvents);
28317902c8dcSWojciech Macek 	case IFCOUNTER_IERRORS:
28327902c8dcSWojciech Macek 		rv = mac_stats->ifInErrors +
28337902c8dcSWojciech Macek 		    mac_stats->etherStatsUndersizePkts + /* good but short */
28347902c8dcSWojciech Macek 		    mac_stats->etherStatsFragments + /* short and bad*/
28357902c8dcSWojciech Macek 		    mac_stats->etherStatsJabbers + /* with crc errors */
28367902c8dcSWojciech Macek 		    mac_stats->etherStatsOversizePkts +
28377902c8dcSWojciech Macek 		    mac_stats->aFrameCheckSequenceErrors +
28387902c8dcSWojciech Macek 		    mac_stats->aAlignmentErrors;
28397902c8dcSWojciech Macek 		return (rv);
28407902c8dcSWojciech Macek 	case IFCOUNTER_OERRORS:
28417902c8dcSWojciech Macek 		return (mac_stats->ifOutErrors);
28427902c8dcSWojciech Macek 	default:
28437902c8dcSWojciech Macek 		return (if_get_counter_default(ifp, cnt));
28447902c8dcSWojciech Macek 	}
28457902c8dcSWojciech Macek }
28467902c8dcSWojciech Macek 
284757d36163SGleb Smirnoff static u_int
284857d36163SGleb Smirnoff al_count_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
284957d36163SGleb Smirnoff {
285057d36163SGleb Smirnoff 	unsigned char *mac;
285157d36163SGleb Smirnoff 
285257d36163SGleb Smirnoff 	mac = LLADDR(sdl);
285357d36163SGleb Smirnoff 	/* default mc address inside mac address */
285457d36163SGleb Smirnoff 	if (mac[3] != 0 && mac[4] != 0 && mac[5] != 1)
285557d36163SGleb Smirnoff 		return (1);
285657d36163SGleb Smirnoff 	else
285757d36163SGleb Smirnoff 		return (0);
285857d36163SGleb Smirnoff }
285957d36163SGleb Smirnoff 
286057d36163SGleb Smirnoff static u_int
286157d36163SGleb Smirnoff al_program_addr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
286257d36163SGleb Smirnoff {
286357d36163SGleb Smirnoff 	struct al_eth_adapter *adapter = arg;
286457d36163SGleb Smirnoff 
286557d36163SGleb Smirnoff 	al_eth_mac_table_unicast_add(adapter,
286657d36163SGleb Smirnoff 	    AL_ETH_MAC_TABLE_UNICAST_IDX_BASE + 1 + cnt, 1);
286757d36163SGleb Smirnoff 
286857d36163SGleb Smirnoff 	return (1);
286957d36163SGleb Smirnoff }
287057d36163SGleb Smirnoff 
28717902c8dcSWojciech Macek /*
28727902c8dcSWojciech Macek  *  Unicast, Multicast and Promiscuous mode set
28737902c8dcSWojciech Macek  *
28747902c8dcSWojciech Macek  *  The set_rx_mode entry point is called whenever the unicast or multicast
28757902c8dcSWojciech Macek  *  address lists or the network interface flags are updated.  This routine is
28767902c8dcSWojciech Macek  *  responsible for configuring the hardware for proper unicast, multicast,
28777902c8dcSWojciech Macek  *  promiscuous mode, and all-multi behavior.
28787902c8dcSWojciech Macek  */
28797902c8dcSWojciech Macek static void
28807902c8dcSWojciech Macek al_eth_set_rx_mode(struct al_eth_adapter *adapter)
28817902c8dcSWojciech Macek {
2882bc14c73bSJustin Hibbits 	if_t ifp = adapter->netdev;
288357d36163SGleb Smirnoff 	int mc, uc;
28847902c8dcSWojciech Macek 	uint8_t i;
28857902c8dcSWojciech Macek 
288657d36163SGleb Smirnoff 	/* XXXGL: why generic count won't work? */
288757d36163SGleb Smirnoff 	mc = if_foreach_llmaddr(ifp, al_count_maddr, NULL);
288857d36163SGleb Smirnoff 	uc = if_lladdr_count(ifp);
28897902c8dcSWojciech Macek 
2890bc14c73bSJustin Hibbits 	if ((if_getflags(ifp) & IFF_PROMISC) != 0) {
28917902c8dcSWojciech Macek 		al_eth_mac_table_promiscuous_set(adapter, true);
28927902c8dcSWojciech Macek 	} else {
2893bc14c73bSJustin Hibbits 		if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
28947902c8dcSWojciech Macek 			/* This interface is in all-multicasts mode (used by multicast routers). */
28957902c8dcSWojciech Macek 			al_eth_mac_table_all_multicast_add(adapter,
28967902c8dcSWojciech Macek 			    AL_ETH_MAC_TABLE_ALL_MULTICAST_IDX, 1);
28977902c8dcSWojciech Macek 		} else {
28987902c8dcSWojciech Macek 			if (mc == 0) {
28997902c8dcSWojciech Macek 				al_eth_mac_table_entry_clear(adapter,
29007902c8dcSWojciech Macek 				    AL_ETH_MAC_TABLE_ALL_MULTICAST_IDX);
29017902c8dcSWojciech Macek 			} else {
29027902c8dcSWojciech Macek 				al_eth_mac_table_all_multicast_add(adapter,
29037902c8dcSWojciech Macek 				    AL_ETH_MAC_TABLE_ALL_MULTICAST_IDX, 1);
29047902c8dcSWojciech Macek 			}
29057902c8dcSWojciech Macek 		}
29067902c8dcSWojciech Macek 		if (uc != 0) {
29077902c8dcSWojciech Macek 			i = AL_ETH_MAC_TABLE_UNICAST_IDX_BASE + 1;
29087902c8dcSWojciech Macek 			if (uc > AL_ETH_MAC_TABLE_UNICAST_MAX_COUNT) {
29097902c8dcSWojciech Macek 				/*
29107902c8dcSWojciech Macek 				 * In this case there are more addresses then
29117902c8dcSWojciech Macek 				 * entries in the mac table - set promiscuous
29127902c8dcSWojciech Macek 				 */
29137902c8dcSWojciech Macek 				al_eth_mac_table_promiscuous_set(adapter, true);
29147902c8dcSWojciech Macek 				return;
29157902c8dcSWojciech Macek 			}
29167902c8dcSWojciech Macek 
29177902c8dcSWojciech Macek 			/* clear the last configuration */
29187902c8dcSWojciech Macek 			while (i < (AL_ETH_MAC_TABLE_UNICAST_IDX_BASE +
29197902c8dcSWojciech Macek 				    AL_ETH_MAC_TABLE_UNICAST_MAX_COUNT)) {
29207902c8dcSWojciech Macek 				al_eth_mac_table_entry_clear(adapter, i);
29217902c8dcSWojciech Macek 				i++;
29227902c8dcSWojciech Macek 			}
29237902c8dcSWojciech Macek 
29247902c8dcSWojciech Macek 			/* set new addresses */
292557d36163SGleb Smirnoff 			if_foreach_lladdr(ifp, al_program_addr, adapter);
29267902c8dcSWojciech Macek 		}
29277902c8dcSWojciech Macek 		al_eth_mac_table_promiscuous_set(adapter, false);
29287902c8dcSWojciech Macek 	}
29297902c8dcSWojciech Macek }
29307902c8dcSWojciech Macek 
29317902c8dcSWojciech Macek static void
29327902c8dcSWojciech Macek al_eth_config_rx_fwd(struct al_eth_adapter *adapter)
29337902c8dcSWojciech Macek {
29347902c8dcSWojciech Macek 	struct al_eth_fwd_ctrl_table_entry entry;
29357902c8dcSWojciech Macek 	int i;
29367902c8dcSWojciech Macek 
29377902c8dcSWojciech Macek 	/* let priority be equal to pbits */
29387902c8dcSWojciech Macek 	for (i = 0; i < AL_ETH_FWD_PBITS_TABLE_NUM; i++)
29397902c8dcSWojciech Macek 		al_eth_fwd_pbits_table_set(&adapter->hal_adapter, i, i);
29407902c8dcSWojciech Macek 
29417902c8dcSWojciech Macek 	/* map priority to queue index, queue id = priority/2 */
29427902c8dcSWojciech Macek 	for (i = 0; i < AL_ETH_FWD_PRIO_TABLE_NUM; i++)
29437902c8dcSWojciech Macek 		al_eth_fwd_priority_table_set(&adapter->hal_adapter, i, i >> 1);
29447902c8dcSWojciech Macek 
29457902c8dcSWojciech Macek 	entry.prio_sel = AL_ETH_CTRL_TABLE_PRIO_SEL_VAL_0;
29467902c8dcSWojciech Macek 	entry.queue_sel_1 = AL_ETH_CTRL_TABLE_QUEUE_SEL_1_THASH_TABLE;
29477902c8dcSWojciech Macek 	entry.queue_sel_2 = AL_ETH_CTRL_TABLE_QUEUE_SEL_2_NO_PRIO;
29487902c8dcSWojciech Macek 	entry.udma_sel = AL_ETH_CTRL_TABLE_UDMA_SEL_MAC_TABLE;
2949463edaf4SJohn Baldwin 	entry.filter = false;
29507902c8dcSWojciech Macek 
2951463edaf4SJohn Baldwin 	al_eth_ctrl_table_def_set(&adapter->hal_adapter, AL_FALSE, &entry);
29527902c8dcSWojciech Macek 
29537902c8dcSWojciech Macek 	/*
29547902c8dcSWojciech Macek 	 * By default set the mac table to forward all unicast packets to our
29557902c8dcSWojciech Macek 	 * MAC address and all broadcast. all the rest will be dropped.
29567902c8dcSWojciech Macek 	 */
29577902c8dcSWojciech Macek 	al_eth_mac_table_unicast_add(adapter, AL_ETH_MAC_TABLE_UNICAST_IDX_BASE,
295857d36163SGleb Smirnoff 	    1);
29597902c8dcSWojciech Macek 	al_eth_mac_table_broadcast_add(adapter, AL_ETH_MAC_TABLE_BROADCAST_IDX, 1);
29607902c8dcSWojciech Macek 	al_eth_mac_table_promiscuous_set(adapter, false);
29617902c8dcSWojciech Macek 
29627902c8dcSWojciech Macek 	/* set toeplitz hash keys */
29637902c8dcSWojciech Macek 	for (i = 0; i < sizeof(adapter->toeplitz_hash_key); i++)
29647902c8dcSWojciech Macek 		*((uint8_t*)adapter->toeplitz_hash_key + i) = (uint8_t)random();
29657902c8dcSWojciech Macek 
29667902c8dcSWojciech Macek 	for (i = 0; i < AL_ETH_RX_HASH_KEY_NUM; i++)
29677902c8dcSWojciech Macek 		al_eth_hash_key_set(&adapter->hal_adapter, i,
29687902c8dcSWojciech Macek 		    htonl(adapter->toeplitz_hash_key[i]));
29697902c8dcSWojciech Macek 
29707902c8dcSWojciech Macek 	for (i = 0; i < AL_ETH_RX_RSS_TABLE_SIZE; i++) {
29717902c8dcSWojciech Macek 		adapter->rss_ind_tbl[i] = ethtool_rxfh_indir_default(i,
29727902c8dcSWojciech Macek 		    AL_ETH_NUM_QUEUES);
29737902c8dcSWojciech Macek 		al_eth_set_thash_table_entry(adapter, i, 0,
29747902c8dcSWojciech Macek 		    adapter->rss_ind_tbl[i]);
29757902c8dcSWojciech Macek 	}
29767902c8dcSWojciech Macek 
29777902c8dcSWojciech Macek 	al_eth_fsm_table_init(adapter);
29787902c8dcSWojciech Macek }
29797902c8dcSWojciech Macek 
29807902c8dcSWojciech Macek static void
29817902c8dcSWojciech Macek al_eth_req_rx_buff_size(struct al_eth_adapter *adapter, int size)
29827902c8dcSWojciech Macek {
29837902c8dcSWojciech Macek 
29847902c8dcSWojciech Macek 	/*
29857902c8dcSWojciech Macek 	* Determine the correct mbuf pool
29867902c8dcSWojciech Macek 	* for doing jumbo frames
29877902c8dcSWojciech Macek 	* Try from the smallest up to maximum supported
29887902c8dcSWojciech Macek 	*/
29897902c8dcSWojciech Macek 	adapter->rx_mbuf_sz = MCLBYTES;
29907902c8dcSWojciech Macek 	if (size > 2048) {
29917902c8dcSWojciech Macek 		if (adapter->max_rx_buff_alloc_size > 2048)
29927902c8dcSWojciech Macek 			adapter->rx_mbuf_sz = MJUMPAGESIZE;
29937902c8dcSWojciech Macek 		else
29947902c8dcSWojciech Macek 			return;
29957902c8dcSWojciech Macek 	}
29967902c8dcSWojciech Macek 	if (size > 4096) {
29977902c8dcSWojciech Macek 		if (adapter->max_rx_buff_alloc_size > 4096)
29987902c8dcSWojciech Macek 			adapter->rx_mbuf_sz = MJUM9BYTES;
29997902c8dcSWojciech Macek 		else
30007902c8dcSWojciech Macek 			return;
30017902c8dcSWojciech Macek 	}
30027902c8dcSWojciech Macek 	if (size > 9216) {
30037902c8dcSWojciech Macek 		if (adapter->max_rx_buff_alloc_size > 9216)
30047902c8dcSWojciech Macek 			adapter->rx_mbuf_sz = MJUM16BYTES;
30057902c8dcSWojciech Macek 		else
30067902c8dcSWojciech Macek 			return;
30077902c8dcSWojciech Macek 	}
30087902c8dcSWojciech Macek }
30097902c8dcSWojciech Macek 
30107902c8dcSWojciech Macek static int
30117902c8dcSWojciech Macek al_eth_change_mtu(struct al_eth_adapter *adapter, int new_mtu)
30127902c8dcSWojciech Macek {
30137902c8dcSWojciech Macek 	int max_frame = new_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN +
30147902c8dcSWojciech Macek 	    ETHER_VLAN_ENCAP_LEN;
30157902c8dcSWojciech Macek 
30167902c8dcSWojciech Macek 	al_eth_req_rx_buff_size(adapter, new_mtu);
30177902c8dcSWojciech Macek 
30187902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "set MTU to %d\n", new_mtu);
30197902c8dcSWojciech Macek 	al_eth_rx_pkt_limit_config(&adapter->hal_adapter,
30207902c8dcSWojciech Macek 	    AL_ETH_MIN_FRAME_LEN, max_frame);
30217902c8dcSWojciech Macek 
30227902c8dcSWojciech Macek 	al_eth_tso_mss_config(&adapter->hal_adapter, 0, new_mtu - 100);
30237902c8dcSWojciech Macek 
30247902c8dcSWojciech Macek 	return (0);
30257902c8dcSWojciech Macek }
30267902c8dcSWojciech Macek 
30277902c8dcSWojciech Macek static int
30287902c8dcSWojciech Macek al_eth_check_mtu(struct al_eth_adapter *adapter, int new_mtu)
30297902c8dcSWojciech Macek {
30307902c8dcSWojciech Macek 	int max_frame = new_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN + ETHER_VLAN_ENCAP_LEN;
30317902c8dcSWojciech Macek 
30327902c8dcSWojciech Macek 	if ((new_mtu < AL_ETH_MIN_FRAME_LEN) ||
30337902c8dcSWojciech Macek 	    (max_frame > AL_ETH_MAX_FRAME_LEN)) {
30347902c8dcSWojciech Macek 		return (EINVAL);
30357902c8dcSWojciech Macek 	}
30367902c8dcSWojciech Macek 
30377902c8dcSWojciech Macek 	return (0);
30387902c8dcSWojciech Macek }
30397902c8dcSWojciech Macek 
30407902c8dcSWojciech Macek static int
30417902c8dcSWojciech Macek al_eth_udma_queue_enable(struct al_eth_adapter *adapter, enum al_udma_type type,
30427902c8dcSWojciech Macek     int qid)
30437902c8dcSWojciech Macek {
30447902c8dcSWojciech Macek 	int rc = 0;
30457902c8dcSWojciech Macek 	char *name = (type == UDMA_TX) ? "Tx" : "Rx";
30467902c8dcSWojciech Macek 	struct al_udma_q_params *q_params;
30477902c8dcSWojciech Macek 
30487902c8dcSWojciech Macek 	if (type == UDMA_TX)
30497902c8dcSWojciech Macek 		q_params = &adapter->tx_ring[qid].q_params;
30507902c8dcSWojciech Macek 	else
30517902c8dcSWojciech Macek 		q_params = &adapter->rx_ring[qid].q_params;
30527902c8dcSWojciech Macek 
30537902c8dcSWojciech Macek 	rc = al_eth_queue_config(&adapter->hal_adapter, type, qid, q_params);
30547902c8dcSWojciech Macek 	if (rc < 0) {
30557902c8dcSWojciech Macek 		device_printf(adapter->dev, "config %s queue %u failed\n", name,
30567902c8dcSWojciech Macek 		    qid);
30577902c8dcSWojciech Macek 		return (rc);
30587902c8dcSWojciech Macek 	}
30597902c8dcSWojciech Macek 	return (rc);
30607902c8dcSWojciech Macek }
30617902c8dcSWojciech Macek 
30627902c8dcSWojciech Macek static int
30637902c8dcSWojciech Macek al_eth_udma_queues_enable_all(struct al_eth_adapter *adapter)
30647902c8dcSWojciech Macek {
30657902c8dcSWojciech Macek 	int i;
30667902c8dcSWojciech Macek 
30677902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_tx_queues; i++)
30687902c8dcSWojciech Macek 		al_eth_udma_queue_enable(adapter, UDMA_TX, i);
30697902c8dcSWojciech Macek 
30707902c8dcSWojciech Macek 	for (i = 0; i < adapter->num_rx_queues; i++)
30717902c8dcSWojciech Macek 		al_eth_udma_queue_enable(adapter, UDMA_RX, i);
30727902c8dcSWojciech Macek 
30737902c8dcSWojciech Macek 	return (0);
30747902c8dcSWojciech Macek }
30757902c8dcSWojciech Macek 
30767902c8dcSWojciech Macek static void
30777902c8dcSWojciech Macek al_eth_up_complete(struct al_eth_adapter *adapter)
30787902c8dcSWojciech Macek {
30797902c8dcSWojciech Macek 
30807902c8dcSWojciech Macek 	al_eth_configure_int_mode(adapter);
30817902c8dcSWojciech Macek 	al_eth_config_rx_fwd(adapter);
3082f46a05b5SJustin Hibbits 	al_eth_change_mtu(adapter, if_getmtu(adapter->netdev));
30837902c8dcSWojciech Macek 	al_eth_udma_queues_enable_all(adapter);
30847902c8dcSWojciech Macek 	al_eth_refill_all_rx_bufs(adapter);
30857902c8dcSWojciech Macek 	al_eth_interrupts_unmask(adapter);
30867902c8dcSWojciech Macek 
30877902c8dcSWojciech Macek 	/* enable forwarding interrupts from eth through pci end point */
30887902c8dcSWojciech Macek 	if ((adapter->board_type == ALPINE_FPGA_NIC) ||
30897902c8dcSWojciech Macek 	    (adapter->board_type == ALPINE_NIC)) {
30907902c8dcSWojciech Macek 		al_eth_forward_int_config((uint32_t*)adapter->internal_pcie_base +
30917902c8dcSWojciech Macek 		    AL_REG_OFFSET_FORWARD_INTR, AL_EN_FORWARD_INTR);
30927902c8dcSWojciech Macek 	}
30937902c8dcSWojciech Macek 
30947902c8dcSWojciech Macek 	al_eth_flow_ctrl_enable(adapter);
30957902c8dcSWojciech Macek 
30967902c8dcSWojciech Macek 	mtx_lock(&adapter->stats_mtx);
30977902c8dcSWojciech Macek 	callout_reset(&adapter->stats_callout, hz, al_tick_stats, (void*)adapter);
30987902c8dcSWojciech Macek 	mtx_unlock(&adapter->stats_mtx);
30997902c8dcSWojciech Macek 
31007902c8dcSWojciech Macek 	al_eth_mac_start(&adapter->hal_adapter);
31017902c8dcSWojciech Macek }
31027902c8dcSWojciech Macek 
31037902c8dcSWojciech Macek static int
3104bc14c73bSJustin Hibbits al_media_update(if_t ifp)
31057902c8dcSWojciech Macek {
3106bc14c73bSJustin Hibbits 	struct al_eth_adapter *adapter = if_getsoftc(ifp);
31077902c8dcSWojciech Macek 
3108bc14c73bSJustin Hibbits 	if ((if_getflags(ifp) & IFF_UP) != 0)
31097902c8dcSWojciech Macek 		mii_mediachg(adapter->mii);
31107902c8dcSWojciech Macek 
31117902c8dcSWojciech Macek 	return (0);
31127902c8dcSWojciech Macek }
31137902c8dcSWojciech Macek 
31147902c8dcSWojciech Macek static void
3115bc14c73bSJustin Hibbits al_media_status(if_t ifp, struct ifmediareq *ifmr)
31167902c8dcSWojciech Macek {
3117bc14c73bSJustin Hibbits 	struct al_eth_adapter *sc = if_getsoftc(ifp);
31187902c8dcSWojciech Macek 	struct mii_data *mii;
31197902c8dcSWojciech Macek 
31207902c8dcSWojciech Macek 	if (sc->mii == NULL) {
31217902c8dcSWojciech Macek 		ifmr->ifm_active = IFM_ETHER | IFM_NONE;
31227902c8dcSWojciech Macek 		ifmr->ifm_status = 0;
31237902c8dcSWojciech Macek 
31247902c8dcSWojciech Macek 		return;
31257902c8dcSWojciech Macek 	}
31267902c8dcSWojciech Macek 
31277902c8dcSWojciech Macek 	mii = sc->mii;
31287902c8dcSWojciech Macek 	mii_pollstat(mii);
31297902c8dcSWojciech Macek 
31307902c8dcSWojciech Macek 	ifmr->ifm_active = mii->mii_media_active;
31317902c8dcSWojciech Macek 	ifmr->ifm_status = mii->mii_media_status;
31327902c8dcSWojciech Macek }
31337902c8dcSWojciech Macek 
31347902c8dcSWojciech Macek static void
31357902c8dcSWojciech Macek al_tick(void *arg)
31367902c8dcSWojciech Macek {
31377902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = arg;
31387902c8dcSWojciech Macek 
31397902c8dcSWojciech Macek 	mii_tick(adapter->mii);
31407902c8dcSWojciech Macek 
31417902c8dcSWojciech Macek 	/* Schedule another timeout one second from now */
31427902c8dcSWojciech Macek 	callout_schedule(&adapter->wd_callout, hz);
31437902c8dcSWojciech Macek }
31447902c8dcSWojciech Macek 
31457902c8dcSWojciech Macek static void
31467902c8dcSWojciech Macek al_tick_stats(void *arg)
31477902c8dcSWojciech Macek {
31487902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = arg;
31497902c8dcSWojciech Macek 
31507902c8dcSWojciech Macek 	al_eth_update_stats(adapter);
31517902c8dcSWojciech Macek 
31527902c8dcSWojciech Macek 	callout_schedule(&adapter->stats_callout, hz);
31537902c8dcSWojciech Macek }
31547902c8dcSWojciech Macek 
31557902c8dcSWojciech Macek static int
31567902c8dcSWojciech Macek al_eth_up(struct al_eth_adapter *adapter)
31577902c8dcSWojciech Macek {
3158bc14c73bSJustin Hibbits 	if_t ifp = adapter->netdev;
31597902c8dcSWojciech Macek 	int rc;
31607902c8dcSWojciech Macek 
31617902c8dcSWojciech Macek 	if (adapter->up)
31627902c8dcSWojciech Macek 		return (0);
31637902c8dcSWojciech Macek 
31647902c8dcSWojciech Macek 	if ((adapter->flags & AL_ETH_FLAG_RESET_REQUESTED) != 0) {
31657902c8dcSWojciech Macek 		al_eth_function_reset(adapter);
31667902c8dcSWojciech Macek 		adapter->flags &= ~AL_ETH_FLAG_RESET_REQUESTED;
31677902c8dcSWojciech Macek 	}
31687902c8dcSWojciech Macek 
3169bc14c73bSJustin Hibbits 	if_sethwassist(ifp, 0);
3170bc14c73bSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_TSO) != 0)
3171bc14c73bSJustin Hibbits 		if_sethwassistbits(ifp, CSUM_TSO, 0);
3172bc14c73bSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_TXCSUM) != 0)
3173bc14c73bSJustin Hibbits 		if_sethwassistbits(ifp, (CSUM_TCP | CSUM_UDP), 0);
3174bc14c73bSJustin Hibbits 	if ((if_getcapenable(ifp) & IFCAP_TXCSUM_IPV6) != 0)
3175bc14c73bSJustin Hibbits 		if_sethwassistbits(ifp, (CSUM_TCP_IPV6 | CSUM_UDP_IPV6), 0);
31767902c8dcSWojciech Macek 
31777902c8dcSWojciech Macek 	al_eth_serdes_init(adapter);
31787902c8dcSWojciech Macek 
31797902c8dcSWojciech Macek 	rc = al_eth_hw_init(adapter);
31807902c8dcSWojciech Macek 	if (rc != 0)
31817902c8dcSWojciech Macek 		goto err_hw_init_open;
31827902c8dcSWojciech Macek 
31837902c8dcSWojciech Macek 	rc = al_eth_setup_int_mode(adapter);
31847902c8dcSWojciech Macek 	if (rc != 0) {
31857902c8dcSWojciech Macek 		device_printf(adapter->dev,
31867902c8dcSWojciech Macek 		    "%s failed at setup interrupt mode!\n", __func__);
31877902c8dcSWojciech Macek 		goto err_setup_int;
31887902c8dcSWojciech Macek 	}
31897902c8dcSWojciech Macek 
31907902c8dcSWojciech Macek 	/* allocate transmit descriptors */
31917902c8dcSWojciech Macek 	rc = al_eth_setup_all_tx_resources(adapter);
31927902c8dcSWojciech Macek 	if (rc != 0)
31937902c8dcSWojciech Macek 		goto err_setup_tx;
31947902c8dcSWojciech Macek 
31957902c8dcSWojciech Macek 	/* allocate receive descriptors */
31967902c8dcSWojciech Macek 	rc = al_eth_setup_all_rx_resources(adapter);
31977902c8dcSWojciech Macek 	if (rc != 0)
31987902c8dcSWojciech Macek 		goto err_setup_rx;
31997902c8dcSWojciech Macek 
32007902c8dcSWojciech Macek 	rc = al_eth_request_irq(adapter);
32017902c8dcSWojciech Macek 	if (rc != 0)
32027902c8dcSWojciech Macek 		goto err_req_irq;
32037902c8dcSWojciech Macek 
32047902c8dcSWojciech Macek 	al_eth_up_complete(adapter);
32057902c8dcSWojciech Macek 
32067902c8dcSWojciech Macek 	adapter->up = true;
32077902c8dcSWojciech Macek 
32087902c8dcSWojciech Macek 	if (adapter->mac_mode == AL_ETH_MAC_MODE_10GbE_Serial)
3209f46a05b5SJustin Hibbits 		if_link_state_change(adapter->netdev, LINK_STATE_UP);
32107902c8dcSWojciech Macek 
32117902c8dcSWojciech Macek 	if (adapter->mac_mode == AL_ETH_MAC_MODE_RGMII) {
32127902c8dcSWojciech Macek 		mii_mediachg(adapter->mii);
32137902c8dcSWojciech Macek 
32147902c8dcSWojciech Macek 		/* Schedule watchdog timeout */
32157902c8dcSWojciech Macek 		mtx_lock(&adapter->wd_mtx);
32167902c8dcSWojciech Macek 		callout_reset(&adapter->wd_callout, hz, al_tick, adapter);
32177902c8dcSWojciech Macek 		mtx_unlock(&adapter->wd_mtx);
32187902c8dcSWojciech Macek 
32197902c8dcSWojciech Macek 		mii_pollstat(adapter->mii);
32207902c8dcSWojciech Macek 	}
32217902c8dcSWojciech Macek 
32227902c8dcSWojciech Macek 	return (rc);
32237902c8dcSWojciech Macek 
32247902c8dcSWojciech Macek err_req_irq:
32257902c8dcSWojciech Macek 	al_eth_free_all_rx_resources(adapter);
32267902c8dcSWojciech Macek err_setup_rx:
32277902c8dcSWojciech Macek 	al_eth_free_all_tx_resources(adapter);
32287902c8dcSWojciech Macek err_setup_tx:
32297902c8dcSWojciech Macek 	al_eth_free_irq(adapter);
32307902c8dcSWojciech Macek err_setup_int:
32317902c8dcSWojciech Macek 	al_eth_hw_stop(adapter);
32327902c8dcSWojciech Macek err_hw_init_open:
32337902c8dcSWojciech Macek 	al_eth_function_reset(adapter);
32347902c8dcSWojciech Macek 
32357902c8dcSWojciech Macek 	return (rc);
32367902c8dcSWojciech Macek }
32377902c8dcSWojciech Macek 
32387902c8dcSWojciech Macek static int
32397902c8dcSWojciech Macek al_shutdown(device_t dev)
32407902c8dcSWojciech Macek {
32417902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = device_get_softc(dev);
32427902c8dcSWojciech Macek 
32437902c8dcSWojciech Macek 	al_eth_down(adapter);
32447902c8dcSWojciech Macek 
32457902c8dcSWojciech Macek 	return (0);
32467902c8dcSWojciech Macek }
32477902c8dcSWojciech Macek 
32487902c8dcSWojciech Macek static void
32497902c8dcSWojciech Macek al_eth_down(struct al_eth_adapter *adapter)
32507902c8dcSWojciech Macek {
32517902c8dcSWojciech Macek 
32527902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev, "al_eth_down: begin\n");
32537902c8dcSWojciech Macek 
32547902c8dcSWojciech Macek 	adapter->up = false;
32557902c8dcSWojciech Macek 
32567902c8dcSWojciech Macek 	mtx_lock(&adapter->wd_mtx);
32577902c8dcSWojciech Macek 	callout_stop(&adapter->wd_callout);
32587902c8dcSWojciech Macek 	mtx_unlock(&adapter->wd_mtx);
32597902c8dcSWojciech Macek 
32607902c8dcSWojciech Macek 	al_eth_disable_int_sync(adapter);
32617902c8dcSWojciech Macek 
32627902c8dcSWojciech Macek 	mtx_lock(&adapter->stats_mtx);
32637902c8dcSWojciech Macek 	callout_stop(&adapter->stats_callout);
32647902c8dcSWojciech Macek 	mtx_unlock(&adapter->stats_mtx);
32657902c8dcSWojciech Macek 
32667902c8dcSWojciech Macek 	al_eth_free_irq(adapter);
32677902c8dcSWojciech Macek 	al_eth_hw_stop(adapter);
32687902c8dcSWojciech Macek 
32697902c8dcSWojciech Macek 	al_eth_free_all_tx_resources(adapter);
32707902c8dcSWojciech Macek 	al_eth_free_all_rx_resources(adapter);
32717902c8dcSWojciech Macek }
32727902c8dcSWojciech Macek 
32737902c8dcSWojciech Macek static int
3274bc14c73bSJustin Hibbits al_ioctl(if_t ifp, u_long command, caddr_t data)
32757902c8dcSWojciech Macek {
3276bc14c73bSJustin Hibbits 	struct al_eth_adapter	*adapter = if_getsoftc(ifp);
32777902c8dcSWojciech Macek 	struct ifreq		*ifr = (struct ifreq *)data;
32787902c8dcSWojciech Macek 	int			error = 0;
32797902c8dcSWojciech Macek 
32807902c8dcSWojciech Macek 	switch (command) {
32817902c8dcSWojciech Macek 	case SIOCSIFMTU:
32827902c8dcSWojciech Macek 	{
32837902c8dcSWojciech Macek 		error = al_eth_check_mtu(adapter, ifr->ifr_mtu);
32847902c8dcSWojciech Macek 		if (error != 0) {
32857902c8dcSWojciech Macek 			device_printf(adapter->dev, "ioctl wrong mtu %u\n",
3286f46a05b5SJustin Hibbits 			    if_getmtu(adapter->netdev));
32877902c8dcSWojciech Macek 			break;
32887902c8dcSWojciech Macek 		}
32897902c8dcSWojciech Macek 
3290bc14c73bSJustin Hibbits 		if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
3291f46a05b5SJustin Hibbits 		if_setmtu(adapter->netdev, ifr->ifr_mtu);
32927902c8dcSWojciech Macek 		al_init(adapter);
32937902c8dcSWojciech Macek 		break;
32947902c8dcSWojciech Macek 	}
32957902c8dcSWojciech Macek 	case SIOCSIFFLAGS:
3296bc14c73bSJustin Hibbits 		if ((if_getflags(ifp) & IFF_UP) != 0) {
3297bc14c73bSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
3298bc14c73bSJustin Hibbits 				if (((if_getflags(ifp) ^ adapter->if_flags) &
32997902c8dcSWojciech Macek 				    (IFF_PROMISC | IFF_ALLMULTI)) != 0) {
33007902c8dcSWojciech Macek 					device_printf_dbg(adapter->dev,
33017902c8dcSWojciech Macek 					    "ioctl promisc/allmulti\n");
33027902c8dcSWojciech Macek 					al_eth_set_rx_mode(adapter);
33037902c8dcSWojciech Macek 				}
33047902c8dcSWojciech Macek 			} else {
33057902c8dcSWojciech Macek 				error = al_eth_up(adapter);
33067902c8dcSWojciech Macek 				if (error == 0)
3307bc14c73bSJustin Hibbits 					if_setdrvflagbits(ifp, IFF_DRV_RUNNING, 0);
33087902c8dcSWojciech Macek 			}
33097902c8dcSWojciech Macek 		} else {
3310bc14c73bSJustin Hibbits 			if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
33117902c8dcSWojciech Macek 				al_eth_down(adapter);
3312bc14c73bSJustin Hibbits 				if_setdrvflagbits(ifp, 0, IFF_DRV_RUNNING);
33137902c8dcSWojciech Macek 			}
33147902c8dcSWojciech Macek 		}
33157902c8dcSWojciech Macek 
3316bc14c73bSJustin Hibbits 		adapter->if_flags = if_getflags(ifp);
33177902c8dcSWojciech Macek 		break;
33187902c8dcSWojciech Macek 
33197902c8dcSWojciech Macek 	case SIOCADDMULTI:
33207902c8dcSWojciech Macek 	case SIOCDELMULTI:
3321bc14c73bSJustin Hibbits 		if ((if_getdrvflags(ifp) & IFF_DRV_RUNNING) != 0) {
33227902c8dcSWojciech Macek 			device_printf_dbg(adapter->dev,
33237902c8dcSWojciech Macek 			    "ioctl add/del multi before\n");
33247902c8dcSWojciech Macek 			al_eth_set_rx_mode(adapter);
33257902c8dcSWojciech Macek #ifdef DEVICE_POLLING
3326bc14c73bSJustin Hibbits 			if ((if_getcapenable(ifp) & IFCAP_POLLING) == 0)
33277902c8dcSWojciech Macek #endif
33287902c8dcSWojciech Macek 		}
33297902c8dcSWojciech Macek 		break;
33307902c8dcSWojciech Macek 	case SIOCSIFMEDIA:
33317902c8dcSWojciech Macek 	case SIOCGIFMEDIA:
33327902c8dcSWojciech Macek 		if (adapter->mii != NULL)
33337902c8dcSWojciech Macek 			error = ifmedia_ioctl(ifp, ifr,
33347902c8dcSWojciech Macek 			    &adapter->mii->mii_media, command);
33357902c8dcSWojciech Macek 		else
33367902c8dcSWojciech Macek 			error = ifmedia_ioctl(ifp, ifr,
33377902c8dcSWojciech Macek 			    &adapter->media, command);
33387902c8dcSWojciech Macek 		break;
33397902c8dcSWojciech Macek 	case SIOCSIFCAP:
33407902c8dcSWojciech Macek 	    {
33417902c8dcSWojciech Macek 		int mask, reinit;
33427902c8dcSWojciech Macek 
33437902c8dcSWojciech Macek 		reinit = 0;
3344bc14c73bSJustin Hibbits 		mask = ifr->ifr_reqcap ^ if_getcapenable(ifp);
33457902c8dcSWojciech Macek #ifdef DEVICE_POLLING
33467902c8dcSWojciech Macek 		if ((mask & IFCAP_POLLING) != 0) {
33477902c8dcSWojciech Macek 			if ((ifr->ifr_reqcap & IFCAP_POLLING) != 0) {
33487902c8dcSWojciech Macek 				if (error != 0)
33497902c8dcSWojciech Macek 					return (error);
3350bc14c73bSJustin Hibbits 				if_setcapenablebit(ifp, IFCAP_POLLING, 0);
33517902c8dcSWojciech Macek 			} else {
33527902c8dcSWojciech Macek 				error = ether_poll_deregister(ifp);
33537902c8dcSWojciech Macek 				/* Enable interrupt even in error case */
3354bc14c73bSJustin Hibbits 				if_setcapenablebit(ifp, 0, IFCAP_POLLING);
33557902c8dcSWojciech Macek 			}
33567902c8dcSWojciech Macek 		}
33577902c8dcSWojciech Macek #endif
33587902c8dcSWojciech Macek 		if ((mask & IFCAP_HWCSUM) != 0) {
33597902c8dcSWojciech Macek 			/* apply to both rx and tx */
3360bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_HWCSUM);
33617902c8dcSWojciech Macek 			reinit = 1;
33627902c8dcSWojciech Macek 		}
33637902c8dcSWojciech Macek 		if ((mask & IFCAP_HWCSUM_IPV6) != 0) {
3364bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_HWCSUM_IPV6);
33657902c8dcSWojciech Macek 			reinit = 1;
33667902c8dcSWojciech Macek 		}
33677902c8dcSWojciech Macek 		if ((mask & IFCAP_TSO) != 0) {
3368bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_TSO);
33697902c8dcSWojciech Macek 			reinit = 1;
33707902c8dcSWojciech Macek 		}
33717902c8dcSWojciech Macek 		if ((mask & IFCAP_LRO) != 0) {
3372bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_LRO);
33737902c8dcSWojciech Macek 		}
33747902c8dcSWojciech Macek 		if ((mask & IFCAP_VLAN_HWTAGGING) != 0) {
3375bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTAGGING);
33767902c8dcSWojciech Macek 			reinit = 1;
33777902c8dcSWojciech Macek 		}
33787902c8dcSWojciech Macek 		if ((mask & IFCAP_VLAN_HWFILTER) != 0) {
3379bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWFILTER);
33807902c8dcSWojciech Macek 			reinit = 1;
33817902c8dcSWojciech Macek 		}
33827902c8dcSWojciech Macek 		if ((mask & IFCAP_VLAN_HWTSO) != 0) {
3383bc14c73bSJustin Hibbits 			if_togglecapenable(ifp, IFCAP_VLAN_HWTSO);
33847902c8dcSWojciech Macek 			reinit = 1;
33857902c8dcSWojciech Macek 		}
33867902c8dcSWojciech Macek 		if ((reinit != 0) &&
3387bc14c73bSJustin Hibbits 		    ((if_getdrvflags(ifp) & IFF_DRV_RUNNING)) != 0)
33887902c8dcSWojciech Macek 		{
33897902c8dcSWojciech Macek 			al_init(adapter);
33907902c8dcSWojciech Macek 		}
33917902c8dcSWojciech Macek 		break;
33927902c8dcSWojciech Macek 	    }
33937902c8dcSWojciech Macek 
33947902c8dcSWojciech Macek 	default:
33957902c8dcSWojciech Macek 		error = ether_ioctl(ifp, command, data);
33967902c8dcSWojciech Macek 		break;
33977902c8dcSWojciech Macek 	}
33987902c8dcSWojciech Macek 
33997902c8dcSWojciech Macek 	return (error);
34007902c8dcSWojciech Macek }
34017902c8dcSWojciech Macek 
34027902c8dcSWojciech Macek static int
34037902c8dcSWojciech Macek al_is_device_supported(device_t dev)
34047902c8dcSWojciech Macek {
34057902c8dcSWojciech Macek 	uint16_t pci_vendor_id = pci_get_vendor(dev);
34067902c8dcSWojciech Macek 	uint16_t pci_device_id = pci_get_device(dev);
34077902c8dcSWojciech Macek 
34087902c8dcSWojciech Macek 	return (pci_vendor_id == PCI_VENDOR_ID_ANNAPURNA_LABS &&
34097902c8dcSWojciech Macek 	    (pci_device_id == PCI_DEVICE_ID_AL_ETH ||
34107902c8dcSWojciech Macek 	    pci_device_id == PCI_DEVICE_ID_AL_ETH_ADVANCED ||
34117902c8dcSWojciech Macek 	    pci_device_id == PCI_DEVICE_ID_AL_ETH_NIC ||
34127902c8dcSWojciech Macek 	    pci_device_id == PCI_DEVICE_ID_AL_ETH_FPGA_NIC));
34137902c8dcSWojciech Macek }
34147902c8dcSWojciech Macek 
34157902c8dcSWojciech Macek /* Time in mSec to keep trying to read / write from MDIO in case of error */
34167902c8dcSWojciech Macek #define	MDIO_TIMEOUT_MSEC	100
34177902c8dcSWojciech Macek #define	MDIO_PAUSE_MSEC		10
34187902c8dcSWojciech Macek 
34197902c8dcSWojciech Macek static int
34207902c8dcSWojciech Macek al_miibus_readreg(device_t dev, int phy, int reg)
34217902c8dcSWojciech Macek {
34227902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = device_get_softc(dev);
34237902c8dcSWojciech Macek 	uint16_t value = 0;
34247902c8dcSWojciech Macek 	int rc;
34257902c8dcSWojciech Macek 	int timeout = MDIO_TIMEOUT_MSEC;
34267902c8dcSWojciech Macek 
34277902c8dcSWojciech Macek 	while (timeout > 0) {
34287902c8dcSWojciech Macek 		rc = al_eth_mdio_read(&adapter->hal_adapter, adapter->phy_addr,
34297902c8dcSWojciech Macek 		    -1, reg, &value);
34307902c8dcSWojciech Macek 
34317902c8dcSWojciech Macek 		if (rc == 0)
34327902c8dcSWojciech Macek 			return (value);
34337902c8dcSWojciech Macek 
34347902c8dcSWojciech Macek 		device_printf_dbg(adapter->dev,
34357902c8dcSWojciech Macek 		    "mdio read failed. try again in 10 msec\n");
34367902c8dcSWojciech Macek 
34377902c8dcSWojciech Macek 		timeout -= MDIO_PAUSE_MSEC;
34387902c8dcSWojciech Macek 		pause("readred pause", MDIO_PAUSE_MSEC);
34397902c8dcSWojciech Macek 	}
34407902c8dcSWojciech Macek 
34417902c8dcSWojciech Macek 	if (rc != 0)
34427902c8dcSWojciech Macek 		device_printf(adapter->dev, "MDIO read failed on timeout\n");
34437902c8dcSWojciech Macek 
34447902c8dcSWojciech Macek 	return (value);
34457902c8dcSWojciech Macek }
34467902c8dcSWojciech Macek 
34477902c8dcSWojciech Macek static int
34487902c8dcSWojciech Macek al_miibus_writereg(device_t dev, int phy, int reg, int value)
34497902c8dcSWojciech Macek {
34507902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = device_get_softc(dev);
34517902c8dcSWojciech Macek 	int rc;
34527902c8dcSWojciech Macek 	int timeout = MDIO_TIMEOUT_MSEC;
34537902c8dcSWojciech Macek 
34547902c8dcSWojciech Macek 	while (timeout > 0) {
34557902c8dcSWojciech Macek 		rc = al_eth_mdio_write(&adapter->hal_adapter, adapter->phy_addr,
34567902c8dcSWojciech Macek 		    -1, reg, value);
34577902c8dcSWojciech Macek 
34587902c8dcSWojciech Macek 		if (rc == 0)
34597902c8dcSWojciech Macek 			return (0);
34607902c8dcSWojciech Macek 
34617902c8dcSWojciech Macek 		device_printf(adapter->dev,
34627902c8dcSWojciech Macek 		    "mdio write failed. try again in 10 msec\n");
34637902c8dcSWojciech Macek 
34647902c8dcSWojciech Macek 		timeout -= MDIO_PAUSE_MSEC;
34657902c8dcSWojciech Macek 		pause("miibus writereg", MDIO_PAUSE_MSEC);
34667902c8dcSWojciech Macek 	}
34677902c8dcSWojciech Macek 
34687902c8dcSWojciech Macek 	if (rc != 0)
34697902c8dcSWojciech Macek 		device_printf(adapter->dev, "MDIO write failed on timeout\n");
34707902c8dcSWojciech Macek 
34717902c8dcSWojciech Macek 	return (rc);
34727902c8dcSWojciech Macek }
34737902c8dcSWojciech Macek 
34747902c8dcSWojciech Macek static void
34757902c8dcSWojciech Macek al_miibus_statchg(device_t dev)
34767902c8dcSWojciech Macek {
34777902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = device_get_softc(dev);
34787902c8dcSWojciech Macek 
34797902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev,
34807902c8dcSWojciech Macek 	    "al_miibus_statchg: state has changed!\n");
34817902c8dcSWojciech Macek 	device_printf_dbg(adapter->dev,
34827902c8dcSWojciech Macek 	    "al_miibus_statchg: active = 0x%x status = 0x%x\n",
34837902c8dcSWojciech Macek 	    adapter->mii->mii_media_active, adapter->mii->mii_media_status);
34847902c8dcSWojciech Macek 
34857902c8dcSWojciech Macek 	if (adapter->up == 0)
34867902c8dcSWojciech Macek 		return;
34877902c8dcSWojciech Macek 
34887902c8dcSWojciech Macek 	if ((adapter->mii->mii_media_status & IFM_AVALID) != 0) {
34897902c8dcSWojciech Macek 		if (adapter->mii->mii_media_status & IFM_ACTIVE) {
34907902c8dcSWojciech Macek 			device_printf(adapter->dev, "link is UP\n");
3491f46a05b5SJustin Hibbits 			if_link_state_change(adapter->netdev, LINK_STATE_UP);
34927902c8dcSWojciech Macek 		} else {
34937902c8dcSWojciech Macek 			device_printf(adapter->dev, "link is DOWN\n");
3494f46a05b5SJustin Hibbits 			if_link_state_change(adapter->netdev, LINK_STATE_DOWN);
34957902c8dcSWojciech Macek 		}
34967902c8dcSWojciech Macek 	}
34977902c8dcSWojciech Macek }
34987902c8dcSWojciech Macek 
34997902c8dcSWojciech Macek static void
35007902c8dcSWojciech Macek al_miibus_linkchg(device_t dev)
35017902c8dcSWojciech Macek {
35027902c8dcSWojciech Macek 	struct al_eth_adapter *adapter = device_get_softc(dev);
35037902c8dcSWojciech Macek 	uint8_t duplex = 0;
35047902c8dcSWojciech Macek 	uint8_t speed = 0;
35057902c8dcSWojciech Macek 
35064d24901aSPedro F. Giffuni 	if (adapter->mii == NULL)
35077902c8dcSWojciech Macek 		return;
35087902c8dcSWojciech Macek 
3509f46a05b5SJustin Hibbits 	if ((if_getflags(adapter->netdev) & IFF_UP) == 0)
35107902c8dcSWojciech Macek 		return;
35117902c8dcSWojciech Macek 
35127902c8dcSWojciech Macek 	/* Ignore link changes when link is not ready */
35137902c8dcSWojciech Macek 	if ((adapter->mii->mii_media_status & (IFM_AVALID | IFM_ACTIVE)) !=
35147902c8dcSWojciech Macek 	    (IFM_AVALID | IFM_ACTIVE)) {
35157902c8dcSWojciech Macek 		return;
35167902c8dcSWojciech Macek 	}
35177902c8dcSWojciech Macek 
35187902c8dcSWojciech Macek 	if ((adapter->mii->mii_media_active & IFM_FDX) != 0)
35197902c8dcSWojciech Macek 		duplex = 1;
35207902c8dcSWojciech Macek 
35217902c8dcSWojciech Macek 	speed = IFM_SUBTYPE(adapter->mii->mii_media_active);
35227902c8dcSWojciech Macek 
35237902c8dcSWojciech Macek 	if (speed == IFM_10_T) {
35247902c8dcSWojciech Macek 		al_eth_mac_link_config(&adapter->hal_adapter, 0, 1,
35257902c8dcSWojciech Macek 		    AL_10BASE_T_SPEED, duplex);
35267902c8dcSWojciech Macek 		return;
35277902c8dcSWojciech Macek 	}
35287902c8dcSWojciech Macek 
35297902c8dcSWojciech Macek 	if (speed == IFM_100_TX) {
35307902c8dcSWojciech Macek 		al_eth_mac_link_config(&adapter->hal_adapter, 0, 1,
35317902c8dcSWojciech Macek 		    AL_100BASE_TX_SPEED, duplex);
35327902c8dcSWojciech Macek 		return;
35337902c8dcSWojciech Macek 	}
35347902c8dcSWojciech Macek 
35357902c8dcSWojciech Macek 	if (speed == IFM_1000_T) {
35367902c8dcSWojciech Macek 		al_eth_mac_link_config(&adapter->hal_adapter, 0, 1,
35377902c8dcSWojciech Macek 		    AL_1000BASE_T_SPEED, duplex);
35387902c8dcSWojciech Macek 		return;
35397902c8dcSWojciech Macek 	}
35407902c8dcSWojciech Macek 
35417902c8dcSWojciech Macek 	device_printf(adapter->dev, "ERROR: unknown MII media active 0x%08x\n",
35427902c8dcSWojciech Macek 	    adapter->mii->mii_media_active);
35437902c8dcSWojciech Macek }
3544