xref: /netbsd-src/sys/dev/cadence/if_cemac.c (revision 1b97a28dfeb41e9bb820c78c627630ae3e812101)
1*1b97a28dSlloyd /*	$NetBSD: if_cemac.c,v 1.45 2024/10/15 00:58:15 lloyd Exp $	*/
22074c2d0Shkenken 
32074c2d0Shkenken /*
42074c2d0Shkenken  * Copyright (c) 2015  Genetec Corporation.  All rights reserved.
52074c2d0Shkenken  * Written by Hashimoto Kenichi for Genetec Corporation.
62074c2d0Shkenken  *
72074c2d0Shkenken  * Based on arch/arm/at91/at91emac.c
82074c2d0Shkenken  *
92074c2d0Shkenken  * Copyright (c) 2007 Embedtronics Oy
102074c2d0Shkenken  * All rights reserved.
112074c2d0Shkenken  *
122074c2d0Shkenken  * Copyright (c) 2004 Jesse Off
132074c2d0Shkenken  * All rights reserved.
142074c2d0Shkenken  *
152074c2d0Shkenken  * Redistribution and use in source and binary forms, with or without
162074c2d0Shkenken  * modification, are permitted provided that the following conditions
172074c2d0Shkenken  * are met:
182074c2d0Shkenken  * 1. Redistributions of source code must retain the above copyright
192074c2d0Shkenken  *    notice, this list of conditions and the following disclaimer.
202074c2d0Shkenken  * 2. Redistributions in binary form must reproduce the above copyright
212074c2d0Shkenken  *    notice, this list of conditions and the following disclaimer in the
222074c2d0Shkenken  *    documentation and/or other materials provided with the distribution.
232074c2d0Shkenken  *
242074c2d0Shkenken  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
252074c2d0Shkenken  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
262074c2d0Shkenken  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
272074c2d0Shkenken  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
282074c2d0Shkenken  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
292074c2d0Shkenken  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
302074c2d0Shkenken  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
312074c2d0Shkenken  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
322074c2d0Shkenken  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
332074c2d0Shkenken  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
342074c2d0Shkenken  * POSSIBILITY OF SUCH DAMAGE.
352074c2d0Shkenken  */
362074c2d0Shkenken 
372074c2d0Shkenken /*
382074c2d0Shkenken  * Cadence EMAC/GEM ethernet controller IP driver
392074c2d0Shkenken  * used by arm/at91, arm/zynq SoC
402074c2d0Shkenken  */
412074c2d0Shkenken 
420f69512fSskrll /*
430f69512fSskrll  * Lock order:
440f69512fSskrll  *
450f69512fSskrll  *	IFNET_LOCK -> sc_mcast_lock
460f69512fSskrll  *	IFNET_LOCK -> sc_intr_lock
470f69512fSskrll  */
480f69512fSskrll 
490f69512fSskrll 
502074c2d0Shkenken #include <sys/cdefs.h>
51*1b97a28dSlloyd __KERNEL_RCSID(0, "$NetBSD: if_cemac.c,v 1.45 2024/10/15 00:58:15 lloyd Exp $");
522074c2d0Shkenken 
532074c2d0Shkenken #include <sys/param.h>
54432a1357Sskrll #include <sys/types.h>
552074c2d0Shkenken 
562074c2d0Shkenken #include <sys/bus.h>
57432a1357Sskrll #include <sys/device.h>
58432a1357Sskrll #include <sys/kernel.h>
59432a1357Sskrll #include <sys/proc.h>
60432a1357Sskrll #include <sys/systm.h>
61432a1357Sskrll #include <sys/time.h>
622074c2d0Shkenken 
632074c2d0Shkenken #include <net/if.h>
642074c2d0Shkenken #include <net/if_dl.h>
652074c2d0Shkenken #include <net/if_types.h>
662074c2d0Shkenken #include <net/if_media.h>
672074c2d0Shkenken #include <net/if_ether.h>
684b508fb1Smsaitoh #include <net/bpf.h>
692074c2d0Shkenken 
702074c2d0Shkenken #include <dev/mii/mii.h>
712074c2d0Shkenken #include <dev/mii/miivar.h>
722074c2d0Shkenken 
732074c2d0Shkenken #ifdef INET
742074c2d0Shkenken #include <netinet/in.h>
752074c2d0Shkenken #include <netinet/in_systm.h>
762074c2d0Shkenken #include <netinet/in_var.h>
772074c2d0Shkenken #include <netinet/ip.h>
782074c2d0Shkenken #include <netinet/if_inarp.h>
792074c2d0Shkenken #endif
802074c2d0Shkenken 
812074c2d0Shkenken #include <dev/cadence/cemacreg.h>
822074c2d0Shkenken #include <dev/cadence/if_cemacvar.h>
832074c2d0Shkenken 
840f69512fSskrll #ifndef CEMAC_WATCHDOG_TIMEOUT
850f69512fSskrll #define CEMAC_WATCHDOG_TIMEOUT 5
860f69512fSskrll #endif
870f69512fSskrll static int cemac_watchdog_timeout = CEMAC_WATCHDOG_TIMEOUT;
880f69512fSskrll 
892074c2d0Shkenken #define DEFAULT_MDCDIV	32
902074c2d0Shkenken 
912074c2d0Shkenken #define CEMAC_READ(x) \
922074c2d0Shkenken 	bus_space_read_4(sc->sc_iot, sc->sc_ioh, (x))
932074c2d0Shkenken #define CEMAC_WRITE(x, y) \
942074c2d0Shkenken 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, (x), (y))
952074c2d0Shkenken #define CEMAC_GEM_WRITE(x, y)						      \
962074c2d0Shkenken     do {								      \
972074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))			      \
982074c2d0Shkenken 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, (GEM_##x), (y));    \
992074c2d0Shkenken 	else								      \
1002074c2d0Shkenken 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, (ETH_##x), (y));    \
1012074c2d0Shkenken     } while(0)
1022074c2d0Shkenken 
1032074c2d0Shkenken static void	cemac_init(struct cemac_softc *);
1042074c2d0Shkenken static int	cemac_gctx(struct cemac_softc *);
1052074c2d0Shkenken static int	cemac_mediachange(struct ifnet *);
1062074c2d0Shkenken static void	cemac_mediastatus(struct ifnet *, struct ifmediareq *);
107a5cdd4b4Smsaitoh static int	cemac_mii_readreg(device_t, int, int, uint16_t *);
108a5cdd4b4Smsaitoh static int	cemac_mii_writereg(device_t, int, int, uint16_t);
1092074c2d0Shkenken static void	cemac_statchg(struct ifnet *);
1102074c2d0Shkenken static void	cemac_tick(void *);
1112074c2d0Shkenken static int	cemac_ifioctl(struct ifnet *, u_long, void *);
1122074c2d0Shkenken static void	cemac_ifstart(struct ifnet *);
1130f69512fSskrll static void	cemac_ifstart_locked(struct ifnet *);
1142074c2d0Shkenken static void	cemac_ifwatchdog(struct ifnet *);
1152074c2d0Shkenken static int	cemac_ifinit(struct ifnet *);
1162074c2d0Shkenken static void	cemac_ifstop(struct ifnet *, int);
1172074c2d0Shkenken static void	cemac_setaddr(struct ifnet *);
1182074c2d0Shkenken 
1192074c2d0Shkenken #ifdef	CEMAC_DEBUG
1202074c2d0Shkenken int cemac_debug = CEMAC_DEBUG;
1212074c2d0Shkenken #define	DPRINTFN(n, fmt)	if (cemac_debug >= (n)) printf fmt
1222074c2d0Shkenken #else
1232074c2d0Shkenken #define	DPRINTFN(n, fmt)
1242074c2d0Shkenken #endif
1252074c2d0Shkenken 
1260f69512fSskrll /*
1270f69512fSskrll  * Perform an interface watchdog reset.
1280f69512fSskrll  */
1290f69512fSskrll static void
1300f69512fSskrll cemac_handle_reset_work(struct work *work, void *arg)
1310f69512fSskrll {
1320f69512fSskrll 	struct cemac_softc * const sc = arg;
1330f69512fSskrll 	struct ifnet * const ifp = &sc->sc_ethercom.ec_if;
1340f69512fSskrll 
1350f69512fSskrll 	printf("%s: watchdog timeout -- resetting\n", ifp->if_xname);
1360f69512fSskrll 
1370f69512fSskrll 	/* Don't want ioctl operations to happen */
1380f69512fSskrll 	IFNET_LOCK(ifp);
1390f69512fSskrll 
1400f69512fSskrll 	/* reset the interface. */
1410f69512fSskrll 	cemac_ifinit(ifp);
1420f69512fSskrll 
1430f69512fSskrll 	IFNET_UNLOCK(ifp);
1440f69512fSskrll 
1450f69512fSskrll 	/*
1460f69512fSskrll 	 * There are still some upper layer processing which call
1470f69512fSskrll 	 * ifp->if_start(). e.g. ALTQ or one CPU system
1480f69512fSskrll 	 */
1490f69512fSskrll 	/* Try to get more packets going. */
1500f69512fSskrll 	ifp->if_start(ifp);
1510f69512fSskrll 
1520f69512fSskrll 	atomic_store_relaxed(&sc->sc_reset_pending, 0);
1530f69512fSskrll }
1540f69512fSskrll 
1550f69512fSskrll 
1562074c2d0Shkenken void
157b1e614ebSskrll cemac_attach_common(struct cemac_softc *sc)
1582074c2d0Shkenken {
1592074c2d0Shkenken 	uint32_t u;
1602074c2d0Shkenken 
1612074c2d0Shkenken 	aprint_naive("\n");
1622074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))
1632074c2d0Shkenken 		aprint_normal(": Cadence Gigabit Ethernet Controller\n");
1642074c2d0Shkenken 	else
1652074c2d0Shkenken 		aprint_normal(": Cadence Ethernet Controller\n");
1662074c2d0Shkenken 
1672074c2d0Shkenken 	/* configure emac: */
1682074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, 0);		// disable everything
1692074c2d0Shkenken 	CEMAC_WRITE(ETH_IDR, -1);		// disable interrupts
1702074c2d0Shkenken 	CEMAC_WRITE(ETH_RBQP, 0);		// clear receive
1712074c2d0Shkenken 	CEMAC_WRITE(ETH_TBQP, 0);		// clear transmit
1722074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))
1732074c2d0Shkenken 		CEMAC_WRITE(ETH_CFG,
1742074c2d0Shkenken 		    GEM_CFG_CLK_64 | GEM_CFG_GEN | ETH_CFG_SPD | ETH_CFG_FD);
1752074c2d0Shkenken 	else
1762074c2d0Shkenken 		CEMAC_WRITE(ETH_CFG,
1772074c2d0Shkenken 		    ETH_CFG_CLK_32 | ETH_CFG_SPD | ETH_CFG_FD | ETH_CFG_BIG);
1782074c2d0Shkenken 	//CEMAC_WRITE(ETH_TCR, 0);		// send nothing
1792074c2d0Shkenken 	//(void)CEMAC_READ(ETH_ISR);
1802074c2d0Shkenken 	u = CEMAC_READ(ETH_TSR);
1812074c2d0Shkenken 	CEMAC_WRITE(ETH_TSR, (u & (ETH_TSR_UND | ETH_TSR_COMP | ETH_TSR_BNQ
1822074c2d0Shkenken 				  | ETH_TSR_IDLE | ETH_TSR_RLE
1832074c2d0Shkenken 				  | ETH_TSR_COL | ETH_TSR_OVR)));
1842074c2d0Shkenken 	u = CEMAC_READ(ETH_RSR);
1852074c2d0Shkenken 	CEMAC_WRITE(ETH_RSR, (u & (ETH_RSR_OVR | ETH_RSR_REC | ETH_RSR_BNA)));
1862074c2d0Shkenken 
1872074c2d0Shkenken 	/* Fetch the Ethernet address from property if set. */
188b1e614ebSskrll 	prop_dictionary_t prop = device_properties(sc->sc_dev);
189b1e614ebSskrll 	prop_data_t enaddr = prop_dictionary_get(prop, "mac-address");
1902074c2d0Shkenken 
1912074c2d0Shkenken 	if (enaddr != NULL) {
1922074c2d0Shkenken 		KASSERT(prop_object_type(enaddr) == PROP_TYPE_DATA);
1932074c2d0Shkenken 		KASSERT(prop_data_size(enaddr) == ETHER_ADDR_LEN);
194047b7c3fSskrll 		memcpy(sc->sc_enaddr, prop_data_value(enaddr),
1952074c2d0Shkenken 		       ETHER_ADDR_LEN);
1962074c2d0Shkenken 	} else {
1972074c2d0Shkenken 		static const uint8_t hardcoded[ETHER_ADDR_LEN] = {
1982074c2d0Shkenken 			0x00, 0x0d, 0x10, 0x81, 0x0c, 0x94
1992074c2d0Shkenken 		};
2002074c2d0Shkenken 		memcpy(sc->sc_enaddr, hardcoded, ETHER_ADDR_LEN);
2012074c2d0Shkenken 	}
2022074c2d0Shkenken 
2032074c2d0Shkenken 	cemac_init(sc);
2042074c2d0Shkenken }
2052074c2d0Shkenken 
2062074c2d0Shkenken static int
2072074c2d0Shkenken cemac_gctx(struct cemac_softc *sc)
2082074c2d0Shkenken {
2092074c2d0Shkenken 	uint32_t tsr;
2102074c2d0Shkenken 
2112074c2d0Shkenken 	tsr = CEMAC_READ(ETH_TSR);
2122074c2d0Shkenken 	if (!ISSET(sc->cemac_flags, CEMAC_FLAG_GEM)) {
2132074c2d0Shkenken 		// no space left
2142074c2d0Shkenken 		if (!(tsr & ETH_TSR_BNQ))
2152074c2d0Shkenken 			return 0;
2162074c2d0Shkenken 	} else {
2172074c2d0Shkenken 		if (tsr & GEM_TSR_TXGO)
2182074c2d0Shkenken 			return 0;
2192074c2d0Shkenken 	}
2202074c2d0Shkenken 	CEMAC_WRITE(ETH_TSR, tsr);
2212074c2d0Shkenken 
2222074c2d0Shkenken 	// free sent frames
2232074c2d0Shkenken 	while (sc->txqc > (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM) ? 0 :
2242074c2d0Shkenken 		(tsr & ETH_TSR_IDLE ? 0 : 1))) {
2252074c2d0Shkenken 		int bi = sc->txqi % TX_QLEN;
2262074c2d0Shkenken 
2272074c2d0Shkenken 		DPRINTFN(3,("%s: TDSC[%i].Addr 0x%08x\n",
2282074c2d0Shkenken 			__FUNCTION__, bi, sc->TDSC[bi].Addr));
2292074c2d0Shkenken 		DPRINTFN(3,("%s: TDSC[%i].Info 0x%08x\n",
2302074c2d0Shkenken 			__FUNCTION__, bi, sc->TDSC[bi].Info));
2312074c2d0Shkenken 
2322074c2d0Shkenken 		bus_dmamap_sync(sc->sc_dmat, sc->txq[bi].m_dmamap, 0,
2332074c2d0Shkenken 		    sc->txq[bi].m->m_pkthdr.len, BUS_DMASYNC_POSTWRITE);
2342074c2d0Shkenken 		bus_dmamap_unload(sc->sc_dmat, sc->txq[bi].m_dmamap);
2352074c2d0Shkenken 		m_freem(sc->txq[bi].m);
2362074c2d0Shkenken 		DPRINTFN(2,("%s: freed idx #%i mbuf %p (txqc=%i)\n",
2372074c2d0Shkenken 		    __FUNCTION__, bi, sc->txq[bi].m, sc->txqc));
2382074c2d0Shkenken 		sc->txq[bi].m = NULL;
2392074c2d0Shkenken 		sc->txqi = (bi + 1) % TX_QLEN;
2402074c2d0Shkenken 		sc->txqc--;
2412074c2d0Shkenken 	}
2422074c2d0Shkenken 
2432074c2d0Shkenken 	// mark we're free
244fd79bed7Sskrll 	if (sc->sc_txbusy) {
245fd79bed7Sskrll 		sc->sc_txbusy = false;
2462074c2d0Shkenken 		/* Disable transmit-buffer-free interrupt */
2472074c2d0Shkenken 		/*CEMAC_WRITE(ETH_IDR, ETH_ISR_TBRE);*/
2482074c2d0Shkenken 	}
2492074c2d0Shkenken 
2502074c2d0Shkenken 	return 1;
2512074c2d0Shkenken }
2522074c2d0Shkenken 
2532074c2d0Shkenken int
2542074c2d0Shkenken cemac_intr(void *arg)
2552074c2d0Shkenken {
2563eb41560Sskrll 	struct cemac_softc * const sc = arg;
257c0b61f8dSskrll 	struct ifnet * const ifp = &sc->sc_ethercom.ec_if;
2582074c2d0Shkenken 	uint32_t imr, isr, ctl;
2592074c2d0Shkenken #ifdef	CEMAC_DEBUG
2602074c2d0Shkenken 	uint32_t rsr;
2612074c2d0Shkenken #endif
2622074c2d0Shkenken 	int bi;
2632074c2d0Shkenken 
2640f69512fSskrll 	mutex_enter(sc->sc_intr_lock);
2650f69512fSskrll 	if (sc->sc_stopping) {
2660f69512fSskrll 		mutex_exit(sc->sc_intr_lock);
2670f69512fSskrll 		return 0;
2680f69512fSskrll 	}
2690f69512fSskrll 
2702074c2d0Shkenken 	imr = ~CEMAC_READ(ETH_IMR);
2717b055612Smsaitoh 	if (!(imr & (ETH_ISR_RCOM | ETH_ISR_TBRE | ETH_ISR_TIDLE |
2727b055612Smsaitoh 	    ETH_ISR_RBNA | ETH_ISR_ROVR | ETH_ISR_TCOM))) {
2732074c2d0Shkenken 		// interrupt not enabled, can't be us
2740f69512fSskrll 		mutex_exit(sc->sc_intr_lock);
2752074c2d0Shkenken 		return 0;
2762074c2d0Shkenken 	}
2772074c2d0Shkenken 
2782074c2d0Shkenken 	isr = CEMAC_READ(ETH_ISR);
2792074c2d0Shkenken 	CEMAC_WRITE(ETH_ISR, isr);
2802074c2d0Shkenken 	isr &= imr;
2810f69512fSskrll 
2820f69512fSskrll 	if (isr == 0) {
2830f69512fSskrll 		mutex_exit(sc->sc_intr_lock);
2840f69512fSskrll 		return 0;
2850f69512fSskrll 	}
2860f69512fSskrll 
2872074c2d0Shkenken #ifdef	CEMAC_DEBUG
2882074c2d0Shkenken 	rsr = CEMAC_READ(ETH_RSR);		// get receive status register
2892074c2d0Shkenken #endif
290d0a9f7e6Sskrll 	DPRINTFN(2, ("%s: isr=0x%08X rsr=0x%08X imr=0x%08X\n", __FUNCTION__,
291d0a9f7e6Sskrll 	    isr, rsr, imr));
2922074c2d0Shkenken 
293d18dc548Sthorpej 	net_stat_ref_t nsr = IF_STAT_GETREF(ifp);
294d0a9f7e6Sskrll 	// out of receive buffers
295d0a9f7e6Sskrll 	if (isr & ETH_ISR_RBNA) {
296d0a9f7e6Sskrll 		// clear interrupt
297d0a9f7e6Sskrll 		CEMAC_WRITE(ETH_RSR, ETH_RSR_BNA);
298d0a9f7e6Sskrll 
299d0a9f7e6Sskrll 		ctl = CEMAC_READ(ETH_CTL);
300d0a9f7e6Sskrll 		// disable receiver
301d0a9f7e6Sskrll 		CEMAC_WRITE(ETH_CTL, ctl & ~ETH_CTL_RE);
302d0a9f7e6Sskrll 		// clear BNA bit
303d0a9f7e6Sskrll 		CEMAC_WRITE(ETH_RSR, ETH_RSR_BNA);
304d0a9f7e6Sskrll 		// re-enable receiver
305d0a9f7e6Sskrll 		CEMAC_WRITE(ETH_CTL, ctl |  ETH_CTL_RE);
306d0a9f7e6Sskrll 
307be6f2fceSriastradh 		if_statinc_ref(ifp, nsr, if_ierrors);
308be6f2fceSriastradh 		if_statinc_ref(ifp, nsr, if_ipackets);
3092074c2d0Shkenken 		DPRINTFN(1,("%s: out of receive buffers\n", __FUNCTION__));
3102074c2d0Shkenken 	}
3112074c2d0Shkenken 	if (isr & ETH_ISR_ROVR) {
312d0a9f7e6Sskrll 		// clear interrupt
313d0a9f7e6Sskrll 		CEMAC_WRITE(ETH_RSR, ETH_RSR_OVR);
314be6f2fceSriastradh 		if_statinc_ref(ifp, nsr, if_ierrors);
315be6f2fceSriastradh 		if_statinc_ref(ifp, nsr, if_ipackets);
3162074c2d0Shkenken 		DPRINTFN(1,("%s: receive overrun\n", __FUNCTION__));
3172074c2d0Shkenken 	}
3182074c2d0Shkenken 
319d0a9f7e6Sskrll 	// packet has been received!
320d0a9f7e6Sskrll 	if (isr & ETH_ISR_RCOM) {
3212074c2d0Shkenken 		uint32_t nfo;
322d0a9f7e6Sskrll 		DPRINTFN(2,("#2 RDSC[%i].INFO=0x%08X\n", sc->rxqi % RX_QLEN,
323d0a9f7e6Sskrll 		    sc->RDSC[sc->rxqi % RX_QLEN].Info));
3242074c2d0Shkenken 		while (sc->RDSC[(bi = sc->rxqi % RX_QLEN)].Addr & ETH_RDSC_F_USED) {
325eebfc9efSrjs 			int fl, csum;
3262074c2d0Shkenken 			struct mbuf *m;
3272074c2d0Shkenken 
3282074c2d0Shkenken 			nfo = sc->RDSC[bi].Info;
3292074c2d0Shkenken 			fl = (nfo & ETH_RDSC_I_LEN) - 4;
3302074c2d0Shkenken 			DPRINTFN(2,("## nfo=0x%08X\n", nfo));
3312074c2d0Shkenken 
3322074c2d0Shkenken 			MGETHDR(m, M_DONTWAIT, MT_DATA);
333a3c09998Sskrll 			if (m != NULL)
334a3c09998Sskrll 				MCLGET(m, M_DONTWAIT);
3352074c2d0Shkenken 			if (m != NULL && (m->m_flags & M_EXT)) {
336d0a9f7e6Sskrll 				bus_dmamap_sync(sc->sc_dmat,
337d0a9f7e6Sskrll 				    sc->rxq[bi].m_dmamap, 0, MCLBYTES,
338d0a9f7e6Sskrll 				    BUS_DMASYNC_POSTREAD);
3392074c2d0Shkenken 				bus_dmamap_unload(sc->sc_dmat,
3402074c2d0Shkenken 					sc->rxq[bi].m_dmamap);
341d938d837Sozaki-r 				m_set_rcvif(sc->rxq[bi].m, ifp);
3422074c2d0Shkenken 				sc->rxq[bi].m->m_pkthdr.len =
3432074c2d0Shkenken 					sc->rxq[bi].m->m_len = fl;
344eebfc9efSrjs 				switch (nfo & ETH_RDSC_I_CHKSUM) {
345eebfc9efSrjs 				case ETH_RDSC_I_CHKSUM_IP:
346eebfc9efSrjs 					csum = M_CSUM_IPv4;
347eebfc9efSrjs 					break;
348eebfc9efSrjs 				case ETH_RDSC_I_CHKSUM_UDP:
349eebfc9efSrjs 					csum = M_CSUM_IPv4 | M_CSUM_UDPv4 |
350eebfc9efSrjs 					    M_CSUM_UDPv6;
351eebfc9efSrjs 					break;
352eebfc9efSrjs 				case ETH_RDSC_I_CHKSUM_TCP:
353eebfc9efSrjs 					csum = M_CSUM_IPv4 | M_CSUM_TCPv4 |
354eebfc9efSrjs 					    M_CSUM_TCPv6;
355eebfc9efSrjs 					break;
356eebfc9efSrjs 				default:
357eebfc9efSrjs 					csum = 0;
358eebfc9efSrjs 					break;
359eebfc9efSrjs 				}
360eebfc9efSrjs 				sc->rxq[bi].m->m_pkthdr.csum_flags = csum;
3612074c2d0Shkenken 				DPRINTFN(2,("received %u bytes packet\n", fl));
3629c4cd063Sozaki-r 				if_percpuq_enqueue(ifp->if_percpuq,
3639c4cd063Sozaki-r 						   sc->rxq[bi].m);
3642074c2d0Shkenken 				if (mtod(m, intptr_t) & 3)
3652074c2d0Shkenken 					m_adj(m, mtod(m, intptr_t) & 3);
3662074c2d0Shkenken 				sc->rxq[bi].m = m;
3672074c2d0Shkenken 				bus_dmamap_load(sc->sc_dmat,
368d0a9f7e6Sskrll 				    sc->rxq[bi].m_dmamap, m->m_ext.ext_buf,
369d0a9f7e6Sskrll 					MCLBYTES, NULL, BUS_DMA_NOWAIT);
370d0a9f7e6Sskrll 				bus_dmamap_sync(sc->sc_dmat,
371d0a9f7e6Sskrll 				    sc->rxq[bi].m_dmamap, 0, MCLBYTES,
372d0a9f7e6Sskrll 				    BUS_DMASYNC_PREREAD);
3732074c2d0Shkenken 				sc->RDSC[bi].Info = 0;
3742074c2d0Shkenken 				sc->RDSC[bi].Addr =
3752074c2d0Shkenken 				    sc->rxq[bi].m_dmamap->dm_segs[0].ds_addr
3762074c2d0Shkenken 				    | (bi == (RX_QLEN-1) ? ETH_RDSC_F_WRAP : 0);
3772074c2d0Shkenken 			} else {
3782074c2d0Shkenken 				/* Drop packets until we can get replacement
3792074c2d0Shkenken 				 * empty mbufs for the RXDQ.
3802074c2d0Shkenken 				 */
3812074c2d0Shkenken 				m_freem(m);
382be6f2fceSriastradh 				if_statinc_ref(ifp, nsr, if_ierrors);
3832074c2d0Shkenken 			}
3842074c2d0Shkenken 			sc->rxqi++;
3852074c2d0Shkenken 		}
3862074c2d0Shkenken 	}
3872074c2d0Shkenken 
388d18dc548Sthorpej 	IF_STAT_PUTREF(ifp);
389d18dc548Sthorpej 
390a1d5203dSozaki-r 	if (cemac_gctx(sc) > 0)
391a1d5203dSozaki-r 		if_schedule_deferred_start(ifp);
3922074c2d0Shkenken #if 0 // reloop
3932074c2d0Shkenken 	irq = CEMAC_READ(IntStsC);
3942074c2d0Shkenken 	if ((irq & (IntSts_RxSQ | IntSts_ECI)) != 0)
3952074c2d0Shkenken 		goto begin;
3962074c2d0Shkenken #endif
3972074c2d0Shkenken 
3980f69512fSskrll 	mutex_exit(sc->sc_intr_lock);
3990f69512fSskrll 
40082a582bfSskrll 	return 1;
4012074c2d0Shkenken }
4022074c2d0Shkenken 
4032074c2d0Shkenken 
4040f69512fSskrll static int
4050f69512fSskrll cemac_ifflags_cb(struct ethercom *ec)
4060f69512fSskrll {
4070f69512fSskrll 	struct ifnet * const ifp = &ec->ec_if;
4080f69512fSskrll 	struct cemac_softc * const sc = ifp->if_softc;
4090f69512fSskrll 	int ret = 0;
4100f69512fSskrll 
4110f69512fSskrll 	KASSERT(IFNET_LOCKED(ifp));
4120f69512fSskrll 	mutex_enter(sc->sc_mcast_lock);
4130f69512fSskrll 
4140f69512fSskrll 	u_short change = ifp->if_flags ^ sc->sc_if_flags;
4150f69512fSskrll 	sc->sc_if_flags = ifp->if_flags;
4160f69512fSskrll 
4170f69512fSskrll 	if ((change & ~(IFF_CANTCHANGE | IFF_DEBUG)) != 0) {
4180f69512fSskrll 		ret = ENETRESET;
4190f69512fSskrll 	} else if ((change & IFF_PROMISC) != 0) {
4200f69512fSskrll 		if ((sc->sc_if_flags & IFF_RUNNING) != 0)
4210f69512fSskrll 			cemac_setaddr(ifp);
4220f69512fSskrll 	}
4230f69512fSskrll 	mutex_exit(sc->sc_mcast_lock);
4240f69512fSskrll 
4250f69512fSskrll 	return ret;
4260f69512fSskrll }
4270f69512fSskrll 
4282074c2d0Shkenken static void
4292074c2d0Shkenken cemac_init(struct cemac_softc *sc)
4302074c2d0Shkenken {
4312074c2d0Shkenken 	bus_dma_segment_t segs;
4322074c2d0Shkenken 	int rsegs, err, i;
433c0b61f8dSskrll 	struct ifnet * const ifp = &sc->sc_ethercom.ec_if;
4347b055612Smsaitoh 	struct mii_data * const mii = &sc->sc_mii;
4352074c2d0Shkenken 	uint32_t u;
4362074c2d0Shkenken #if 0
4372074c2d0Shkenken 	int mdcdiv = DEFAULT_MDCDIV;
4382074c2d0Shkenken #endif
4392074c2d0Shkenken 
4400f69512fSskrll 	callout_init(&sc->cemac_tick_ch, CALLOUT_MPSAFE);
4410f69512fSskrll 	callout_setfunc(&sc->cemac_tick_ch, cemac_tick, sc);
4422074c2d0Shkenken 
4432074c2d0Shkenken 	// ok...
4442074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, ETH_CTL_MPE);	// disable everything
4452074c2d0Shkenken 	CEMAC_WRITE(ETH_IDR, -1);		// disable interrupts
4462074c2d0Shkenken 	CEMAC_WRITE(ETH_RBQP, 0);		// clear receive
4472074c2d0Shkenken 	CEMAC_WRITE(ETH_TBQP, 0);		// clear transmit
4482074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))
4492074c2d0Shkenken 		CEMAC_WRITE(ETH_CFG,
4502074c2d0Shkenken 		    GEM_CFG_CLK_64 | ETH_CFG_SPD | ETH_CFG_FD | ETH_CFG_BIG);
4512074c2d0Shkenken 	else
4522074c2d0Shkenken 		CEMAC_WRITE(ETH_CFG,
4532074c2d0Shkenken 		    ETH_CFG_CLK_32 | ETH_CFG_SPD | ETH_CFG_FD | ETH_CFG_BIG);
4542074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM)) {
4552074c2d0Shkenken 		CEMAC_WRITE(GEM_DMA_CFG,
4562074c2d0Shkenken 		    __SHIFTIN((MCLBYTES + 63) / 64, GEM_DMA_CFG_RX_BUF_SIZE) |
4572074c2d0Shkenken 		    __SHIFTIN(3, GEM_DMA_CFG_RX_PKTBUF_MEMSZ_SEL) |
4582074c2d0Shkenken 		    GEM_DMA_CFG_TX_PKTBUF_MEMSZ_SEL |
4592074c2d0Shkenken 		    __SHIFTIN(16, GEM_DMA_CFG_AHB_FIXED_BURST_LEN) |
4602074c2d0Shkenken 		    GEM_DMA_CFG_DISC_WHEN_NO_AHB);
4612074c2d0Shkenken 	}
4622074c2d0Shkenken //	CEMAC_WRITE(ETH_TCR, 0);			// send nothing
4632074c2d0Shkenken //	(void)CEMAC_READ(ETH_ISR);
4642074c2d0Shkenken 	u = CEMAC_READ(ETH_TSR);
4652074c2d0Shkenken 	CEMAC_WRITE(ETH_TSR, (u & (ETH_TSR_UND | ETH_TSR_COMP | ETH_TSR_BNQ
4662074c2d0Shkenken 		    | ETH_TSR_IDLE | ETH_TSR_RLE
4672074c2d0Shkenken 		    | ETH_TSR_COL | ETH_TSR_OVR)));
4682074c2d0Shkenken 	u = CEMAC_READ(ETH_RSR);
4692074c2d0Shkenken 	CEMAC_WRITE(ETH_RSR, (u & (ETH_RSR_OVR | ETH_RSR_REC | ETH_RSR_BNA)));
4702074c2d0Shkenken 
4712074c2d0Shkenken #if 0
4722074c2d0Shkenken 	if (device_cfdata(sc->sc_dev)->cf_flags)
4732074c2d0Shkenken 		mdcdiv = device_cfdata(sc->sc_dev)->cf_flags;
4742074c2d0Shkenken #endif
4752074c2d0Shkenken 	/* set ethernet address */
4762074c2d0Shkenken 	CEMAC_GEM_WRITE(SA1L, (sc->sc_enaddr[3] << 24)
4772074c2d0Shkenken 	    | (sc->sc_enaddr[2] << 16) | (sc->sc_enaddr[1] << 8)
4782074c2d0Shkenken 	    | (sc->sc_enaddr[0]));
4792074c2d0Shkenken 	CEMAC_GEM_WRITE(SA1H, (sc->sc_enaddr[5] << 8)
4802074c2d0Shkenken 	    | (sc->sc_enaddr[4]));
4812074c2d0Shkenken 	CEMAC_GEM_WRITE(SA2L, 0);
4822074c2d0Shkenken 	CEMAC_GEM_WRITE(SA2H, 0);
4832074c2d0Shkenken 	CEMAC_GEM_WRITE(SA3L, 0);
4842074c2d0Shkenken 	CEMAC_GEM_WRITE(SA3H, 0);
4852074c2d0Shkenken 	CEMAC_GEM_WRITE(SA4L, 0);
4862074c2d0Shkenken 	CEMAC_GEM_WRITE(SA4H, 0);
4872074c2d0Shkenken 
4880f69512fSskrll 	char wqname[MAXCOMLEN];
4890f69512fSskrll 	snprintf(wqname, sizeof(wqname), "%sReset", device_xname(sc->sc_dev));
4900f69512fSskrll 	int error = workqueue_create(&sc->sc_reset_wq, wqname,
4910f69512fSskrll 	    cemac_handle_reset_work, sc, PRI_NONE, IPL_SOFTCLOCK,
4920f69512fSskrll 	    WQ_MPSAFE);
4930f69512fSskrll 	if (error) {
4940f69512fSskrll 		aprint_error_dev(sc->sc_dev,
4950f69512fSskrll 		    "unable to create reset workqueue\n");
4960f69512fSskrll 		return;
4970f69512fSskrll 	}
4980f69512fSskrll 
4990d6ed57fSskrll 	/* Allocate memory for receive queue descriptors */
50091d44c46Sskrll 	sc->rbqlen = roundup(ETH_DSC_SIZE * (RX_QLEN + 1) * 2, PAGE_SIZE);
5012074c2d0Shkenken 	DPRINTFN(1,("%s: rbqlen=%i\n", __FUNCTION__, sc->rbqlen));
5022074c2d0Shkenken 
503d0a9f7e6Sskrll 	// see EMAC errata why forced to 16384 byte boundary
5042074c2d0Shkenken 	err = bus_dmamem_alloc(sc->sc_dmat, sc->rbqlen, 0,
505d0a9f7e6Sskrll 	    MAX(16384, PAGE_SIZE), &segs, 1, &rsegs, BUS_DMA_WAITOK);
5062074c2d0Shkenken 	if (err == 0) {
5072074c2d0Shkenken 		DPRINTFN(1,("%s: -> bus_dmamem_map\n", __FUNCTION__));
5082074c2d0Shkenken 		err = bus_dmamem_map(sc->sc_dmat, &segs, 1, sc->rbqlen,
5092074c2d0Shkenken 		    &sc->rbqpage, (BUS_DMA_WAITOK | BUS_DMA_COHERENT));
5102074c2d0Shkenken 	}
5112074c2d0Shkenken 	if (err == 0) {
5122074c2d0Shkenken 		DPRINTFN(1,("%s: -> bus_dmamap_create\n", __FUNCTION__));
5132074c2d0Shkenken 		err = bus_dmamap_create(sc->sc_dmat, sc->rbqlen, 1,
5142074c2d0Shkenken 		    sc->rbqlen, MAX(16384, PAGE_SIZE), BUS_DMA_WAITOK,
5152074c2d0Shkenken 		    &sc->rbqpage_dmamap);
5162074c2d0Shkenken 	}
5172074c2d0Shkenken 	if (err == 0) {
5182074c2d0Shkenken 		DPRINTFN(1,("%s: -> bus_dmamap_load\n", __FUNCTION__));
5192074c2d0Shkenken 		err = bus_dmamap_load(sc->sc_dmat, sc->rbqpage_dmamap,
5202074c2d0Shkenken 		    sc->rbqpage, sc->rbqlen, NULL, BUS_DMA_WAITOK);
5212074c2d0Shkenken 	}
5222074c2d0Shkenken 	if (err != 0)
5232074c2d0Shkenken 		panic("%s: Cannot get DMA memory", device_xname(sc->sc_dev));
5242074c2d0Shkenken 
5252074c2d0Shkenken 	sc->rbqpage_dsaddr = sc->rbqpage_dmamap->dm_segs[0].ds_addr;
5262074c2d0Shkenken 	memset(sc->rbqpage, 0, sc->rbqlen);
5272074c2d0Shkenken 
5280d6ed57fSskrll 	/* Allocate memory for transmit queue descriptors */
52991d44c46Sskrll 	sc->tbqlen = roundup(ETH_DSC_SIZE * (TX_QLEN + 1) * 2, PAGE_SIZE);
5302074c2d0Shkenken 	DPRINTFN(1,("%s: tbqlen=%i\n", __FUNCTION__, sc->tbqlen));
5312074c2d0Shkenken 
532d0a9f7e6Sskrll 	// see EMAC errata why forced to 16384 byte boundary
5332074c2d0Shkenken 	err = bus_dmamem_alloc(sc->sc_dmat, sc->tbqlen, 0,
534d0a9f7e6Sskrll 	    MAX(16384, PAGE_SIZE), &segs, 1, &rsegs, BUS_DMA_WAITOK);
5352074c2d0Shkenken 	if (err == 0) {
5362074c2d0Shkenken 		DPRINTFN(1,("%s: -> bus_dmamem_map\n", __FUNCTION__));
5372074c2d0Shkenken 		err = bus_dmamem_map(sc->sc_dmat, &segs, 1, sc->tbqlen,
5382074c2d0Shkenken 		    &sc->tbqpage, (BUS_DMA_WAITOK | BUS_DMA_COHERENT));
5392074c2d0Shkenken 	}
5402074c2d0Shkenken 	if (err == 0) {
5412074c2d0Shkenken 		DPRINTFN(1,("%s: -> bus_dmamap_create\n", __FUNCTION__));
5422074c2d0Shkenken 		err = bus_dmamap_create(sc->sc_dmat, sc->tbqlen, 1,
5432074c2d0Shkenken 		    sc->tbqlen, MAX(16384, PAGE_SIZE), BUS_DMA_WAITOK,
5442074c2d0Shkenken 		    &sc->tbqpage_dmamap);
5452074c2d0Shkenken 	}
5462074c2d0Shkenken 	if (err == 0) {
5472074c2d0Shkenken 		DPRINTFN(1,("%s: -> bus_dmamap_load\n", __FUNCTION__));
5482074c2d0Shkenken 		err = bus_dmamap_load(sc->sc_dmat, sc->tbqpage_dmamap,
5492074c2d0Shkenken 		    sc->tbqpage, sc->tbqlen, NULL, BUS_DMA_WAITOK);
5502074c2d0Shkenken 	}
5512074c2d0Shkenken 	if (err != 0)
5522074c2d0Shkenken 		panic("%s: Cannot get DMA memory", device_xname(sc->sc_dev));
5532074c2d0Shkenken 
5542074c2d0Shkenken 	sc->tbqpage_dsaddr = sc->tbqpage_dmamap->dm_segs[0].ds_addr;
5552074c2d0Shkenken 	memset(sc->tbqpage, 0, sc->tbqlen);
5562074c2d0Shkenken 
5572074c2d0Shkenken 	/* Set up pointers to start of each queue in kernel addr space.
5582074c2d0Shkenken 	 * Each descriptor queue or status queue entry uses 2 words
5592074c2d0Shkenken 	 */
5602074c2d0Shkenken 	sc->RDSC = (void *)sc->rbqpage;
5612074c2d0Shkenken 	sc->TDSC = (void *)sc->tbqpage;
5622074c2d0Shkenken 
5632074c2d0Shkenken 	/* init TX queue */
5642074c2d0Shkenken 	for (i = 0; i < TX_QLEN; i++) {
5652074c2d0Shkenken 		sc->TDSC[i].Addr = 0;
5662074c2d0Shkenken 		sc->TDSC[i].Info = ETH_TDSC_I_USED |
5672074c2d0Shkenken 		    (i == (TX_QLEN - 1) ? ETH_TDSC_I_WRAP : 0);
5682074c2d0Shkenken 	}
5692074c2d0Shkenken 
5702074c2d0Shkenken 	/* Populate the RXQ with mbufs */
5712074c2d0Shkenken 	sc->rxqi = 0;
5722074c2d0Shkenken 	for (i = 0; i < RX_QLEN; i++) {
5732074c2d0Shkenken 		struct mbuf *m;
5742074c2d0Shkenken 
575d0a9f7e6Sskrll 		err = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES,
576d0a9f7e6Sskrll 		    PAGE_SIZE, BUS_DMA_WAITOK, &sc->rxq[i].m_dmamap);
5772074c2d0Shkenken 		if (err) {
578d0a9f7e6Sskrll 			panic("%s: dmamap_create failed: %i\n", __FUNCTION__,
579d0a9f7e6Sskrll 			    err);
5802074c2d0Shkenken 		}
5812074c2d0Shkenken 		MGETHDR(m, M_WAIT, MT_DATA);
5822074c2d0Shkenken 		MCLGET(m, M_WAIT);
5832074c2d0Shkenken 		sc->rxq[i].m = m;
5842074c2d0Shkenken 		if (mtod(m, intptr_t) & 3) {
5852074c2d0Shkenken 			m_adj(m, mtod(m, intptr_t) & 3);
5862074c2d0Shkenken 		}
5872074c2d0Shkenken 		err = bus_dmamap_load(sc->sc_dmat, sc->rxq[i].m_dmamap,
5882074c2d0Shkenken 		    m->m_ext.ext_buf, MCLBYTES, NULL,
5892074c2d0Shkenken 		    BUS_DMA_WAITOK);
5902074c2d0Shkenken 		if (err) {
5912074c2d0Shkenken 			panic("%s: dmamap_load failed: %i\n", __FUNCTION__, err);
5922074c2d0Shkenken 		}
5932074c2d0Shkenken 		sc->RDSC[i].Addr = sc->rxq[i].m_dmamap->dm_segs[0].ds_addr
5942074c2d0Shkenken 		    | (i == (RX_QLEN-1) ? ETH_RDSC_F_WRAP : 0);
5952074c2d0Shkenken 		sc->RDSC[i].Info = 0;
5962074c2d0Shkenken 		bus_dmamap_sync(sc->sc_dmat, sc->rxq[i].m_dmamap, 0,
5972074c2d0Shkenken 		    MCLBYTES, BUS_DMASYNC_PREREAD);
5982074c2d0Shkenken 	}
5992074c2d0Shkenken 
6002074c2d0Shkenken 	/* prepare transmit queue */
6012074c2d0Shkenken 	for (i = 0; i < TX_QLEN; i++) {
6022074c2d0Shkenken 		err = bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
6032074c2d0Shkenken 		    (BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW),
6042074c2d0Shkenken 		    &sc->txq[i].m_dmamap);
6052074c2d0Shkenken 		if (err)
6062074c2d0Shkenken 			panic("ARGH #1");
6072074c2d0Shkenken 		sc->txq[i].m = NULL;
6082074c2d0Shkenken 	}
6092074c2d0Shkenken 
6102074c2d0Shkenken 	/* Program each queue's start addr, cur addr, and len registers
6112074c2d0Shkenken 	 * with the physical addresses.
6122074c2d0Shkenken 	 */
6132074c2d0Shkenken 	CEMAC_WRITE(ETH_RBQP, (uint32_t)sc->rbqpage_dsaddr);
6142074c2d0Shkenken 	CEMAC_WRITE(ETH_TBQP, (uint32_t)sc->tbqpage_dsaddr);
6152074c2d0Shkenken 
6160f69512fSskrll 	sc->sc_mcast_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_SOFTNET);
6170f69512fSskrll 	sc->sc_intr_lock = mutex_obj_alloc(MUTEX_DEFAULT, IPL_NET);
6180f69512fSskrll 
6192074c2d0Shkenken 	/* Divide HCLK by 32 for MDC clock */
6207b055612Smsaitoh 	sc->sc_ethercom.ec_mii = mii;
6217b055612Smsaitoh 	mii->mii_ifp = ifp;
6227b055612Smsaitoh 	mii->mii_readreg = cemac_mii_readreg;
6237b055612Smsaitoh 	mii->mii_writereg = cemac_mii_writereg;
6247b055612Smsaitoh 	mii->mii_statchg = cemac_statchg;
6257b055612Smsaitoh 	ifmedia_init(&mii->mii_media, IFM_IMASK, cemac_mediachange,
6262074c2d0Shkenken 	    cemac_mediastatus);
627*1b97a28dSlloyd 	mii_attach(sc->sc_dev, mii, 0xffffffff, sc->sc_phyno, MII_OFFSET_ANY, 0);
6287b055612Smsaitoh 	ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
6292074c2d0Shkenken 
6302074c2d0Shkenken #if 0
6312074c2d0Shkenken 	// enable / disable interrupts
6322074c2d0Shkenken 	CEMAC_WRITE(ETH_IDR, -1);
6332074c2d0Shkenken 	CEMAC_WRITE(ETH_IER, ETH_ISR_RCOM | ETH_ISR_TBRE | ETH_ISR_TIDLE
6342074c2d0Shkenken 	    | ETH_ISR_RBNA | ETH_ISR_ROVR | ETH_ISR_TCOM);
6352074c2d0Shkenken //	(void)CEMAC_READ(ETH_ISR); // why
6362074c2d0Shkenken 
6372074c2d0Shkenken 	// enable transmitter / receiver
6382074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, ETH_CTL_TE | ETH_CTL_RE | ETH_CTL_ISR
6392074c2d0Shkenken 	    | ETH_CTL_CSR | ETH_CTL_MPE);
6402074c2d0Shkenken #endif
6412074c2d0Shkenken 	/*
642eebfc9efSrjs 	 * We can support hardware checksumming.
643eebfc9efSrjs 	 */
644eebfc9efSrjs 	ifp->if_capabilities |=
645eebfc9efSrjs 	    IFCAP_CSUM_IPv4_Tx | IFCAP_CSUM_IPv4_Rx |
646eebfc9efSrjs 	    IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_TCPv4_Rx |
647eebfc9efSrjs 	    IFCAP_CSUM_UDPv4_Tx | IFCAP_CSUM_UDPv4_Rx |
648eebfc9efSrjs 	    IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_TCPv6_Rx |
649eebfc9efSrjs 	    IFCAP_CSUM_UDPv6_Tx | IFCAP_CSUM_UDPv6_Rx;
650eebfc9efSrjs 
651eebfc9efSrjs 	/*
6522074c2d0Shkenken 	 * We can support 802.1Q VLAN-sized frames.
6532074c2d0Shkenken 	 */
6542074c2d0Shkenken 	sc->sc_ethercom.ec_capabilities |= ETHERCAP_VLAN_MTU;
6552074c2d0Shkenken 
6562074c2d0Shkenken 	strcpy(ifp->if_xname, device_xname(sc->sc_dev));
657091e1526Smsaitoh 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
6580f69512fSskrll 	ifp->if_extflags = IFEF_MPSAFE;
6592074c2d0Shkenken 	ifp->if_ioctl = cemac_ifioctl;
6602074c2d0Shkenken 	ifp->if_start = cemac_ifstart;
6612074c2d0Shkenken 	ifp->if_watchdog = cemac_ifwatchdog;
6622074c2d0Shkenken 	ifp->if_init = cemac_ifinit;
6632074c2d0Shkenken 	ifp->if_stop = cemac_ifstop;
6642074c2d0Shkenken 	ifp->if_softc = sc;
6652074c2d0Shkenken 	IFQ_SET_READY(&ifp->if_snd);
6662074c2d0Shkenken 	if_attach(ifp);
667a1d5203dSozaki-r 	if_deferred_start_init(ifp, NULL);
6682074c2d0Shkenken 	ether_ifattach(ifp, (sc)->sc_enaddr);
6690f69512fSskrll 	ether_set_ifflags_cb(&sc->sc_ethercom, cemac_ifflags_cb);
6702074c2d0Shkenken }
6712074c2d0Shkenken 
6722074c2d0Shkenken static int
6732074c2d0Shkenken cemac_mediachange(struct ifnet *ifp)
6742074c2d0Shkenken {
6752074c2d0Shkenken 	if (ifp->if_flags & IFF_UP)
6762074c2d0Shkenken 		cemac_ifinit(ifp);
67782a582bfSskrll 	return 0;
6782074c2d0Shkenken }
6792074c2d0Shkenken 
6802074c2d0Shkenken static void
6812074c2d0Shkenken cemac_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
6822074c2d0Shkenken {
6833eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
6842074c2d0Shkenken 
6852074c2d0Shkenken 	mii_pollstat(&sc->sc_mii);
6862074c2d0Shkenken 	ifmr->ifm_active = sc->sc_mii.mii_media_active;
6872074c2d0Shkenken 	ifmr->ifm_status = sc->sc_mii.mii_media_status;
6882074c2d0Shkenken }
6892074c2d0Shkenken 
6902074c2d0Shkenken 
6912074c2d0Shkenken static int
692a5cdd4b4Smsaitoh cemac_mii_readreg(device_t self, int phy, int reg, uint16_t *val)
6932074c2d0Shkenken {
6943eb41560Sskrll 	struct cemac_softc * const sc = device_private(self);
6952074c2d0Shkenken 
6962074c2d0Shkenken 	CEMAC_WRITE(ETH_MAN, (ETH_MAN_HIGH | ETH_MAN_RW_RD
6972074c2d0Shkenken 			     | ((phy << ETH_MAN_PHYA_SHIFT) & ETH_MAN_PHYA)
6982074c2d0Shkenken 			     | ((reg << ETH_MAN_REGA_SHIFT) & ETH_MAN_REGA)
6992074c2d0Shkenken 			     | ETH_MAN_CODE_IEEE802_3));
7007b055612Smsaitoh 	while (!(CEMAC_READ(ETH_SR) & ETH_SR_IDLE))
7017b055612Smsaitoh 		;
7022074c2d0Shkenken 
703a5cdd4b4Smsaitoh 	*val = CEMAC_READ(ETH_MAN) & ETH_MAN_DATA;
704a5cdd4b4Smsaitoh 	return 0;
7052074c2d0Shkenken }
7062074c2d0Shkenken 
707a5cdd4b4Smsaitoh static int
708a5cdd4b4Smsaitoh cemac_mii_writereg(device_t self, int phy, int reg, uint16_t val)
7092074c2d0Shkenken {
7103eb41560Sskrll 	struct cemac_softc * const sc = device_private(self);
7112074c2d0Shkenken 
7122074c2d0Shkenken 	CEMAC_WRITE(ETH_MAN, (ETH_MAN_HIGH | ETH_MAN_RW_WR
7132074c2d0Shkenken 			     | ((phy << ETH_MAN_PHYA_SHIFT) & ETH_MAN_PHYA)
7142074c2d0Shkenken 			     | ((reg << ETH_MAN_REGA_SHIFT) & ETH_MAN_REGA)
7152074c2d0Shkenken 			     | ETH_MAN_CODE_IEEE802_3
7162074c2d0Shkenken 			     | (val & ETH_MAN_DATA)));
7177b055612Smsaitoh 	while (!(CEMAC_READ(ETH_SR) & ETH_SR_IDLE))
7187b055612Smsaitoh 		;
719a5cdd4b4Smsaitoh 
720a5cdd4b4Smsaitoh 	return 0;
7212074c2d0Shkenken }
7222074c2d0Shkenken 
7232074c2d0Shkenken 
7242074c2d0Shkenken static void
7252074c2d0Shkenken cemac_statchg(struct ifnet *ifp)
7262074c2d0Shkenken {
7273eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
7282074c2d0Shkenken 	struct mii_data *mii = &sc->sc_mii;
7292074c2d0Shkenken 	uint32_t reg;
7302074c2d0Shkenken 
7312074c2d0Shkenken 	/*
7322074c2d0Shkenken 	 * We must keep the MAC and the PHY in sync as
7332074c2d0Shkenken 	 * to the status of full-duplex!
7342074c2d0Shkenken 	 */
7352074c2d0Shkenken 	reg = CEMAC_READ(ETH_CFG);
7362074c2d0Shkenken 	reg &= ~ETH_CFG_FD;
7372074c2d0Shkenken 	if (sc->sc_mii.mii_media_active & IFM_FDX)
7382074c2d0Shkenken 		reg |= ETH_CFG_FD;
7392074c2d0Shkenken 
7402074c2d0Shkenken 	reg &= ~ETH_CFG_SPD;
7412074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))
7422074c2d0Shkenken 		reg &= ~GEM_CFG_GEN;
7432074c2d0Shkenken 	switch (IFM_SUBTYPE(mii->mii_media_active)) {
7442074c2d0Shkenken 	case IFM_10_T:
7452074c2d0Shkenken 		break;
7462074c2d0Shkenken 	case IFM_100_TX:
7472074c2d0Shkenken 		reg |= ETH_CFG_SPD;
7482074c2d0Shkenken 		break;
7492074c2d0Shkenken 	case IFM_1000_T:
7502074c2d0Shkenken 		reg |= ETH_CFG_SPD | GEM_CFG_GEN;
7512074c2d0Shkenken 		break;
7522074c2d0Shkenken 	default:
7532074c2d0Shkenken 		break;
7542074c2d0Shkenken 	}
7552074c2d0Shkenken 	CEMAC_WRITE(ETH_CFG, reg);
7562074c2d0Shkenken }
7572074c2d0Shkenken 
7580f69512fSskrll static bool
7590f69512fSskrll cemac_watchdog_check(struct cemac_softc * const sc)
7600f69512fSskrll {
7610f69512fSskrll 
7620f69512fSskrll 	KASSERT(mutex_owned(sc->sc_intr_lock));
7630f69512fSskrll 
7640f69512fSskrll 	if (!sc->sc_tx_sending)
7650f69512fSskrll 		return true;
7660f69512fSskrll 
7670f69512fSskrll 	if (time_uptime - sc->sc_tx_lastsent <= cemac_watchdog_timeout)
7680f69512fSskrll 		return true;
7690f69512fSskrll 
7700f69512fSskrll 	return false;
7710f69512fSskrll }
7720f69512fSskrll 
7730f69512fSskrll static bool
7740f69512fSskrll cemac_watchdog_tick(struct ifnet *ifp)
7750f69512fSskrll {
7760f69512fSskrll 	struct cemac_softc * const sc = ifp->if_softc;
7770f69512fSskrll 
7780f69512fSskrll 	KASSERT(mutex_owned(sc->sc_intr_lock));
7790f69512fSskrll 
7800f69512fSskrll 	if (!sc->sc_trigger_reset && cemac_watchdog_check(sc))
7810f69512fSskrll 		return true;
7820f69512fSskrll 
7830f69512fSskrll 	if (atomic_swap_uint(&sc->sc_reset_pending, 1) == 0)
7840f69512fSskrll 		workqueue_enqueue(sc->sc_reset_wq, &sc->sc_reset_work, NULL);
7850f69512fSskrll 
7860f69512fSskrll 	return false;
7870f69512fSskrll }
7880f69512fSskrll 
7890f69512fSskrll 
7902074c2d0Shkenken static void
7912074c2d0Shkenken cemac_tick(void *arg)
7922074c2d0Shkenken {
7933eb41560Sskrll 	struct cemac_softc * const sc = arg;
794c0b61f8dSskrll 	struct ifnet * const ifp = &sc->sc_ethercom.ec_if;
7950f69512fSskrll 
7960f69512fSskrll 	mutex_enter(sc->sc_intr_lock);
7970f69512fSskrll 	if (sc->sc_stopping) {
7980f69512fSskrll 		mutex_exit(sc->sc_intr_lock);
7990f69512fSskrll 		return;
8000f69512fSskrll 	}
8012074c2d0Shkenken 
802314f76f8Srjs 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))
803d18dc548Sthorpej 		if_statadd(ifp, if_collisions,
804d18dc548Sthorpej 		    CEMAC_READ(GEM_SCOL) + CEMAC_READ(GEM_MCOL));
805314f76f8Srjs 	else
806d18dc548Sthorpej 		if_statadd(ifp, if_collisions,
807d18dc548Sthorpej 		    CEMAC_READ(ETH_SCOL) + CEMAC_READ(ETH_MCOL));
808314f76f8Srjs 
8092074c2d0Shkenken 	/* These misses are ok, they will happen if the RAM/CPU can't keep up */
8102074c2d0Shkenken 	if (!ISSET(sc->cemac_flags, CEMAC_FLAG_GEM)) {
8112074c2d0Shkenken 		uint32_t misses = CEMAC_READ(ETH_DRFC);
8122074c2d0Shkenken 		if (misses > 0)
813f74ff419Srjs 			aprint_normal_ifnet(ifp, "%d rx misses\n", misses);
8142074c2d0Shkenken 	}
8152074c2d0Shkenken 
8162074c2d0Shkenken 	mii_tick(&sc->sc_mii);
8170f69512fSskrll 
8180f69512fSskrll 	const bool ok = cemac_watchdog_tick(ifp);
8190f69512fSskrll 	if (ok)
8200f69512fSskrll 		callout_schedule(&sc->cemac_tick_ch, hz);
8210f69512fSskrll 
8220f69512fSskrll 	mutex_exit(sc->sc_intr_lock);
8232074c2d0Shkenken }
8242074c2d0Shkenken 
8252074c2d0Shkenken 
8262074c2d0Shkenken static int
8272074c2d0Shkenken cemac_ifioctl(struct ifnet *ifp, u_long cmd, void *data)
8282074c2d0Shkenken {
8290f69512fSskrll 	struct cemac_softc * const sc = ifp->if_softc;
8300f69512fSskrll 	int error;
8312074c2d0Shkenken 
8322074c2d0Shkenken  	switch (cmd) {
8330f69512fSskrll 	case SIOCADDMULTI:
8340f69512fSskrll 	case SIOCDELMULTI:
835eebfc9efSrjs 		break;
8360f69512fSskrll  	default:
8370f69512fSskrll 		KASSERT(IFNET_LOCKED(ifp));
8380f69512fSskrll 	}
8390f69512fSskrll 
8400f69512fSskrll 	const int s = splnet();
8410f69512fSskrll 	error = ether_ioctl(ifp, cmd, data);
8420f69512fSskrll 	splx(s);
8430f69512fSskrll 
8440f69512fSskrll 	if (error == ENETRESET) {
8452074c2d0Shkenken  		error = 0;
846eebfc9efSrjs 
8470f69512fSskrll 		if (cmd == SIOCADDMULTI || cmd == SIOCDELMULTI) {
8480f69512fSskrll 			mutex_enter(sc->sc_mcast_lock);
8490f69512fSskrll 			if ((sc->sc_if_flags & IFF_RUNNING) != 0)
850eebfc9efSrjs 				cemac_setaddr(ifp);
8510f69512fSskrll 
8520f69512fSskrll 			mutex_exit(sc->sc_mcast_lock);
8532074c2d0Shkenken  		}
8542074c2d0Shkenken  	}
8550f69512fSskrll 
8562074c2d0Shkenken 	return error;
8572074c2d0Shkenken }
8582074c2d0Shkenken 
8590f69512fSskrll 
8600f69512fSskrll 
8612074c2d0Shkenken static void
8622074c2d0Shkenken cemac_ifstart(struct ifnet *ifp)
8632074c2d0Shkenken {
8643eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
8650f69512fSskrll 	KASSERT(if_is_mpsafe(ifp));
8660f69512fSskrll 
8670f69512fSskrll 	mutex_enter(sc->sc_intr_lock);
8680f69512fSskrll 	if (!sc->sc_stopping) {
8690f69512fSskrll 		cemac_ifstart_locked(ifp);
8700f69512fSskrll 	}
8710f69512fSskrll 	mutex_exit(sc->sc_intr_lock);
8720f69512fSskrll }
8730f69512fSskrll 
8740f69512fSskrll static void
8750f69512fSskrll cemac_ifstart_locked(struct ifnet *ifp)
8760f69512fSskrll {
8770f69512fSskrll 	struct cemac_softc * const sc = ifp->if_softc;
8782074c2d0Shkenken 	struct mbuf *m;
8792074c2d0Shkenken 	bus_dma_segment_t *segs;
8800f69512fSskrll 	int bi, err, nsegs;
8812074c2d0Shkenken 
8820f69512fSskrll 	KASSERT(mutex_owned(sc->sc_intr_lock));
8830f69512fSskrll 
8842074c2d0Shkenken start:
8852074c2d0Shkenken 	if (cemac_gctx(sc) == 0) {
8862074c2d0Shkenken 		/* Enable transmit-buffer-free interrupt */
8872074c2d0Shkenken 		CEMAC_WRITE(ETH_IER, ETH_ISR_TBRE);
888fd79bed7Sskrll 		sc->sc_txbusy = true;
8892074c2d0Shkenken 		return;
8902074c2d0Shkenken 	}
8912074c2d0Shkenken 
8922074c2d0Shkenken 	IFQ_POLL(&ifp->if_snd, m);
8932074c2d0Shkenken 	if (m == NULL) {
8942074c2d0Shkenken 		return;
8952074c2d0Shkenken 	}
8962074c2d0Shkenken 
8972074c2d0Shkenken 	bi = (sc->txqi + sc->txqc) % TX_QLEN;
8982074c2d0Shkenken 	if ((err = bus_dmamap_load_mbuf(sc->sc_dmat, sc->txq[bi].m_dmamap, m,
8992074c2d0Shkenken 		BUS_DMA_NOWAIT)) ||
9002074c2d0Shkenken 		sc->txq[bi].m_dmamap->dm_segs[0].ds_addr & 0x3 ||
9012074c2d0Shkenken 		sc->txq[bi].m_dmamap->dm_nsegs > 1) {
9022074c2d0Shkenken 		/* Copy entire mbuf chain to new single */
9032074c2d0Shkenken 		struct mbuf *mn;
9042074c2d0Shkenken 
9052074c2d0Shkenken 		if (err == 0)
9062074c2d0Shkenken 			bus_dmamap_unload(sc->sc_dmat, sc->txq[bi].m_dmamap);
9072074c2d0Shkenken 
9082074c2d0Shkenken 		MGETHDR(mn, M_DONTWAIT, MT_DATA);
909a3c09998Sskrll 		if (mn == NULL)
9100f69512fSskrll 			return;
9112074c2d0Shkenken 		if (m->m_pkthdr.len > MHLEN) {
9122074c2d0Shkenken 			MCLGET(mn, M_DONTWAIT);
9132074c2d0Shkenken 			if ((mn->m_flags & M_EXT) == 0) {
9142074c2d0Shkenken 				m_freem(mn);
9150f69512fSskrll 				return;
9162074c2d0Shkenken 			}
9172074c2d0Shkenken 		}
9182074c2d0Shkenken 		m_copydata(m, 0, m->m_pkthdr.len, mtod(mn, void *));
9192074c2d0Shkenken 		mn->m_pkthdr.len = mn->m_len = m->m_pkthdr.len;
9202074c2d0Shkenken 		IFQ_DEQUEUE(&ifp->if_snd, m);
9212074c2d0Shkenken 		m_freem(m);
9222074c2d0Shkenken 		m = mn;
9232074c2d0Shkenken 		bus_dmamap_load_mbuf(sc->sc_dmat, sc->txq[bi].m_dmamap, m,
9242074c2d0Shkenken 		    BUS_DMA_NOWAIT);
9252074c2d0Shkenken 	} else {
9262074c2d0Shkenken 		IFQ_DEQUEUE(&ifp->if_snd, m);
9272074c2d0Shkenken 	}
9282074c2d0Shkenken 
9293cd62456Smsaitoh 	bpf_mtap(ifp, m, BPF_D_OUT);
9302074c2d0Shkenken 
9312074c2d0Shkenken 	nsegs = sc->txq[bi].m_dmamap->dm_nsegs;
9322074c2d0Shkenken 	segs = sc->txq[bi].m_dmamap->dm_segs;
9332074c2d0Shkenken 	if (nsegs > 1)
9342074c2d0Shkenken 		panic("#### ARGH #2");
9352074c2d0Shkenken 
9362074c2d0Shkenken 	sc->txq[bi].m = m;
9372074c2d0Shkenken 	sc->txqc++;
9382074c2d0Shkenken 
939d0a9f7e6Sskrll 	DPRINTFN(2,("%s: start sending idx #%i mbuf %p (txqc=%i, phys %p), "
940d0a9f7e6Sskrll 	    "len=%u\n", __FUNCTION__, bi, sc->txq[bi].m, sc->txqc,
941d0a9f7e6Sskrll 	     (void *)segs->ds_addr, (unsigned)m->m_pkthdr.len));
9422074c2d0Shkenken #ifdef	DIAGNOSTIC
9432074c2d0Shkenken 	if (sc->txqc > TX_QLEN)
9442074c2d0Shkenken 		panic("%s: txqc %i > %i", __FUNCTION__, sc->txqc, TX_QLEN);
9452074c2d0Shkenken #endif
9462074c2d0Shkenken 
9472074c2d0Shkenken 	bus_dmamap_sync(sc->sc_dmat, sc->txq[bi].m_dmamap, 0,
948d0a9f7e6Sskrll 	    sc->txq[bi].m_dmamap->dm_mapsize, BUS_DMASYNC_PREWRITE);
9492074c2d0Shkenken 
9502074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM)) {
9512074c2d0Shkenken 		sc->TDSC[bi].Addr = segs->ds_addr;
952d0a9f7e6Sskrll 		sc->TDSC[bi].Info =
953d0a9f7e6Sskrll 		    __SHIFTIN(m->m_pkthdr.len, ETH_TDSC_I_LEN) |
954d0a9f7e6Sskrll 		    ETH_TDSC_I_LAST_BUF |
955d0a9f7e6Sskrll 		    (bi == (TX_QLEN - 1) ? ETH_TDSC_I_WRAP : 0);
9562074c2d0Shkenken 
9572074c2d0Shkenken 		DPRINTFN(3,("%s: TDSC[%i].Addr 0x%08x\n",
9582074c2d0Shkenken 			__FUNCTION__, bi, sc->TDSC[bi].Addr));
9592074c2d0Shkenken 		DPRINTFN(3,("%s: TDSC[%i].Info 0x%08x\n",
9602074c2d0Shkenken 			__FUNCTION__, bi, sc->TDSC[bi].Info));
9612074c2d0Shkenken 
9622074c2d0Shkenken 		uint32_t ctl = CEMAC_READ(ETH_CTL) | GEM_CTL_STARTTX;
9632074c2d0Shkenken 		CEMAC_WRITE(ETH_CTL, ctl);
964d0a9f7e6Sskrll 		DPRINTFN(3,("%s: ETH_CTL 0x%08x\n", __FUNCTION__,
965d0a9f7e6Sskrll 		    CEMAC_READ(ETH_CTL)));
9662074c2d0Shkenken 	} else {
9672074c2d0Shkenken 		CEMAC_WRITE(ETH_TAR, segs->ds_addr);
9682074c2d0Shkenken 		CEMAC_WRITE(ETH_TCR, m->m_pkthdr.len);
9692074c2d0Shkenken 	}
9700f69512fSskrll 	sc->sc_tx_lastsent = time_uptime;
9710f69512fSskrll 
9722074c2d0Shkenken 	if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
9732074c2d0Shkenken 		goto start;
9742074c2d0Shkenken 
9752074c2d0Shkenken 	return;
9762074c2d0Shkenken }
9772074c2d0Shkenken 
9782074c2d0Shkenken static void
9792074c2d0Shkenken cemac_ifwatchdog(struct ifnet *ifp)
9802074c2d0Shkenken {
9813eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
9822074c2d0Shkenken 
9832074c2d0Shkenken 	if ((ifp->if_flags & IFF_RUNNING) == 0)
9842074c2d0Shkenken 		return;
985f74ff419Srjs 	aprint_error_ifnet(ifp, "device timeout, CTL = 0x%08x, CFG = 0x%08x\n",
986f74ff419Srjs 	    CEMAC_READ(ETH_CTL), CEMAC_READ(ETH_CFG));
9872074c2d0Shkenken }
9882074c2d0Shkenken 
9892074c2d0Shkenken static int
9902074c2d0Shkenken cemac_ifinit(struct ifnet *ifp)
9912074c2d0Shkenken {
9923eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
993eebfc9efSrjs 	uint32_t dma, cfg;
9942074c2d0Shkenken 
9950f69512fSskrll 	ASSERT_SLEEPABLE();
9960f69512fSskrll 	KASSERT(IFNET_LOCKED(ifp));
9970f69512fSskrll 
9980f69512fSskrll 	/* Cancel pending I/O and flush buffers. */
9990f69512fSskrll 	cemac_ifstop(ifp, 0);
10002074c2d0Shkenken 
1001eebfc9efSrjs 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM)) {
1002eebfc9efSrjs 
1003eebfc9efSrjs 		if (ifp->if_capenable &
1004eebfc9efSrjs 		    (IFCAP_CSUM_IPv4_Tx |
1005eebfc9efSrjs 			IFCAP_CSUM_TCPv4_Tx | IFCAP_CSUM_UDPv4_Tx |
1006eebfc9efSrjs 			IFCAP_CSUM_TCPv6_Tx | IFCAP_CSUM_UDPv6_Tx)) {
1007eebfc9efSrjs 			dma = CEMAC_READ(GEM_DMA_CFG);
1008eebfc9efSrjs 			dma |= GEM_DMA_CFG_CHKSUM_GEN_OFFLOAD_EN;
1009eebfc9efSrjs 			CEMAC_WRITE(GEM_DMA_CFG, dma);
1010eebfc9efSrjs 		}
1011eebfc9efSrjs 		if (ifp->if_capenable &
1012eebfc9efSrjs 		    (IFCAP_CSUM_IPv4_Rx |
1013eebfc9efSrjs 			IFCAP_CSUM_TCPv4_Rx | IFCAP_CSUM_UDPv4_Rx |
1014eebfc9efSrjs 			IFCAP_CSUM_TCPv6_Rx | IFCAP_CSUM_UDPv6_Rx)) {
1015eebfc9efSrjs 			cfg = CEMAC_READ(ETH_CFG);
1016809fcef2Sskrll 			cfg |= GEM_CFG_RXCOEN;
1017eebfc9efSrjs 			CEMAC_WRITE(ETH_CFG, cfg);
1018eebfc9efSrjs 		}
1019eebfc9efSrjs 	}
1020eebfc9efSrjs 
10212074c2d0Shkenken 	// enable interrupts
10222074c2d0Shkenken 	CEMAC_WRITE(ETH_IDR, -1);
10232074c2d0Shkenken 	CEMAC_WRITE(ETH_IER, ETH_ISR_RCOM | ETH_ISR_TBRE | ETH_ISR_TIDLE
10242074c2d0Shkenken 	    | ETH_ISR_RBNA | ETH_ISR_ROVR | ETH_ISR_TCOM);
10252074c2d0Shkenken 
10262074c2d0Shkenken 	// enable transmitter / receiver
10272074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, ETH_CTL_TE | ETH_CTL_RE | ETH_CTL_ISR
10282074c2d0Shkenken 	    | ETH_CTL_CSR | ETH_CTL_MPE);
10292074c2d0Shkenken 
10302074c2d0Shkenken 	mii_mediachg(&sc->sc_mii);
10312074c2d0Shkenken 	callout_reset(&sc->cemac_tick_ch, hz, cemac_tick, sc);
10322074c2d0Shkenken 	ifp->if_flags |= IFF_RUNNING;
10330f69512fSskrll 
10340f69512fSskrll 	mutex_enter(sc->sc_intr_lock);
10350f69512fSskrll 	sc->sc_stopping = false;
10360f69512fSskrll 	mutex_exit(sc->sc_intr_lock);
10370f69512fSskrll 
10382074c2d0Shkenken 	return 0;
10392074c2d0Shkenken }
10402074c2d0Shkenken 
10412074c2d0Shkenken static void
10422074c2d0Shkenken cemac_ifstop(struct ifnet *ifp, int disable)
10432074c2d0Shkenken {
10442074c2d0Shkenken //	uint32_t u;
10453eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
10462074c2d0Shkenken 
10470f69512fSskrll 	ASSERT_SLEEPABLE();
10480f69512fSskrll 	KASSERT(IFNET_LOCKED(ifp));
10490f69512fSskrll 
10500f69512fSskrll 	ifp->if_flags &= ~IFF_RUNNING;
10510f69512fSskrll 
10520f69512fSskrll 	mutex_enter(sc->sc_mcast_lock);
10530f69512fSskrll 	sc->sc_if_flags = ifp->if_flags;
10540f69512fSskrll 	mutex_exit(sc->sc_mcast_lock);
10550f69512fSskrll 
10560f69512fSskrll 	mutex_enter(sc->sc_intr_lock);
10570f69512fSskrll 	sc->sc_stopping = true;
10580f69512fSskrll 	mutex_exit(sc->sc_intr_lock);
10590f69512fSskrll 
10602074c2d0Shkenken #if 0
10612074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, ETH_CTL_MPE);	// disable everything
10622074c2d0Shkenken 	CEMAC_WRITE(ETH_IDR, -1);		// disable interrupts
10632074c2d0Shkenken //	CEMAC_WRITE(ETH_RBQP, 0);		// clear receive
10642074c2d0Shkenken 	if (ISSET(sc->cemac_flags, CEMAC_FLAG_GEM))
10652074c2d0Shkenken 		CEMAC_WRITE(ETH_CFG,
10662074c2d0Shkenken 		    GEM_CFG_CLK_64 | ETH_CFG_SPD | ETH_CFG_FD | ETH_CFG_BIG);
10672074c2d0Shkenken 	else
10682074c2d0Shkenken 		CEMAC_WRITE(ETH_CFG,
10692074c2d0Shkenken 		    ETH_CFG_CLK_32 | ETH_CFG_SPD | ETH_CFG_FD | ETH_CFG_BIG);
10702074c2d0Shkenken //	CEMAC_WRITE(ETH_TCR, 0);			// send nothing
10712074c2d0Shkenken //	(void)CEMAC_READ(ETH_ISR);
10722074c2d0Shkenken 	u = CEMAC_READ(ETH_TSR);
10732074c2d0Shkenken 	CEMAC_WRITE(ETH_TSR, (u & (ETH_TSR_UND | ETH_TSR_COMP | ETH_TSR_BNQ
10742074c2d0Shkenken 				  | ETH_TSR_IDLE | ETH_TSR_RLE
10752074c2d0Shkenken 				  | ETH_TSR_COL | ETH_TSR_OVR)));
10762074c2d0Shkenken 	u = CEMAC_READ(ETH_RSR);
10772074c2d0Shkenken 	CEMAC_WRITE(ETH_RSR, (u & (ETH_RSR_OVR | ETH_RSR_REC | ETH_RSR_BNA)));
10782074c2d0Shkenken #endif
10790f69512fSskrll 	callout_halt(&sc->cemac_tick_ch, NULL);
10802074c2d0Shkenken 
10812074c2d0Shkenken 	/* Down the MII. */
10822074c2d0Shkenken 	mii_down(&sc->sc_mii);
10832074c2d0Shkenken 
1084e11f9b38Sthorpej 	ifp->if_flags &= ~IFF_RUNNING;
1085fd79bed7Sskrll 	sc->sc_txbusy = false;
10862074c2d0Shkenken 	sc->sc_mii.mii_media_status &= ~IFM_ACTIVE;
10872074c2d0Shkenken }
10882074c2d0Shkenken 
10892074c2d0Shkenken static void
10902074c2d0Shkenken cemac_setaddr(struct ifnet *ifp)
10912074c2d0Shkenken {
10923eb41560Sskrll 	struct cemac_softc * const sc = ifp->if_softc;
10937b055612Smsaitoh 	struct ethercom *ec = &sc->sc_ethercom;
10942074c2d0Shkenken 	struct ether_multi *enm;
10952074c2d0Shkenken 	struct ether_multistep step;
10962074c2d0Shkenken 	uint8_t ias[3][ETHER_ADDR_LEN];
10972074c2d0Shkenken 	uint32_t h, nma = 0, hashes[2] = { 0, 0 };
10982074c2d0Shkenken 	uint32_t ctl = CEMAC_READ(ETH_CTL);
10992074c2d0Shkenken 	uint32_t cfg = CEMAC_READ(ETH_CFG);
11002074c2d0Shkenken 
11010f69512fSskrll 	KASSERT(mutex_owned(sc->sc_mcast_lock));
11020f69512fSskrll 
11032074c2d0Shkenken 	/* disable receiver temporarily */
11042074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, ctl & ~ETH_CTL_RE);
11052074c2d0Shkenken 
11062074c2d0Shkenken 	cfg &= ~(ETH_CFG_MTI | ETH_CFG_UNI | ETH_CFG_CAF | ETH_CFG_UNI);
11072074c2d0Shkenken 
11080f69512fSskrll 	if (sc->sc_if_flags & IFF_PROMISC) {
11092074c2d0Shkenken 		cfg |=	ETH_CFG_CAF;
11102074c2d0Shkenken 	} else {
11112074c2d0Shkenken 		cfg &= ~ETH_CFG_CAF;
11122074c2d0Shkenken 	}
11132074c2d0Shkenken 
11142074c2d0Shkenken 	// ETH_CFG_BIG?
11152074c2d0Shkenken 
111683759283Smsaitoh 	ETHER_LOCK(ec);
11170f69512fSskrll 	ec->ec_flags &= ~ETHER_F_ALLMULTI;
11180f69512fSskrll 
11197b055612Smsaitoh 	ETHER_FIRST_MULTI(step, ec, enm);
11202074c2d0Shkenken 	while (enm != NULL) {
11212074c2d0Shkenken 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
11222074c2d0Shkenken 			/*
11232074c2d0Shkenken 			 * We must listen to a range of multicast addresses.
11242074c2d0Shkenken 			 * For now, just accept all multicasts, rather than
11252074c2d0Shkenken 			 * trying to set only those filter bits needed to match
11262074c2d0Shkenken 			 * the range.  (At this time, the only use of address
11272074c2d0Shkenken 			 * ranges is for IP multicast routing, for which the
11282074c2d0Shkenken 			 * range is big enough to require all bits set.)
11292074c2d0Shkenken 			 */
113040b201d5Srjs 			cfg |= ETH_CFG_MTI;
11312074c2d0Shkenken 			hashes[0] = 0xffffffffUL;
11322074c2d0Shkenken 			hashes[1] = 0xffffffffUL;
11332074c2d0Shkenken 			nma = 0;
11340f69512fSskrll 			ec->ec_flags |= ETHER_F_ALLMULTI;
11352074c2d0Shkenken 			break;
11362074c2d0Shkenken 		}
11372074c2d0Shkenken 
11382074c2d0Shkenken 		if (nma < 3) {
11392074c2d0Shkenken 			/* We can program 3 perfect address filters for mcast */
11402074c2d0Shkenken 			memcpy(ias[nma], enm->enm_addrlo, ETHER_ADDR_LEN);
11412074c2d0Shkenken 		} else {
11422074c2d0Shkenken 			/*
11432074c2d0Shkenken 			 * XXX: Datasheet is not very clear here, I'm not sure
11442074c2d0Shkenken 			 * if I'm doing this right.  --joff
11452074c2d0Shkenken 			 */
11462074c2d0Shkenken 			h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
11472074c2d0Shkenken 
11482074c2d0Shkenken 			/* Just want the 6 most-significant bits. */
11492074c2d0Shkenken 			h = h >> 26;
115040b201d5Srjs #if 0
11512074c2d0Shkenken 			hashes[h / 32] |=  (1 << (h % 32));
115240b201d5Srjs #else
115340b201d5Srjs 			hashes[0] = 0xffffffffUL;
115440b201d5Srjs 			hashes[1] = 0xffffffffUL;
115540b201d5Srjs #endif
11562074c2d0Shkenken 			cfg |= ETH_CFG_MTI;
11572074c2d0Shkenken 		}
11582074c2d0Shkenken 		ETHER_NEXT_MULTI(step, enm);
11592074c2d0Shkenken 		nma++;
11602074c2d0Shkenken 	}
116183759283Smsaitoh 	ETHER_UNLOCK(ec);
11622074c2d0Shkenken 
11632074c2d0Shkenken 	// program...
11642074c2d0Shkenken 	DPRINTFN(1,("%s: en0 %02x:%02x:%02x:%02x:%02x:%02x\n", __FUNCTION__,
11652074c2d0Shkenken 		sc->sc_enaddr[0], sc->sc_enaddr[1], sc->sc_enaddr[2],
11662074c2d0Shkenken 		sc->sc_enaddr[3], sc->sc_enaddr[4], sc->sc_enaddr[5]));
11672074c2d0Shkenken 	CEMAC_GEM_WRITE(SA1L, (sc->sc_enaddr[3] << 24)
11682074c2d0Shkenken 	    | (sc->sc_enaddr[2] << 16) | (sc->sc_enaddr[1] << 8)
11692074c2d0Shkenken 	    | (sc->sc_enaddr[0]));
11702074c2d0Shkenken 	CEMAC_GEM_WRITE(SA1H, (sc->sc_enaddr[5] << 8)
11712074c2d0Shkenken 	    | (sc->sc_enaddr[4]));
117240b201d5Srjs 	if (nma > 0) {
1173d0a9f7e6Sskrll 		DPRINTFN(1,("%s: en1 %02x:%02x:%02x:%02x:%02x:%02x\n",
1174d0a9f7e6Sskrll 		    __FUNCTION__,
11752074c2d0Shkenken 		    ias[0][0], ias[0][1], ias[0][2],
11762074c2d0Shkenken 		    ias[0][3], ias[0][4], ias[0][5]));
11772074c2d0Shkenken 		CEMAC_WRITE(ETH_SA2L, (ias[0][3] << 24)
11782074c2d0Shkenken 		    | (ias[0][2] << 16) | (ias[0][1] << 8)
11792074c2d0Shkenken 		    | (ias[0][0]));
11802074c2d0Shkenken 		CEMAC_WRITE(ETH_SA2H, (ias[0][4] << 8)
11812074c2d0Shkenken 		    | (ias[0][5]));
11822074c2d0Shkenken 	}
118340b201d5Srjs 	if (nma > 1) {
1184d0a9f7e6Sskrll 		DPRINTFN(1,("%s: en2 %02x:%02x:%02x:%02x:%02x:%02x\n",
1185d0a9f7e6Sskrll 		    __FUNCTION__,
11862074c2d0Shkenken 		    ias[1][0], ias[1][1], ias[1][2],
11872074c2d0Shkenken 		    ias[1][3], ias[1][4], ias[1][5]));
11882074c2d0Shkenken 		CEMAC_WRITE(ETH_SA3L, (ias[1][3] << 24)
11892074c2d0Shkenken 		    | (ias[1][2] << 16) | (ias[1][1] << 8)
11902074c2d0Shkenken 		    | (ias[1][0]));
11912074c2d0Shkenken 		CEMAC_WRITE(ETH_SA3H, (ias[1][4] << 8)
11922074c2d0Shkenken 		    | (ias[1][5]));
11932074c2d0Shkenken 	}
119440b201d5Srjs 	if (nma > 2) {
1195d0a9f7e6Sskrll 		DPRINTFN(1,("%s: en3 %02x:%02x:%02x:%02x:%02x:%02x\n",
1196d0a9f7e6Sskrll 		    __FUNCTION__,
11972074c2d0Shkenken 		    ias[2][0], ias[2][1], ias[2][2],
11982074c2d0Shkenken 		    ias[2][3], ias[2][4], ias[2][5]));
119940b201d5Srjs 		CEMAC_WRITE(ETH_SA4L, (ias[2][3] << 24)
12002074c2d0Shkenken 		    | (ias[2][2] << 16) | (ias[2][1] << 8)
12012074c2d0Shkenken 		    | (ias[2][0]));
120240b201d5Srjs 		CEMAC_WRITE(ETH_SA4H, (ias[2][4] << 8)
12032074c2d0Shkenken 		    | (ias[2][5]));
12042074c2d0Shkenken 	}
12052074c2d0Shkenken 	CEMAC_GEM_WRITE(HSH, hashes[0]);
12062074c2d0Shkenken 	CEMAC_GEM_WRITE(HSL, hashes[1]);
12072074c2d0Shkenken 	CEMAC_WRITE(ETH_CFG, cfg);
12082074c2d0Shkenken 	CEMAC_WRITE(ETH_CTL, ctl | ETH_CTL_RE);
12092074c2d0Shkenken }
1210