xref: /freebsd/sys/dev/dwc/dwc1000_core.c (revision 1f469a9f)
162519d5aSEmmanuel Vadot /*-
262519d5aSEmmanuel Vadot  * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com>
362519d5aSEmmanuel Vadot  *
462519d5aSEmmanuel Vadot  * This software was developed by SRI International and the University of
562519d5aSEmmanuel Vadot  * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237)
662519d5aSEmmanuel Vadot  * ("CTSRD"), as part of the DARPA CRASH research programme.
762519d5aSEmmanuel Vadot  *
862519d5aSEmmanuel Vadot  * Redistribution and use in source and binary forms, with or without
962519d5aSEmmanuel Vadot  * modification, are permitted provided that the following conditions
1062519d5aSEmmanuel Vadot  * are met:
1162519d5aSEmmanuel Vadot  * 1. Redistributions of source code must retain the above copyright
1262519d5aSEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer.
1362519d5aSEmmanuel Vadot  * 2. Redistributions in binary form must reproduce the above copyright
1462519d5aSEmmanuel Vadot  *    notice, this list of conditions and the following disclaimer in the
1562519d5aSEmmanuel Vadot  *    documentation and/or other materials provided with the distribution.
1662519d5aSEmmanuel Vadot  *
1762519d5aSEmmanuel Vadot  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1862519d5aSEmmanuel Vadot  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1962519d5aSEmmanuel Vadot  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2062519d5aSEmmanuel Vadot  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2162519d5aSEmmanuel Vadot  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2262519d5aSEmmanuel Vadot  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2362519d5aSEmmanuel Vadot  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2462519d5aSEmmanuel Vadot  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2562519d5aSEmmanuel Vadot  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2662519d5aSEmmanuel Vadot  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2762519d5aSEmmanuel Vadot  * SUCH DAMAGE.
2862519d5aSEmmanuel Vadot  */
2962519d5aSEmmanuel Vadot 
3062519d5aSEmmanuel Vadot /*
3162519d5aSEmmanuel Vadot  * Ethernet media access controller (EMAC)
3262519d5aSEmmanuel Vadot  * Chapter 17, Altera Cyclone V Device Handbook (CV-5V2 2014.07.22)
3362519d5aSEmmanuel Vadot  *
3462519d5aSEmmanuel Vadot  * EMAC is an instance of the Synopsys DesignWare 3504-0
3562519d5aSEmmanuel Vadot  * Universal 10/100/1000 Ethernet MAC (DWC_gmac).
3662519d5aSEmmanuel Vadot  */
3762519d5aSEmmanuel Vadot 
3862519d5aSEmmanuel Vadot #include <sys/param.h>
3962519d5aSEmmanuel Vadot #include <sys/systm.h>
4062519d5aSEmmanuel Vadot #include <sys/bus.h>
4162519d5aSEmmanuel Vadot #include <sys/kernel.h>
4262519d5aSEmmanuel Vadot #include <sys/lock.h>
4362519d5aSEmmanuel Vadot #include <sys/malloc.h>
4462519d5aSEmmanuel Vadot #include <sys/mbuf.h>
4562519d5aSEmmanuel Vadot #include <sys/module.h>
4662519d5aSEmmanuel Vadot #include <sys/mutex.h>
4762519d5aSEmmanuel Vadot #include <sys/rman.h>
4862519d5aSEmmanuel Vadot #include <sys/socket.h>
4962519d5aSEmmanuel Vadot 
5062519d5aSEmmanuel Vadot #include <net/if.h>
5162519d5aSEmmanuel Vadot #include <net/ethernet.h>
5262519d5aSEmmanuel Vadot #include <net/if_dl.h>
5362519d5aSEmmanuel Vadot #include <net/if_media.h>
5462519d5aSEmmanuel Vadot 
5562519d5aSEmmanuel Vadot #include <machine/bus.h>
5662519d5aSEmmanuel Vadot 
57be82b3a0SEmmanuel Vadot #include <dev/clk/clk.h>
581f469a9fSEmmanuel Vadot #include <dev/hwreset/hwreset.h>
5962519d5aSEmmanuel Vadot 
6062519d5aSEmmanuel Vadot #include <dev/mii/mii.h>
6162519d5aSEmmanuel Vadot #include <dev/mii/miivar.h>
6262519d5aSEmmanuel Vadot #include <dev/ofw/ofw_bus.h>
6362519d5aSEmmanuel Vadot #include <dev/ofw/ofw_bus_subr.h>
6462519d5aSEmmanuel Vadot #include <dev/mii/mii_fdt.h>
6562519d5aSEmmanuel Vadot 
6662519d5aSEmmanuel Vadot #include <dev/dwc/if_dwcvar.h>
6762519d5aSEmmanuel Vadot #include <dev/dwc/dwc1000_reg.h>
6862519d5aSEmmanuel Vadot #include <dev/dwc/dwc1000_core.h>
6962519d5aSEmmanuel Vadot #include <dev/dwc/dwc1000_dma.h>
7062519d5aSEmmanuel Vadot 
7162519d5aSEmmanuel Vadot #include "if_dwc_if.h"
7262519d5aSEmmanuel Vadot 
73627726b5SEmmanuel Vadot struct dwc_hash_maddr_ctx {
74627726b5SEmmanuel Vadot 	struct dwc_softc *sc;
75627726b5SEmmanuel Vadot 	uint32_t hash[8];
76627726b5SEmmanuel Vadot };
77627726b5SEmmanuel Vadot 
7862519d5aSEmmanuel Vadot #define	STATS_HARVEST_INTERVAL	2
7962519d5aSEmmanuel Vadot 
8062519d5aSEmmanuel Vadot /* Pause time field in the transmitted control frame */
8162519d5aSEmmanuel Vadot static int dwc_pause_time = 0xffff;
8262519d5aSEmmanuel Vadot TUNABLE_INT("hw.dwc.pause_time", &dwc_pause_time);
8362519d5aSEmmanuel Vadot 
8462519d5aSEmmanuel Vadot /*
8562519d5aSEmmanuel Vadot  * MIIBUS functions
8662519d5aSEmmanuel Vadot  */
8762519d5aSEmmanuel Vadot 
8862519d5aSEmmanuel Vadot int
dwc1000_miibus_read_reg(device_t dev,int phy,int reg)8962519d5aSEmmanuel Vadot dwc1000_miibus_read_reg(device_t dev, int phy, int reg)
9062519d5aSEmmanuel Vadot {
9162519d5aSEmmanuel Vadot 	struct dwc_softc *sc;
9262519d5aSEmmanuel Vadot 	uint16_t mii;
9362519d5aSEmmanuel Vadot 	size_t cnt;
9462519d5aSEmmanuel Vadot 	int rv = 0;
9562519d5aSEmmanuel Vadot 
9662519d5aSEmmanuel Vadot 	sc = device_get_softc(dev);
9762519d5aSEmmanuel Vadot 
9862519d5aSEmmanuel Vadot 	mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT)
9962519d5aSEmmanuel Vadot 	    | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT)
10062519d5aSEmmanuel Vadot 	    | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT)
10162519d5aSEmmanuel Vadot 	    | GMII_ADDRESS_GB; /* Busy flag */
10262519d5aSEmmanuel Vadot 
10362519d5aSEmmanuel Vadot 	WRITE4(sc, GMII_ADDRESS, mii);
10462519d5aSEmmanuel Vadot 
10562519d5aSEmmanuel Vadot 	for (cnt = 0; cnt < 1000; cnt++) {
10662519d5aSEmmanuel Vadot 		if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) {
10762519d5aSEmmanuel Vadot 			rv = READ4(sc, GMII_DATA);
10862519d5aSEmmanuel Vadot 			break;
10962519d5aSEmmanuel Vadot 		}
11062519d5aSEmmanuel Vadot 		DELAY(10);
11162519d5aSEmmanuel Vadot 	}
11262519d5aSEmmanuel Vadot 
11362519d5aSEmmanuel Vadot 	return rv;
11462519d5aSEmmanuel Vadot }
11562519d5aSEmmanuel Vadot 
11662519d5aSEmmanuel Vadot int
dwc1000_miibus_write_reg(device_t dev,int phy,int reg,int val)11762519d5aSEmmanuel Vadot dwc1000_miibus_write_reg(device_t dev, int phy, int reg, int val)
11862519d5aSEmmanuel Vadot {
11962519d5aSEmmanuel Vadot 	struct dwc_softc *sc;
12062519d5aSEmmanuel Vadot 	uint16_t mii;
12162519d5aSEmmanuel Vadot 	size_t cnt;
12262519d5aSEmmanuel Vadot 
12362519d5aSEmmanuel Vadot 	sc = device_get_softc(dev);
12462519d5aSEmmanuel Vadot 
12562519d5aSEmmanuel Vadot 	mii = ((phy & GMII_ADDRESS_PA_MASK) << GMII_ADDRESS_PA_SHIFT)
12662519d5aSEmmanuel Vadot 	    | ((reg & GMII_ADDRESS_GR_MASK) << GMII_ADDRESS_GR_SHIFT)
12762519d5aSEmmanuel Vadot 	    | (sc->mii_clk << GMII_ADDRESS_CR_SHIFT)
12862519d5aSEmmanuel Vadot 	    | GMII_ADDRESS_GB | GMII_ADDRESS_GW;
12962519d5aSEmmanuel Vadot 
13062519d5aSEmmanuel Vadot 	WRITE4(sc, GMII_DATA, val);
13162519d5aSEmmanuel Vadot 	WRITE4(sc, GMII_ADDRESS, mii);
13262519d5aSEmmanuel Vadot 
13362519d5aSEmmanuel Vadot 	for (cnt = 0; cnt < 1000; cnt++) {
13462519d5aSEmmanuel Vadot 		if (!(READ4(sc, GMII_ADDRESS) & GMII_ADDRESS_GB)) {
13562519d5aSEmmanuel Vadot 			break;
13662519d5aSEmmanuel Vadot                 }
13762519d5aSEmmanuel Vadot 		DELAY(10);
13862519d5aSEmmanuel Vadot 	}
13962519d5aSEmmanuel Vadot 
14062519d5aSEmmanuel Vadot 	return (0);
14162519d5aSEmmanuel Vadot }
14262519d5aSEmmanuel Vadot 
14362519d5aSEmmanuel Vadot void
dwc1000_miibus_statchg(device_t dev)14462519d5aSEmmanuel Vadot dwc1000_miibus_statchg(device_t dev)
14562519d5aSEmmanuel Vadot {
14662519d5aSEmmanuel Vadot 	struct dwc_softc *sc;
14762519d5aSEmmanuel Vadot 	struct mii_data *mii;
14862519d5aSEmmanuel Vadot 	uint32_t reg;
14962519d5aSEmmanuel Vadot 
15062519d5aSEmmanuel Vadot 	/*
15162519d5aSEmmanuel Vadot 	 * Called by the MII bus driver when the PHY establishes
15262519d5aSEmmanuel Vadot 	 * link to set the MAC interface registers.
15362519d5aSEmmanuel Vadot 	 */
15462519d5aSEmmanuel Vadot 
15562519d5aSEmmanuel Vadot 	sc = device_get_softc(dev);
15662519d5aSEmmanuel Vadot 
15762519d5aSEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
15862519d5aSEmmanuel Vadot 
15962519d5aSEmmanuel Vadot 	mii = sc->mii_softc;
16062519d5aSEmmanuel Vadot 
16162519d5aSEmmanuel Vadot 	if (mii->mii_media_status & IFM_ACTIVE)
16262519d5aSEmmanuel Vadot 		sc->link_is_up = true;
16362519d5aSEmmanuel Vadot 	else
16462519d5aSEmmanuel Vadot 		sc->link_is_up = false;
16562519d5aSEmmanuel Vadot 
16662519d5aSEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
16762519d5aSEmmanuel Vadot 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
16862519d5aSEmmanuel Vadot 	case IFM_1000_T:
16962519d5aSEmmanuel Vadot 	case IFM_1000_SX:
17062519d5aSEmmanuel Vadot 		reg &= ~(CONF_FES | CONF_PS);
17162519d5aSEmmanuel Vadot 		break;
17262519d5aSEmmanuel Vadot 	case IFM_100_TX:
17362519d5aSEmmanuel Vadot 		reg |= (CONF_FES | CONF_PS);
17462519d5aSEmmanuel Vadot 		break;
17562519d5aSEmmanuel Vadot 	case IFM_10_T:
17662519d5aSEmmanuel Vadot 		reg &= ~(CONF_FES);
17762519d5aSEmmanuel Vadot 		reg |= (CONF_PS);
17862519d5aSEmmanuel Vadot 		break;
17962519d5aSEmmanuel Vadot 	case IFM_NONE:
18062519d5aSEmmanuel Vadot 		sc->link_is_up = false;
18162519d5aSEmmanuel Vadot 		return;
18262519d5aSEmmanuel Vadot 	default:
18362519d5aSEmmanuel Vadot 		sc->link_is_up = false;
18462519d5aSEmmanuel Vadot 		device_printf(dev, "Unsupported media %u\n",
18562519d5aSEmmanuel Vadot 		    IFM_SUBTYPE(mii->mii_media_active));
18662519d5aSEmmanuel Vadot 		return;
18762519d5aSEmmanuel Vadot 	}
18862519d5aSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
18962519d5aSEmmanuel Vadot 		reg |= (CONF_DM);
19062519d5aSEmmanuel Vadot 	else
19162519d5aSEmmanuel Vadot 		reg &= ~(CONF_DM);
19262519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
19362519d5aSEmmanuel Vadot 
19462519d5aSEmmanuel Vadot 	reg = FLOW_CONTROL_UP;
19562519d5aSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_TXPAUSE) != 0)
19662519d5aSEmmanuel Vadot 		reg |= FLOW_CONTROL_TX;
19762519d5aSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_ETH_RXPAUSE) != 0)
19862519d5aSEmmanuel Vadot 		reg |= FLOW_CONTROL_RX;
19962519d5aSEmmanuel Vadot 	if ((IFM_OPTIONS(mii->mii_media_active) & IFM_FDX) != 0)
20062519d5aSEmmanuel Vadot 		reg |= dwc_pause_time << FLOW_CONTROL_PT_SHIFT;
20162519d5aSEmmanuel Vadot 	WRITE4(sc, FLOW_CONTROL, reg);
20262519d5aSEmmanuel Vadot 
20362519d5aSEmmanuel Vadot 	IF_DWC_SET_SPEED(dev, IFM_SUBTYPE(mii->mii_media_active));
20462519d5aSEmmanuel Vadot 
20562519d5aSEmmanuel Vadot }
20662519d5aSEmmanuel Vadot 
20762519d5aSEmmanuel Vadot void
dwc1000_core_setup(struct dwc_softc * sc)20862519d5aSEmmanuel Vadot dwc1000_core_setup(struct dwc_softc *sc)
20962519d5aSEmmanuel Vadot {
21062519d5aSEmmanuel Vadot 	uint32_t reg;
21162519d5aSEmmanuel Vadot 
21262519d5aSEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
21362519d5aSEmmanuel Vadot 
21462519d5aSEmmanuel Vadot 	/* Enable core */
21562519d5aSEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
21662519d5aSEmmanuel Vadot 	reg |= (CONF_JD | CONF_ACS | CONF_BE);
21762519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
21862519d5aSEmmanuel Vadot }
21962519d5aSEmmanuel Vadot 
22062519d5aSEmmanuel Vadot void
dwc1000_enable_mac(struct dwc_softc * sc,bool enable)22162519d5aSEmmanuel Vadot dwc1000_enable_mac(struct dwc_softc *sc, bool enable)
22262519d5aSEmmanuel Vadot {
22362519d5aSEmmanuel Vadot 	uint32_t reg;
22462519d5aSEmmanuel Vadot 
22562519d5aSEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
22662519d5aSEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
22762519d5aSEmmanuel Vadot 	if (enable)
22862519d5aSEmmanuel Vadot 		reg |= CONF_TE | CONF_RE;
22962519d5aSEmmanuel Vadot 	else
23062519d5aSEmmanuel Vadot 		reg &= ~(CONF_TE | CONF_RE);
23162519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
23262519d5aSEmmanuel Vadot }
23362519d5aSEmmanuel Vadot 
23462519d5aSEmmanuel Vadot void
dwc1000_enable_csum_offload(struct dwc_softc * sc)23562519d5aSEmmanuel Vadot dwc1000_enable_csum_offload(struct dwc_softc *sc)
23662519d5aSEmmanuel Vadot {
23762519d5aSEmmanuel Vadot 	uint32_t reg;
23862519d5aSEmmanuel Vadot 
23962519d5aSEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
24062519d5aSEmmanuel Vadot 	reg = READ4(sc, MAC_CONFIGURATION);
24162519d5aSEmmanuel Vadot 	if ((if_getcapenable(sc->ifp) & IFCAP_RXCSUM) != 0)
24262519d5aSEmmanuel Vadot 		reg |= CONF_IPC;
24362519d5aSEmmanuel Vadot 	else
24462519d5aSEmmanuel Vadot 		reg &= ~CONF_IPC;
24562519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_CONFIGURATION, reg);
24662519d5aSEmmanuel Vadot }
24762519d5aSEmmanuel Vadot 
24862519d5aSEmmanuel Vadot static const uint8_t nibbletab[] = {
24962519d5aSEmmanuel Vadot 	/* 0x0 0000 -> 0000 */  0x0,
25062519d5aSEmmanuel Vadot 	/* 0x1 0001 -> 1000 */  0x8,
25162519d5aSEmmanuel Vadot 	/* 0x2 0010 -> 0100 */  0x4,
25262519d5aSEmmanuel Vadot 	/* 0x3 0011 -> 1100 */  0xc,
25362519d5aSEmmanuel Vadot 	/* 0x4 0100 -> 0010 */  0x2,
25462519d5aSEmmanuel Vadot 	/* 0x5 0101 -> 1010 */  0xa,
25562519d5aSEmmanuel Vadot 	/* 0x6 0110 -> 0110 */  0x6,
25662519d5aSEmmanuel Vadot 	/* 0x7 0111 -> 1110 */  0xe,
25762519d5aSEmmanuel Vadot 	/* 0x8 1000 -> 0001 */  0x1,
25862519d5aSEmmanuel Vadot 	/* 0x9 1001 -> 1001 */  0x9,
25962519d5aSEmmanuel Vadot 	/* 0xa 1010 -> 0101 */  0x5,
26062519d5aSEmmanuel Vadot 	/* 0xb 1011 -> 1101 */  0xd,
26162519d5aSEmmanuel Vadot 	/* 0xc 1100 -> 0011 */  0x3,
26262519d5aSEmmanuel Vadot 	/* 0xd 1101 -> 1011 */  0xb,
26362519d5aSEmmanuel Vadot 	/* 0xe 1110 -> 0111 */  0x7,
26462519d5aSEmmanuel Vadot 	/* 0xf 1111 -> 1111 */  0xf, };
26562519d5aSEmmanuel Vadot 
26662519d5aSEmmanuel Vadot static uint8_t
bitreverse(uint8_t x)26762519d5aSEmmanuel Vadot bitreverse(uint8_t x)
26862519d5aSEmmanuel Vadot {
26962519d5aSEmmanuel Vadot 
27062519d5aSEmmanuel Vadot 	return (nibbletab[x & 0xf] << 4) | nibbletab[x >> 4];
27162519d5aSEmmanuel Vadot }
27262519d5aSEmmanuel Vadot 
27362519d5aSEmmanuel Vadot static u_int
dwc_hash_maddr(void * arg,struct sockaddr_dl * sdl,u_int cnt)27462519d5aSEmmanuel Vadot dwc_hash_maddr(void *arg, struct sockaddr_dl *sdl, u_int cnt)
27562519d5aSEmmanuel Vadot {
27662519d5aSEmmanuel Vadot 	struct dwc_hash_maddr_ctx *ctx = arg;
27762519d5aSEmmanuel Vadot 	uint32_t crc, hashbit, hashreg;
27862519d5aSEmmanuel Vadot 	uint8_t val;
27962519d5aSEmmanuel Vadot 
28062519d5aSEmmanuel Vadot 	crc = ether_crc32_le(LLADDR(sdl), ETHER_ADDR_LEN);
28162519d5aSEmmanuel Vadot 	/* Take lower 8 bits and reverse it */
28262519d5aSEmmanuel Vadot 	val = bitreverse(~crc & 0xff);
2834b7975ecSEmmanuel Vadot 	/*
2844b7975ecSEmmanuel Vadot 	 * TODO: There is probably a HW_FEATURES bit which isn't
2854b7975ecSEmmanuel Vadot 	 * related to the extended descriptors that describe this
2864b7975ecSEmmanuel Vadot 	 */
2874b7975ecSEmmanuel Vadot 	if (!ctx->sc->dma_ext_desc)
28862519d5aSEmmanuel Vadot 		val >>= 2; /* Only need lower 6 bits */
28962519d5aSEmmanuel Vadot 	hashreg = (val >> 5);
29062519d5aSEmmanuel Vadot 	hashbit = (val & 31);
29162519d5aSEmmanuel Vadot 	ctx->hash[hashreg] |= (1 << hashbit);
29262519d5aSEmmanuel Vadot 
29362519d5aSEmmanuel Vadot 	return (1);
29462519d5aSEmmanuel Vadot }
29562519d5aSEmmanuel Vadot 
29662519d5aSEmmanuel Vadot void
dwc1000_setup_rxfilter(struct dwc_softc * sc)29762519d5aSEmmanuel Vadot dwc1000_setup_rxfilter(struct dwc_softc *sc)
29862519d5aSEmmanuel Vadot {
29962519d5aSEmmanuel Vadot 	struct dwc_hash_maddr_ctx ctx;
30062519d5aSEmmanuel Vadot 	if_t ifp;
30162519d5aSEmmanuel Vadot 	uint8_t *eaddr;
30262519d5aSEmmanuel Vadot 	uint32_t ffval, hi, lo;
30362519d5aSEmmanuel Vadot 	int nhash, i;
30462519d5aSEmmanuel Vadot 
30562519d5aSEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
30662519d5aSEmmanuel Vadot 
30762519d5aSEmmanuel Vadot 	ifp = sc->ifp;
3084b7975ecSEmmanuel Vadot 	/*
3094b7975ecSEmmanuel Vadot 	 * TODO: There is probably a HW_FEATURES bit which isn't
3104b7975ecSEmmanuel Vadot 	 * related to the extended descriptors that describe this
3114b7975ecSEmmanuel Vadot 	 */
3124b7975ecSEmmanuel Vadot 	nhash = sc->dma_ext_desc == false ? 2 : 8;
31362519d5aSEmmanuel Vadot 
31462519d5aSEmmanuel Vadot 	/*
31562519d5aSEmmanuel Vadot 	 * Set the multicast (group) filter hash.
31662519d5aSEmmanuel Vadot 	 */
31762519d5aSEmmanuel Vadot 	if ((if_getflags(ifp) & IFF_ALLMULTI) != 0) {
31862519d5aSEmmanuel Vadot 		ffval = (FRAME_FILTER_PM);
31962519d5aSEmmanuel Vadot 		for (i = 0; i < nhash; i++)
32062519d5aSEmmanuel Vadot 			ctx.hash[i] = ~0;
32162519d5aSEmmanuel Vadot 	} else {
32262519d5aSEmmanuel Vadot 		ffval = (FRAME_FILTER_HMC);
32362519d5aSEmmanuel Vadot 		for (i = 0; i < nhash; i++)
32462519d5aSEmmanuel Vadot 			ctx.hash[i] = 0;
32562519d5aSEmmanuel Vadot 		ctx.sc = sc;
32662519d5aSEmmanuel Vadot 		if_foreach_llmaddr(ifp, dwc_hash_maddr, &ctx);
32762519d5aSEmmanuel Vadot 	}
32862519d5aSEmmanuel Vadot 
32962519d5aSEmmanuel Vadot 	/*
33062519d5aSEmmanuel Vadot 	 * Set the individual address filter hash.
33162519d5aSEmmanuel Vadot 	 */
33262519d5aSEmmanuel Vadot 	if ((if_getflags(ifp) & IFF_PROMISC) != 0)
33362519d5aSEmmanuel Vadot 		ffval |= (FRAME_FILTER_PR);
33462519d5aSEmmanuel Vadot 
33562519d5aSEmmanuel Vadot 	/*
33662519d5aSEmmanuel Vadot 	 * Set the primary address.
33762519d5aSEmmanuel Vadot 	 */
33862519d5aSEmmanuel Vadot 	eaddr = if_getlladdr(ifp);
33962519d5aSEmmanuel Vadot 	lo = eaddr[0] | (eaddr[1] << 8) | (eaddr[2] << 16) |
34062519d5aSEmmanuel Vadot 	    (eaddr[3] << 24);
34162519d5aSEmmanuel Vadot 	hi = eaddr[4] | (eaddr[5] << 8);
34262519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_ADDRESS_LOW(0), lo);
34362519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_ADDRESS_HIGH(0), hi);
34462519d5aSEmmanuel Vadot 	WRITE4(sc, MAC_FRAME_FILTER, ffval);
3454b7975ecSEmmanuel Vadot 	if (!sc->dma_ext_desc) {
34662519d5aSEmmanuel Vadot 		WRITE4(sc, GMAC_MAC_HTLOW, ctx.hash[0]);
34762519d5aSEmmanuel Vadot 		WRITE4(sc, GMAC_MAC_HTHIGH, ctx.hash[1]);
34862519d5aSEmmanuel Vadot 	} else {
34962519d5aSEmmanuel Vadot 		for (i = 0; i < nhash; i++)
35062519d5aSEmmanuel Vadot 			WRITE4(sc, HASH_TABLE_REG(i), ctx.hash[i]);
35162519d5aSEmmanuel Vadot 	}
35262519d5aSEmmanuel Vadot }
35362519d5aSEmmanuel Vadot 
35462519d5aSEmmanuel Vadot void
dwc1000_get_hwaddr(struct dwc_softc * sc,uint8_t * hwaddr)35562519d5aSEmmanuel Vadot dwc1000_get_hwaddr(struct dwc_softc *sc, uint8_t *hwaddr)
35662519d5aSEmmanuel Vadot {
35762519d5aSEmmanuel Vadot 	uint32_t hi, lo, rnd;
35862519d5aSEmmanuel Vadot 
35962519d5aSEmmanuel Vadot 	/*
36062519d5aSEmmanuel Vadot 	 * Try to recover a MAC address from the running hardware. If there's
36162519d5aSEmmanuel Vadot 	 * something non-zero there, assume the bootloader did the right thing
36262519d5aSEmmanuel Vadot 	 * and just use it.
36362519d5aSEmmanuel Vadot 	 *
36462519d5aSEmmanuel Vadot 	 * Otherwise, set the address to a convenient locally assigned address,
36562519d5aSEmmanuel Vadot 	 * 'bsd' + random 24 low-order bits.  'b' is 0x62, which has the locally
36662519d5aSEmmanuel Vadot 	 * assigned bit set, and the broadcast/multicast bit clear.
36762519d5aSEmmanuel Vadot 	 */
36862519d5aSEmmanuel Vadot 	lo = READ4(sc, MAC_ADDRESS_LOW(0));
36962519d5aSEmmanuel Vadot 	hi = READ4(sc, MAC_ADDRESS_HIGH(0)) & 0xffff;
37062519d5aSEmmanuel Vadot 	if ((lo != 0xffffffff) || (hi != 0xffff)) {
37162519d5aSEmmanuel Vadot 		hwaddr[0] = (lo >>  0) & 0xff;
37262519d5aSEmmanuel Vadot 		hwaddr[1] = (lo >>  8) & 0xff;
37362519d5aSEmmanuel Vadot 		hwaddr[2] = (lo >> 16) & 0xff;
37462519d5aSEmmanuel Vadot 		hwaddr[3] = (lo >> 24) & 0xff;
37562519d5aSEmmanuel Vadot 		hwaddr[4] = (hi >>  0) & 0xff;
37662519d5aSEmmanuel Vadot 		hwaddr[5] = (hi >>  8) & 0xff;
37762519d5aSEmmanuel Vadot 	} else {
37862519d5aSEmmanuel Vadot 		rnd = arc4random() & 0x00ffffff;
37962519d5aSEmmanuel Vadot 		hwaddr[0] = 'b';
38062519d5aSEmmanuel Vadot 		hwaddr[1] = 's';
38162519d5aSEmmanuel Vadot 		hwaddr[2] = 'd';
38262519d5aSEmmanuel Vadot 		hwaddr[3] = rnd >> 16;
38362519d5aSEmmanuel Vadot 		hwaddr[4] = rnd >>  8;
38462519d5aSEmmanuel Vadot 		hwaddr[5] = rnd >>  0;
38562519d5aSEmmanuel Vadot 	}
38662519d5aSEmmanuel Vadot }
38762519d5aSEmmanuel Vadot 
38862519d5aSEmmanuel Vadot /*
38962519d5aSEmmanuel Vadot  * Stats
39062519d5aSEmmanuel Vadot  */
39162519d5aSEmmanuel Vadot 
39262519d5aSEmmanuel Vadot static void
dwc1000_clear_stats(struct dwc_softc * sc)39362519d5aSEmmanuel Vadot dwc1000_clear_stats(struct dwc_softc *sc)
39462519d5aSEmmanuel Vadot {
39562519d5aSEmmanuel Vadot 	uint32_t reg;
39662519d5aSEmmanuel Vadot 
39762519d5aSEmmanuel Vadot 	reg = READ4(sc, MMC_CONTROL);
39862519d5aSEmmanuel Vadot 	reg |= (MMC_CONTROL_CNTRST);
39962519d5aSEmmanuel Vadot 	WRITE4(sc, MMC_CONTROL, reg);
40062519d5aSEmmanuel Vadot }
40162519d5aSEmmanuel Vadot 
40262519d5aSEmmanuel Vadot void
dwc1000_harvest_stats(struct dwc_softc * sc)40362519d5aSEmmanuel Vadot dwc1000_harvest_stats(struct dwc_softc *sc)
40462519d5aSEmmanuel Vadot {
40562519d5aSEmmanuel Vadot 	if_t ifp;
40662519d5aSEmmanuel Vadot 
40762519d5aSEmmanuel Vadot 	/* We don't need to harvest too often. */
40862519d5aSEmmanuel Vadot 	if (++sc->stats_harvest_count < STATS_HARVEST_INTERVAL)
40962519d5aSEmmanuel Vadot 		return;
41062519d5aSEmmanuel Vadot 
41162519d5aSEmmanuel Vadot 	sc->stats_harvest_count = 0;
41262519d5aSEmmanuel Vadot 	ifp = sc->ifp;
41362519d5aSEmmanuel Vadot 
41462519d5aSEmmanuel Vadot 	if_inc_counter(ifp, IFCOUNTER_IERRORS,
41562519d5aSEmmanuel Vadot 	    READ4(sc, RXOVERSIZE_G) + READ4(sc, RXUNDERSIZE_G) +
41662519d5aSEmmanuel Vadot 	    READ4(sc, RXCRCERROR) + READ4(sc, RXALIGNMENTERROR) +
41762519d5aSEmmanuel Vadot 	    READ4(sc, RXRUNTERROR) + READ4(sc, RXJABBERERROR) +
41862519d5aSEmmanuel Vadot 	    READ4(sc, RXLENGTHERROR));
41962519d5aSEmmanuel Vadot 
42062519d5aSEmmanuel Vadot 	if_inc_counter(ifp, IFCOUNTER_OERRORS,
42162519d5aSEmmanuel Vadot 	    READ4(sc, TXOVERSIZE_G) + READ4(sc, TXEXCESSDEF) +
42262519d5aSEmmanuel Vadot 	    READ4(sc, TXCARRIERERR) + READ4(sc, TXUNDERFLOWERROR));
42362519d5aSEmmanuel Vadot 
42462519d5aSEmmanuel Vadot 	if_inc_counter(ifp, IFCOUNTER_COLLISIONS,
42562519d5aSEmmanuel Vadot 	    READ4(sc, TXEXESSCOL) + READ4(sc, TXLATECOL));
42662519d5aSEmmanuel Vadot 
42762519d5aSEmmanuel Vadot 	dwc1000_clear_stats(sc);
42862519d5aSEmmanuel Vadot }
42929776aa4SEmmanuel Vadot 
43029776aa4SEmmanuel Vadot void
dwc1000_intr(struct dwc_softc * sc)43129776aa4SEmmanuel Vadot dwc1000_intr(struct dwc_softc *sc)
43229776aa4SEmmanuel Vadot {
43329776aa4SEmmanuel Vadot 	uint32_t reg;
43429776aa4SEmmanuel Vadot 
43529776aa4SEmmanuel Vadot 	DWC_ASSERT_LOCKED(sc);
43629776aa4SEmmanuel Vadot 
43729776aa4SEmmanuel Vadot 	reg = READ4(sc, INTERRUPT_STATUS);
43829776aa4SEmmanuel Vadot 	if (reg)
43929776aa4SEmmanuel Vadot 		READ4(sc, SGMII_RGMII_SMII_CTRL_STATUS);
44029776aa4SEmmanuel Vadot }
44129776aa4SEmmanuel Vadot 
44229776aa4SEmmanuel Vadot void
dwc1000_intr_disable(struct dwc_softc * sc)44329776aa4SEmmanuel Vadot dwc1000_intr_disable(struct dwc_softc *sc)
44429776aa4SEmmanuel Vadot {
44529776aa4SEmmanuel Vadot 
44629776aa4SEmmanuel Vadot 	WRITE4(sc, INTERRUPT_ENABLE, 0);
44729776aa4SEmmanuel Vadot }
448