xref: /netbsd-src/sys/arch/arm/ep93xx/epe.c (revision 481d3881954fd794ca5f2d880b68c53a5db8620e)
1*481d3881Srin /*	$NetBSD: epe.c,v 1.51 2024/07/05 04:31:49 rin Exp $	*/
24eeab775Sjoff 
34eeab775Sjoff /*
44eeab775Sjoff  * Copyright (c) 2004 Jesse Off
54eeab775Sjoff  * All rights reserved.
64eeab775Sjoff  *
74eeab775Sjoff  * Redistribution and use in source and binary forms, with or without
84eeab775Sjoff  * modification, are permitted provided that the following conditions
94eeab775Sjoff  * are met:
104eeab775Sjoff  * 1. Redistributions of source code must retain the above copyright
114eeab775Sjoff  *    notice, this list of conditions and the following disclaimer.
124eeab775Sjoff  * 2. Redistributions in binary form must reproduce the above copyright
134eeab775Sjoff  *    notice, this list of conditions and the following disclaimer in the
144eeab775Sjoff  *    documentation and/or other materials provided with the distribution.
154eeab775Sjoff  *
164eeab775Sjoff  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
174eeab775Sjoff  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
184eeab775Sjoff  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
194eeab775Sjoff  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
204eeab775Sjoff  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
214eeab775Sjoff  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
224eeab775Sjoff  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
234eeab775Sjoff  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244eeab775Sjoff  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
254eeab775Sjoff  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
264eeab775Sjoff  * POSSIBILITY OF SUCH DAMAGE.
274eeab775Sjoff  */
284eeab775Sjoff 
294eeab775Sjoff #include <sys/cdefs.h>
30*481d3881Srin __KERNEL_RCSID(0, "$NetBSD: epe.c,v 1.51 2024/07/05 04:31:49 rin Exp $");
314eeab775Sjoff 
324eeab775Sjoff #include <sys/types.h>
334eeab775Sjoff #include <sys/param.h>
344eeab775Sjoff #include <sys/systm.h>
354eeab775Sjoff #include <sys/ioctl.h>
364eeab775Sjoff #include <sys/kernel.h>
374eeab775Sjoff #include <sys/proc.h>
384eeab775Sjoff #include <sys/time.h>
394eeab775Sjoff #include <sys/device.h>
404eeab775Sjoff #include <uvm/uvm_extern.h>
414eeab775Sjoff 
42cf10107dSdyoung #include <sys/bus.h>
434eeab775Sjoff #include <machine/intr.h>
444eeab775Sjoff 
454eeab775Sjoff #include <arm/cpufunc.h>
464eeab775Sjoff 
474eeab775Sjoff #include <arm/ep93xx/epsocvar.h>
484eeab775Sjoff #include <arm/ep93xx/ep93xxvar.h>
494eeab775Sjoff 
504eeab775Sjoff #include <net/if.h>
514eeab775Sjoff #include <net/if_dl.h>
524eeab775Sjoff #include <net/if_types.h>
534eeab775Sjoff #include <net/if_media.h>
544eeab775Sjoff #include <net/if_ether.h>
554b508fb1Smsaitoh #include <net/bpf.h>
564eeab775Sjoff 
574eeab775Sjoff #include <dev/mii/mii.h>
584eeab775Sjoff #include <dev/mii/miivar.h>
594eeab775Sjoff 
604eeab775Sjoff #ifdef INET
614eeab775Sjoff #include <netinet/in.h>
624eeab775Sjoff #include <netinet/in_systm.h>
634eeab775Sjoff #include <netinet/in_var.h>
644eeab775Sjoff #include <netinet/ip.h>
654eeab775Sjoff #include <netinet/if_inarp.h>
664eeab775Sjoff #endif
674eeab775Sjoff 
68944e317aSjoff #include <arm/ep93xx/ep93xxreg.h>
694eeab775Sjoff #include <arm/ep93xx/epereg.h>
704eeab775Sjoff #include <arm/ep93xx/epevar.h>
714eeab775Sjoff 
727959c31aShamajima #define DEFAULT_MDCDIV	32
737959c31aShamajima 
74944e317aSjoff #ifndef EPE_FAST
75944e317aSjoff #define EPE_FAST
76944e317aSjoff #endif
774eeab775Sjoff 
78944e317aSjoff #ifndef EPE_FAST
794eeab775Sjoff #define EPE_READ(x) \
804eeab775Sjoff 	bus_space_read_4(sc->sc_iot, sc->sc_ioh, (EPE_ ## x))
814eeab775Sjoff #define EPE_WRITE(x, y) \
824eeab775Sjoff 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, (EPE_ ## x), (y))
83944e317aSjoff #define CTRLPAGE_DMASYNC(x, y, z) \
84944e317aSjoff 	bus_dmamap_sync(sc->sc_dmat, sc->ctrlpage_dmamap, (x), (y), (z))
85944e317aSjoff #else
8608a4aba7Sskrll #define EPE_READ(x) *(volatile uint32_t *) \
87944e317aSjoff 	(EP93XX_AHB_VBASE + EP93XX_AHB_EPE + (EPE_ ## x))
8808a4aba7Sskrll #define EPE_WRITE(x, y) *(volatile uint32_t *) \
89944e317aSjoff 	(EP93XX_AHB_VBASE + EP93XX_AHB_EPE + (EPE_ ## x)) = y
90944e317aSjoff #define CTRLPAGE_DMASYNC(x, y, z)
91944e317aSjoff #endif /* ! EPE_FAST */
924eeab775Sjoff 
930bc32000Smatt static int	epe_match(device_t , cfdata_t, void *);
940bc32000Smatt static void	epe_attach(device_t, device_t, void *);
954eeab775Sjoff static void	epe_init(struct epe_softc *);
964eeab775Sjoff static int	epe_intr(void* arg);
97944e317aSjoff static int	epe_gctx(struct epe_softc *);
98a5cdd4b4Smsaitoh int		epe_mii_readreg (device_t, int, int, uint16_t *);
99a5cdd4b4Smsaitoh int		epe_mii_writereg (device_t, int, int, uint16_t);
1000bc32000Smatt void		epe_statchg (struct ifnet *);
1014eeab775Sjoff void		epe_tick (void *);
10253524e44Schristos static int	epe_ifioctl (struct ifnet *, u_long, void *);
1034eeab775Sjoff static void	epe_ifstart (struct ifnet *);
1044eeab775Sjoff static void	epe_ifwatchdog (struct ifnet *);
1054eeab775Sjoff static int	epe_ifinit (struct ifnet *);
1064eeab775Sjoff static void	epe_ifstop (struct ifnet *, int);
1074eeab775Sjoff static void	epe_setaddr (struct ifnet *);
1084eeab775Sjoff 
109cbab9cadSchs CFATTACH_DECL_NEW(epe, sizeof(struct epe_softc),
1104eeab775Sjoff     epe_match, epe_attach, NULL, NULL);
1114eeab775Sjoff 
1124eeab775Sjoff static int
epe_match(device_t parent,cfdata_t match,void * aux)1130bc32000Smatt epe_match(device_t parent, cfdata_t match, void *aux)
1144eeab775Sjoff {
1154eeab775Sjoff 	return 2;
1164eeab775Sjoff }
1174eeab775Sjoff 
1184eeab775Sjoff static void
epe_attach(device_t parent,device_t self,void * aux)1190bc32000Smatt epe_attach(device_t parent, device_t self, void *aux)
1204eeab775Sjoff {
1210bc32000Smatt 	struct epe_softc		*sc = device_private(self);
1224eeab775Sjoff 	struct epsoc_attach_args	*sa;
123fb44a857Sthorpej 	prop_data_t			 enaddr;
1244eeab775Sjoff 
1250bc32000Smatt 	aprint_normal("\n");
1264eeab775Sjoff 	sa = aux;
1270bc32000Smatt 	sc->sc_dev = self;
1284eeab775Sjoff 	sc->sc_iot = sa->sa_iot;
1294eeab775Sjoff 	sc->sc_intr = sa->sa_intr;
1304eeab775Sjoff 	sc->sc_dmat = sa->sa_dmat;
1314eeab775Sjoff 
1324eeab775Sjoff 	if (bus_space_map(sa->sa_iot, sa->sa_addr, sa->sa_size,
1334eeab775Sjoff 		0, &sc->sc_ioh))
134cbab9cadSchs 		panic("%s: Cannot map registers", device_xname(self));
1354eeab775Sjoff 
1367959c31aShamajima 	/* Fetch the Ethernet address from property if set. */
137f9e1815aSmartin 	enaddr = prop_dictionary_get(device_properties(self), "mac-address");
138fb44a857Sthorpej 	if (enaddr != NULL) {
139fb44a857Sthorpej 		KASSERT(prop_object_type(enaddr) == PROP_TYPE_DATA);
140fb44a857Sthorpej 		KASSERT(prop_data_size(enaddr) == ETHER_ADDR_LEN);
141fb44a857Sthorpej 		memcpy(sc->sc_enaddr, prop_data_data_nocopy(enaddr),
142fb44a857Sthorpej 		       ETHER_ADDR_LEN);
1437959c31aShamajima 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, EPE_AFP, 0);
1447959c31aShamajima 		bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, EPE_IndAd,
1457959c31aShamajima 					 sc->sc_enaddr, ETHER_ADDR_LEN);
1467959c31aShamajima 	}
1477959c31aShamajima 
1484eeab775Sjoff 	ep93xx_intr_establish(sc->sc_intr, IPL_NET, epe_intr, sc);
1494eeab775Sjoff 	epe_init(sc);
1504eeab775Sjoff }
1514eeab775Sjoff 
1524eeab775Sjoff static int
epe_gctx(struct epe_softc * sc)153944e317aSjoff epe_gctx(struct epe_softc *sc)
154944e317aSjoff {
155944e317aSjoff 	struct ifnet * ifp = &sc->sc_ec.ec_if;
15608a4aba7Sskrll 	uint32_t *cur, ndq = 0;
157944e317aSjoff 
158944e317aSjoff 	/* Handle transmit completions */
15908a4aba7Sskrll 	cur = (uint32_t *)(EPE_READ(TXStsQCurAdd) -
160bc250f04She 		sc->ctrlpage_dsaddr + (char*)sc->ctrlpage);
161944e317aSjoff 
162944e317aSjoff 	if (sc->TXStsQ_cur != cur) {
16308a4aba7Sskrll 		CTRLPAGE_DMASYNC(TX_QLEN * 2 * sizeof(uint32_t),
16408a4aba7Sskrll 			TX_QLEN * sizeof(uint32_t), BUS_DMASYNC_PREREAD);
1657b055612Smsaitoh 	} else
166944e317aSjoff 		return 0;
167944e317aSjoff 
168944e317aSjoff 	do {
16908a4aba7Sskrll 		uint32_t tbi = *sc->TXStsQ_cur & 0x7fff;
170944e317aSjoff 		struct mbuf *m = sc->txq[tbi].m;
171944e317aSjoff 
1727b055612Smsaitoh 		if ((*sc->TXStsQ_cur & TXStsQ_TxWE) == 0)
173180f0415Sskrll 			if_statinc(ifp, if_oerrors);
1747b055612Smsaitoh 
175944e317aSjoff 		bus_dmamap_unload(sc->sc_dmat, sc->txq[tbi].m_dmamap);
176944e317aSjoff 		m_freem(m);
177944e317aSjoff 		do {
178944e317aSjoff 			sc->txq[tbi].m = NULL;
179944e317aSjoff 			ndq++;
180944e317aSjoff 			tbi = (tbi + 1) % TX_QLEN;
181944e317aSjoff 		} while (sc->txq[tbi].m == m);
182944e317aSjoff 
183180f0415Sskrll 		if_statinc(ifp, if_opackets);
184944e317aSjoff 		sc->TXStsQ_cur++;
185944e317aSjoff 		if (sc->TXStsQ_cur >= sc->TXStsQ + TX_QLEN) {
186944e317aSjoff 			sc->TXStsQ_cur = sc->TXStsQ;
187944e317aSjoff 		}
188944e317aSjoff 	} while (sc->TXStsQ_cur != cur);
189944e317aSjoff 
190944e317aSjoff 	sc->TXDQ_avail += ndq;
19140db1b6eSthorpej 	if (sc->tx_busy) {
19240db1b6eSthorpej 		sc->tx_busy = false;
193944e317aSjoff 		/* Disable end-of-tx-chain interrupt */
194944e317aSjoff 		EPE_WRITE(IntEn, IntEn_REOFIE);
195944e317aSjoff 	}
196944e317aSjoff 	return ndq;
197944e317aSjoff }
198944e317aSjoff 
199944e317aSjoff static int
epe_intr(void * arg)2004eeab775Sjoff epe_intr(void *arg)
2014eeab775Sjoff {
2024eeab775Sjoff 	struct epe_softc *sc = (struct epe_softc *)arg;
2034eeab775Sjoff 	struct ifnet * ifp = &sc->sc_ec.ec_if;
20408a4aba7Sskrll 	uint32_t ndq = 0, irq, *cur;
2054eeab775Sjoff 
2064eeab775Sjoff 	irq = EPE_READ(IntStsC);
2074eeab775Sjoff begin:
20808a4aba7Sskrll 	cur = (uint32_t *)(EPE_READ(RXStsQCurAdd) -
209bc250f04She 		sc->ctrlpage_dsaddr + (char*)sc->ctrlpage);
21008a4aba7Sskrll 	CTRLPAGE_DMASYNC(TX_QLEN * 3 * sizeof(uint32_t),
21108a4aba7Sskrll 		RX_QLEN * 4 * sizeof(uint32_t),
2124eeab775Sjoff 		BUS_DMASYNC_PREREAD);
2134eeab775Sjoff 	while (sc->RXStsQ_cur != cur) {
2147b055612Smsaitoh 		if ((sc->RXStsQ_cur[0] & (RXStsQ_RWE | RXStsQ_RFP |RXStsQ_EOB))
2157b055612Smsaitoh 		    == (RXStsQ_RWE | RXStsQ_RFP | RXStsQ_EOB)) {
21608a4aba7Sskrll 			uint32_t bi = (sc->RXStsQ_cur[1] >> 16) & 0x7fff;
21708a4aba7Sskrll 			uint32_t fl = sc->RXStsQ_cur[1] & 0xffff;
2184eeab775Sjoff 			struct mbuf *m;
2194eeab775Sjoff 
2204eeab775Sjoff 			MGETHDR(m, M_DONTWAIT, MT_DATA);
2214eeab775Sjoff 			if (m != NULL) MCLGET(m, M_DONTWAIT);
2224eeab775Sjoff 			if (m != NULL && (m->m_flags & M_EXT)) {
2234eeab775Sjoff 				bus_dmamap_unload(sc->sc_dmat,
2244eeab775Sjoff 					sc->rxq[bi].m_dmamap);
225d938d837Sozaki-r 				m_set_rcvif(sc->rxq[bi].m, ifp);
2264eeab775Sjoff 				sc->rxq[bi].m->m_pkthdr.len =
2274eeab775Sjoff 					sc->rxq[bi].m->m_len = fl;
2289c4cd063Sozaki-r 				if_percpuq_enqueue(ifp->if_percpuq,
2299c4cd063Sozaki-r 				    sc->rxq[bi].m);
2304eeab775Sjoff 				sc->rxq[bi].m = m;
2314eeab775Sjoff 				bus_dmamap_load(sc->sc_dmat,
2324eeab775Sjoff 					sc->rxq[bi].m_dmamap,
2334eeab775Sjoff 					m->m_ext.ext_buf, MCLBYTES,
2344eeab775Sjoff 					NULL, BUS_DMA_NOWAIT);
2354eeab775Sjoff 				sc->RXDQ[bi * 2] =
2364eeab775Sjoff 					sc->rxq[bi].m_dmamap->dm_segs[0].ds_addr;
2374eeab775Sjoff 			} else {
2384eeab775Sjoff 				/* Drop packets until we can get replacement
2394eeab775Sjoff 				 * empty mbufs for the RXDQ.
2404eeab775Sjoff 				 */
2414eeab775Sjoff 				m_freem(m);
2427b055612Smsaitoh 
243180f0415Sskrll 				if_statinc(ifp, if_ierrors);
2444eeab775Sjoff 			}
2457b055612Smsaitoh 		} else
246180f0415Sskrll 			if_statinc(ifp, if_ierrors);
2474eeab775Sjoff 
2484eeab775Sjoff 		ndq++;
2494eeab775Sjoff 
2504eeab775Sjoff 		sc->RXStsQ_cur += 2;
2517b055612Smsaitoh 		if (sc->RXStsQ_cur >= sc->RXStsQ + (RX_QLEN * 2))
2524eeab775Sjoff 			sc->RXStsQ_cur = sc->RXStsQ;
2534eeab775Sjoff 	}
2544eeab775Sjoff 
2554eeab775Sjoff 	if (ndq > 0) {
25608a4aba7Sskrll 		CTRLPAGE_DMASYNC(TX_QLEN * 3 * sizeof(uint32_t),
25708a4aba7Sskrll 			RX_QLEN * 4 * sizeof(uint32_t),
2584eeab775Sjoff 			BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
2594eeab775Sjoff 		EPE_WRITE(RXStsEnq, ndq);
2604eeab775Sjoff 		EPE_WRITE(RXDEnq, ndq);
2614eeab775Sjoff 		ndq = 0;
2624eeab775Sjoff 	}
2634eeab775Sjoff 
264944e317aSjoff 	if (epe_gctx(sc) > 0 && IFQ_IS_EMPTY(&ifp->if_snd) == 0) {
26530b089e7Snonaka 		if_schedule_deferred_start(ifp);
2664eeab775Sjoff 	}
2674eeab775Sjoff 
2684eeab775Sjoff 	irq = EPE_READ(IntStsC);
269944e317aSjoff 	if ((irq & (IntSts_RxSQ | IntSts_ECI)) != 0)
2704eeab775Sjoff 		goto begin;
271944e317aSjoff 
2727b055612Smsaitoh 	return 1;
2734eeab775Sjoff }
2744eeab775Sjoff 
2754eeab775Sjoff 
2764eeab775Sjoff static void
epe_init(struct epe_softc * sc)2774eeab775Sjoff epe_init(struct epe_softc *sc)
2784eeab775Sjoff {
2794eeab775Sjoff 	bus_dma_segment_t segs;
280bc250f04She 	char *addr;
2814eeab775Sjoff 	int rsegs, err, i;
2824eeab775Sjoff 	struct ifnet * ifp = &sc->sc_ec.ec_if;
2835689358dSmsaitoh 	struct mii_data *mii = &sc->sc_mii;
2847959c31aShamajima 	int mdcdiv = DEFAULT_MDCDIV;
2854eeab775Sjoff 
28688ab7da9Sad 	callout_init(&sc->epe_tick_ch, 0);
2874eeab775Sjoff 
2884eeab775Sjoff 	/* Select primary Individual Address in Address Filter Pointer */
2894eeab775Sjoff 	EPE_WRITE(AFP, 0);
2904eeab775Sjoff 	/* Read ethernet MAC, should already be set by bootrom */
2914eeab775Sjoff 	bus_space_read_region_1(sc->sc_iot, sc->sc_ioh, EPE_IndAd,
2924eeab775Sjoff 		sc->sc_enaddr, ETHER_ADDR_LEN);
2930bc32000Smatt 	aprint_normal_dev(sc->sc_dev, "MAC address %s\n",
2944eeab775Sjoff 		ether_sprintf(sc->sc_enaddr));
2954eeab775Sjoff 
2964eeab775Sjoff 	/* Soft Reset the MAC */
2974eeab775Sjoff 	EPE_WRITE(SelfCtl, SelfCtl_RESET);
2987b055612Smsaitoh 	while (EPE_READ(SelfCtl) & SelfCtl_RESET)
2997b055612Smsaitoh 		;
3004eeab775Sjoff 
3014eeab775Sjoff 	/* suggested magic initialization values from datasheet */
3024eeab775Sjoff 	EPE_WRITE(RXBufThrshld, 0x800040);
3034eeab775Sjoff 	EPE_WRITE(TXBufThrshld, 0x200010);
3044eeab775Sjoff 	EPE_WRITE(RXStsThrshld, 0x40002);
3054eeab775Sjoff 	EPE_WRITE(TXStsThrshld, 0x40002);
3064eeab775Sjoff 	EPE_WRITE(RXDThrshld, 0x40002);
3074eeab775Sjoff 	EPE_WRITE(TXDThrshld, 0x40002);
3084eeab775Sjoff 
3094eeab775Sjoff 	/* Allocate a page of memory for descriptor and status queues */
3104eeab775Sjoff 	err = bus_dmamem_alloc(sc->sc_dmat, PAGE_SIZE, 0, PAGE_SIZE,
3114eeab775Sjoff 		&segs, 1, &rsegs, BUS_DMA_WAITOK);
3124eeab775Sjoff 	if (err == 0) {
3134eeab775Sjoff 		err = bus_dmamem_map(sc->sc_dmat, &segs, 1, PAGE_SIZE,
314944e317aSjoff 			&sc->ctrlpage, (BUS_DMA_WAITOK | BUS_DMA_COHERENT));
3154eeab775Sjoff 	}
3164eeab775Sjoff 	if (err == 0) {
3174eeab775Sjoff 		err = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, PAGE_SIZE,
3184eeab775Sjoff 			0, BUS_DMA_WAITOK, &sc->ctrlpage_dmamap);
3194eeab775Sjoff 	}
3204eeab775Sjoff 	if (err == 0) {
3214eeab775Sjoff 		err = bus_dmamap_load(sc->sc_dmat, sc->ctrlpage_dmamap,
3224eeab775Sjoff 			sc->ctrlpage, PAGE_SIZE, NULL, BUS_DMA_WAITOK);
3234eeab775Sjoff 	}
3244eeab775Sjoff 	if (err != 0) {
3250bc32000Smatt 		panic("%s: Cannot get DMA memory", device_xname(sc->sc_dev));
3264eeab775Sjoff 	}
327944e317aSjoff 	sc->ctrlpage_dsaddr = sc->ctrlpage_dmamap->dm_segs[0].ds_addr;
328c363a9cbScegger 	memset(sc->ctrlpage, 0, PAGE_SIZE);
3294eeab775Sjoff 
3304eeab775Sjoff 	/* Set up pointers to start of each queue in kernel addr space.
3314eeab775Sjoff 	 * Each descriptor queue or status queue entry uses 2 words
3324eeab775Sjoff 	 */
33308a4aba7Sskrll 	sc->TXDQ = (uint32_t *)sc->ctrlpage;
3344eeab775Sjoff 	sc->TXDQ_cur = sc->TXDQ;
3354eeab775Sjoff 	sc->TXDQ_avail = TX_QLEN - 1;
3364eeab775Sjoff 	sc->TXStsQ = &sc->TXDQ[TX_QLEN * 2];
3374eeab775Sjoff 	sc->TXStsQ_cur = sc->TXStsQ;
3384eeab775Sjoff 	sc->RXDQ = &sc->TXStsQ[TX_QLEN];
3394eeab775Sjoff 	sc->RXStsQ = &sc->RXDQ[RX_QLEN * 2];
3404eeab775Sjoff 	sc->RXStsQ_cur = sc->RXStsQ;
3414eeab775Sjoff 
3424eeab775Sjoff 	/* Program each queue's start addr, cur addr, and len registers
3434eeab775Sjoff 	 * with the physical addresses.
3444eeab775Sjoff 	 */
345bc250f04She 	addr = (char *)sc->ctrlpage_dmamap->dm_segs[0].ds_addr;
34608a4aba7Sskrll 	EPE_WRITE(TXDQBAdd, (uint32_t)addr);
34708a4aba7Sskrll 	EPE_WRITE(TXDQCurAdd, (uint32_t)addr);
34808a4aba7Sskrll 	EPE_WRITE(TXDQBLen, TX_QLEN * 2 * sizeof(uint32_t));
3494eeab775Sjoff 
35008a4aba7Sskrll 	addr += (sc->TXStsQ - sc->TXDQ) * sizeof(uint32_t);
35108a4aba7Sskrll 	EPE_WRITE(TXStsQBAdd, (uint32_t)addr);
35208a4aba7Sskrll 	EPE_WRITE(TXStsQCurAdd, (uint32_t)addr);
35308a4aba7Sskrll 	EPE_WRITE(TXStsQBLen, TX_QLEN * sizeof(uint32_t));
3544eeab775Sjoff 
35508a4aba7Sskrll 	addr += (sc->RXDQ - sc->TXStsQ) * sizeof(uint32_t);
35608a4aba7Sskrll 	EPE_WRITE(RXDQBAdd, (uint32_t)addr);
35708a4aba7Sskrll 	EPE_WRITE(RXDCurAdd, (uint32_t)addr);
35808a4aba7Sskrll 	EPE_WRITE(RXDQBLen, RX_QLEN * 2 * sizeof(uint32_t));
3594eeab775Sjoff 
36008a4aba7Sskrll 	addr += (sc->RXStsQ - sc->RXDQ) * sizeof(uint32_t);
36108a4aba7Sskrll 	EPE_WRITE(RXStsQBAdd, (uint32_t)addr);
36208a4aba7Sskrll 	EPE_WRITE(RXStsQCurAdd, (uint32_t)addr);
36308a4aba7Sskrll 	EPE_WRITE(RXStsQBLen, RX_QLEN * 2 * sizeof(uint32_t));
3644eeab775Sjoff 
3654eeab775Sjoff 	/* Populate the RXDQ with mbufs */
3664eeab775Sjoff 	for (i = 0; i < RX_QLEN; i++) {
3674eeab775Sjoff 		struct mbuf *m;
3684eeab775Sjoff 
3697b055612Smsaitoh 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, TX_QLEN/4, MCLBYTES,
3707b055612Smsaitoh 		    0, BUS_DMA_WAITOK, &sc->rxq[i].m_dmamap);
3714eeab775Sjoff 		MGETHDR(m, M_WAIT, MT_DATA);
3724eeab775Sjoff 		MCLGET(m, M_WAIT);
3734eeab775Sjoff 		sc->rxq[i].m = m;
3744eeab775Sjoff 		bus_dmamap_load(sc->sc_dmat, sc->rxq[i].m_dmamap,
3757b055612Smsaitoh 			m->m_ext.ext_buf, MCLBYTES, NULL, BUS_DMA_WAITOK);
3764eeab775Sjoff 
3774eeab775Sjoff 		sc->RXDQ[i * 2] = sc->rxq[i].m_dmamap->dm_segs[0].ds_addr;
3784eeab775Sjoff 		sc->RXDQ[i * 2 + 1] = (i << 16) | MCLBYTES;
3794eeab775Sjoff 		bus_dmamap_sync(sc->sc_dmat, sc->rxq[i].m_dmamap, 0,
3804eeab775Sjoff 			MCLBYTES, BUS_DMASYNC_PREREAD);
3814eeab775Sjoff 	}
3824eeab775Sjoff 
3834eeab775Sjoff 	for (i = 0; i < TX_QLEN; i++) {
3844eeab775Sjoff 		bus_dmamap_create(sc->sc_dmat, MCLBYTES, 1, MCLBYTES, 0,
3854eeab775Sjoff 			(BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW),
3864eeab775Sjoff 			&sc->txq[i].m_dmamap);
3874eeab775Sjoff 		sc->txq[i].m = NULL;
3884eeab775Sjoff 		sc->TXDQ[i * 2 + 1] = (i << 16);
3894eeab775Sjoff 	}
3904eeab775Sjoff 
3914eeab775Sjoff 	/* Divide HCLK by 32 for MDC clock */
3920bc32000Smatt 	if (device_cfdata(sc->sc_dev)->cf_flags)
3930bc32000Smatt 		mdcdiv = device_cfdata(sc->sc_dev)->cf_flags;
3947959c31aShamajima 	EPE_WRITE(SelfCtl, (SelfCtl_MDCDIV(mdcdiv) | SelfCtl_PSPRS));
3954eeab775Sjoff 
3965689358dSmsaitoh 	mii->mii_ifp = ifp;
3975689358dSmsaitoh 	mii->mii_readreg = epe_mii_readreg;
3985689358dSmsaitoh 	mii->mii_writereg = epe_mii_writereg;
3995689358dSmsaitoh 	mii->mii_statchg = epe_statchg;
4005689358dSmsaitoh 	sc->sc_ec.ec_mii = mii;
401912cc31fSthorpej 	ifmedia_init(&mii->mii_media, IFM_IMASK, ether_mediachange,
402b480b622Sdyoung 		ether_mediastatus);
4035689358dSmsaitoh 	mii_attach(sc->sc_dev, mii, 0xffffffff, MII_PHY_ANY,
4044eeab775Sjoff 	    MII_OFFSET_ANY, 0);
4055689358dSmsaitoh 	ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO);
4064eeab775Sjoff 
4074eeab775Sjoff 	EPE_WRITE(BMCtl, BMCtl_RxEn | BMCtl_TxEn);
408944e317aSjoff 	EPE_WRITE(IntEn, IntEn_REOFIE);
4094eeab775Sjoff 	/* maximum valid max frame length */
4104eeab775Sjoff 	EPE_WRITE(MaxFrmLen, (0x7ff << 16) | MHLEN);
4114eeab775Sjoff 	/* wait for receiver ready */
4122300bb5cSjoerg 	while ((EPE_READ(BMSts) & BMSts_RxAct) == 0)
4132300bb5cSjoerg 		continue;
4144eeab775Sjoff 	/* enqueue the entries in RXStsQ and RXDQ */
415944e317aSjoff 	CTRLPAGE_DMASYNC(0, sc->ctrlpage_dmamap->dm_mapsize,
4164eeab775Sjoff 		BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
4174eeab775Sjoff 	EPE_WRITE(RXDEnq, RX_QLEN - 1);
4184eeab775Sjoff 	EPE_WRITE(RXStsEnq, RX_QLEN - 1);
4194eeab775Sjoff 
4204eeab775Sjoff 	/*
4214eeab775Sjoff 	 * We can support 802.1Q VLAN-sized frames.
4224eeab775Sjoff 	 */
4234eeab775Sjoff 	sc->sc_ec.ec_capabilities |= ETHERCAP_VLAN_MTU;
4244eeab775Sjoff 
4250bc32000Smatt 	strcpy(ifp->if_xname, device_xname(sc->sc_dev));
426091e1526Smsaitoh 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
4274eeab775Sjoff 	ifp->if_ioctl = epe_ifioctl;
4284eeab775Sjoff 	ifp->if_start = epe_ifstart;
4294eeab775Sjoff 	ifp->if_watchdog = epe_ifwatchdog;
4304eeab775Sjoff 	ifp->if_init = epe_ifinit;
4314eeab775Sjoff 	ifp->if_stop = epe_ifstop;
4324eeab775Sjoff 	ifp->if_timer = 0;
4334eeab775Sjoff 	ifp->if_softc = sc;
4344eeab775Sjoff 	IFQ_SET_READY(&ifp->if_snd);
4354eeab775Sjoff 	if_attach(ifp);
43630b089e7Snonaka 	if_deferred_start_init(ifp, NULL);
4374eeab775Sjoff 	ether_ifattach(ifp, (sc)->sc_enaddr);
4384eeab775Sjoff }
4394eeab775Sjoff 
4404eeab775Sjoff int
epe_mii_readreg(device_t self,int phy,int reg,uint16_t * val)441a5cdd4b4Smsaitoh epe_mii_readreg(device_t self, int phy, int reg, uint16_t *val)
4424eeab775Sjoff {
443a5cdd4b4Smsaitoh 	uint32_t d;
4444eeab775Sjoff 
4454eeab775Sjoff 	d = EPE_READ(SelfCtl);
4464eeab775Sjoff 	EPE_WRITE(SelfCtl, d & ~SelfCtl_PSPRS); /* no preamble suppress */
4474eeab775Sjoff 	EPE_WRITE(MIICmd, (MIICmd_READ | (phy << 5) | reg));
4487b055612Smsaitoh 	while (EPE_READ(MIISts) & MIISts_BUSY)
4497b055612Smsaitoh 		;
450a5cdd4b4Smsaitoh 	*val = EPE_READ(MIIData) & 0xffff;
4514eeab775Sjoff 	EPE_WRITE(SelfCtl, d); /* restore old value */
452a5cdd4b4Smsaitoh 	return 0;
4534eeab775Sjoff }
4544eeab775Sjoff 
455a5cdd4b4Smsaitoh int
epe_mii_writereg(device_t self,int phy,int reg,uint16_t val)456a5cdd4b4Smsaitoh epe_mii_writereg(device_t self, int phy, int reg, uint16_t val)
4574eeab775Sjoff {
45808a4aba7Sskrll 	uint32_t d;
4594eeab775Sjoff 
4604eeab775Sjoff 	d = EPE_READ(SelfCtl);
4614eeab775Sjoff 	EPE_WRITE(SelfCtl, d & ~SelfCtl_PSPRS); /* no preamble suppress */
4624eeab775Sjoff 	EPE_WRITE(MIIData, val);
4630f969113Shamajima 	EPE_WRITE(MIICmd, (MIICmd_WRITE | (phy << 5) | reg));
4647b055612Smsaitoh 	while (EPE_READ(MIISts) & MIISts_BUSY)
4657b055612Smsaitoh 		;
4664eeab775Sjoff 	EPE_WRITE(SelfCtl, d); /* restore old value */
467a5cdd4b4Smsaitoh 
468a5cdd4b4Smsaitoh 	return 0;
4694eeab775Sjoff }
4704eeab775Sjoff 
4714eeab775Sjoff void
epe_statchg(struct ifnet * ifp)4720bc32000Smatt epe_statchg(struct ifnet *ifp)
4734eeab775Sjoff {
4740bc32000Smatt 	struct epe_softc *sc = ifp->if_softc;
47508a4aba7Sskrll 	uint32_t reg;
4764eeab775Sjoff 
4774eeab775Sjoff 	/*
4784eeab775Sjoff 	 * We must keep the MAC and the PHY in sync as
4794eeab775Sjoff 	 * to the status of full-duplex!
4804eeab775Sjoff 	 */
4814eeab775Sjoff 	reg = EPE_READ(TestCtl);
4824eeab775Sjoff 	if (sc->sc_mii.mii_media_active & IFM_FDX)
4834eeab775Sjoff 		reg |= TestCtl_MFDX;
4844eeab775Sjoff 	else
4854eeab775Sjoff 		reg &= ~TestCtl_MFDX;
4864eeab775Sjoff 	EPE_WRITE(TestCtl, reg);
4874eeab775Sjoff }
4884eeab775Sjoff 
4894eeab775Sjoff void
epe_tick(void * arg)490454af1c0Sdsl epe_tick(void *arg)
4914eeab775Sjoff {
4924eeab775Sjoff 	struct epe_softc* sc = (struct epe_softc *)arg;
4934eeab775Sjoff 	struct ifnet * ifp = &sc->sc_ec.ec_if;
494944e317aSjoff 	int s;
49508a4aba7Sskrll 	uint32_t misses;
4964eeab775Sjoff 
49745fddbdaSskrll 	if_statadd(ifp, if_collisions, EPE_READ(TXCollCnt));
4984eeab775Sjoff 	/* These misses are ok, they will happen if the RAM/CPU can't keep up */
4994eeab775Sjoff 	misses = EPE_READ(RXMissCnt);
5004eeab775Sjoff 	if (misses > 0)
5010bc32000Smatt 		printf("%s: %d rx misses\n", device_xname(sc->sc_dev), misses);
5024eeab775Sjoff 
503944e317aSjoff 	s = splnet();
504944e317aSjoff 	if (epe_gctx(sc) > 0 && IFQ_IS_EMPTY(&ifp->if_snd) == 0) {
505944e317aSjoff 		epe_ifstart(ifp);
506944e317aSjoff 	}
507944e317aSjoff 	splx(s);
508944e317aSjoff 
5094eeab775Sjoff 	mii_tick(&sc->sc_mii);
5104eeab775Sjoff 	callout_reset(&sc->epe_tick_ch, hz, epe_tick, sc);
5114eeab775Sjoff }
5124eeab775Sjoff 
5134eeab775Sjoff 
5144eeab775Sjoff static int
epe_ifioctl(struct ifnet * ifp,u_long cmd,void * data)515454af1c0Sdsl epe_ifioctl(struct ifnet *ifp, u_long cmd, void *data)
5164eeab775Sjoff {
5174eeab775Sjoff 	int s, error;
5184eeab775Sjoff 
5194eeab775Sjoff 	s = splnet();
5204eeab775Sjoff 	error = ether_ioctl(ifp, cmd, data);
5214eeab775Sjoff 	if (error == ENETRESET) {
5224eeab775Sjoff 		if (ifp->if_flags & IFF_RUNNING)
5234eeab775Sjoff 			epe_setaddr(ifp);
5244eeab775Sjoff 		error = 0;
5254eeab775Sjoff 	}
5264eeab775Sjoff 	splx(s);
5274eeab775Sjoff 	return error;
5284eeab775Sjoff }
5294eeab775Sjoff 
5304eeab775Sjoff static void
epe_ifstart(struct ifnet * ifp)531454af1c0Sdsl epe_ifstart(struct ifnet *ifp)
5324eeab775Sjoff {
5334eeab775Sjoff 	struct epe_softc *sc = (struct epe_softc *)ifp->if_softc;
5344eeab775Sjoff 	struct mbuf *m;
5354eeab775Sjoff 	bus_dma_segment_t *segs;
536944e317aSjoff 	int s, bi, err, nsegs, ndq;
5374eeab775Sjoff 
5384eeab775Sjoff 	s = splnet();
539944e317aSjoff start:
540944e317aSjoff 	ndq = 0;
5414eeab775Sjoff 	if (sc->TXDQ_avail == 0) {
542944e317aSjoff 		if (epe_gctx(sc) == 0) {
543944e317aSjoff 			/* Enable End-Of-TX-Chain interrupt */
544944e317aSjoff 			EPE_WRITE(IntEn, IntEn_REOFIE | IntEn_ECIE);
54540db1b6eSthorpej 			sc->tx_busy = true;
546944e317aSjoff 			ifp->if_timer = 10;
5474eeab775Sjoff 			splx(s);
5484eeab775Sjoff 			return;
5494eeab775Sjoff 		}
550944e317aSjoff 	}
551944e317aSjoff 
5524eeab775Sjoff 	bi = sc->TXDQ_cur - sc->TXDQ;
5534eeab775Sjoff 
5544eeab775Sjoff 	IFQ_POLL(&ifp->if_snd, m);
5554eeab775Sjoff 	if (m == NULL) {
5564eeab775Sjoff 		splx(s);
5574eeab775Sjoff 		return;
5584eeab775Sjoff 	}
559944e317aSjoff more:
5604eeab775Sjoff 	if ((err = bus_dmamap_load_mbuf(sc->sc_dmat, sc->txq[bi].m_dmamap, m,
5614eeab775Sjoff 		BUS_DMA_NOWAIT)) ||
5624eeab775Sjoff 		sc->txq[bi].m_dmamap->dm_segs[0].ds_addr & 0x3 ||
5634eeab775Sjoff 		sc->txq[bi].m_dmamap->dm_nsegs > (sc->TXDQ_avail - ndq)) {
5644eeab775Sjoff 		/* Copy entire mbuf chain to new and 32-bit aligned storage */
5654eeab775Sjoff 		struct mbuf *mn;
5664eeab775Sjoff 
5674eeab775Sjoff 		if (err == 0)
5684eeab775Sjoff 			bus_dmamap_unload(sc->sc_dmat, sc->txq[bi].m_dmamap);
5694eeab775Sjoff 
5704eeab775Sjoff 		MGETHDR(mn, M_DONTWAIT, MT_DATA);
5714eeab775Sjoff 		if (mn == NULL) goto stop;
5724eeab775Sjoff 		if (m->m_pkthdr.len > (MHLEN & (~0x3))) {
5734eeab775Sjoff 			MCLGET(mn, M_DONTWAIT);
5744eeab775Sjoff 			if ((mn->m_flags & M_EXT) == 0) {
5754eeab775Sjoff 				m_freem(mn);
5764eeab775Sjoff 				goto stop;
5774eeab775Sjoff 			}
5784eeab775Sjoff 		}
57908a4aba7Sskrll 		mn->m_data = (void *)(((uint32_t)mn->m_data + 0x3) & (~0x3));
58053524e44Schristos 		m_copydata(m, 0, m->m_pkthdr.len, mtod(mn, void *));
5814eeab775Sjoff 		mn->m_pkthdr.len = mn->m_len = m->m_pkthdr.len;
5824eeab775Sjoff 		IFQ_DEQUEUE(&ifp->if_snd, m);
5834eeab775Sjoff 		m_freem(m);
5844eeab775Sjoff 		m = mn;
5854eeab775Sjoff 		bus_dmamap_load_mbuf(sc->sc_dmat, sc->txq[bi].m_dmamap, m,
5864eeab775Sjoff 			BUS_DMA_NOWAIT);
5874eeab775Sjoff 	} else {
5884eeab775Sjoff 		IFQ_DEQUEUE(&ifp->if_snd, m);
5894eeab775Sjoff 	}
5904eeab775Sjoff 
5913cd62456Smsaitoh 	bpf_mtap(ifp, m, BPF_D_OUT);
5924eeab775Sjoff 
5934eeab775Sjoff 	nsegs = sc->txq[bi].m_dmamap->dm_nsegs;
5944eeab775Sjoff 	segs = sc->txq[bi].m_dmamap->dm_segs;
5954eeab775Sjoff 	bus_dmamap_sync(sc->sc_dmat, sc->txq[bi].m_dmamap, 0,
5964eeab775Sjoff 		sc->txq[bi].m_dmamap->dm_mapsize,
5974eeab775Sjoff 		BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
5984eeab775Sjoff 
5994eeab775Sjoff 	/* XXX: This driver hasn't been tested w/nsegs > 1 */
6004eeab775Sjoff 	while (nsegs > 0) {
6014eeab775Sjoff 		nsegs--;
6024eeab775Sjoff 		sc->txq[bi].m = m;
6034eeab775Sjoff 		sc->TXDQ[bi * 2] = segs->ds_addr;
6044eeab775Sjoff 		if (nsegs == 0)
6054eeab775Sjoff 			sc->TXDQ[bi * 2 + 1] = segs->ds_len | (bi << 16) |
6064eeab775Sjoff 				(1 << 31);
6074eeab775Sjoff 		else
6084eeab775Sjoff 			sc->TXDQ[bi * 2 + 1] = segs->ds_len | (bi << 16);
6094eeab775Sjoff 		segs++;
6104eeab775Sjoff 		bi = (bi + 1) % TX_QLEN;
6114eeab775Sjoff 		ndq++;
6124eeab775Sjoff 	}
6134eeab775Sjoff 
6144eeab775Sjoff 
615944e317aSjoff 	/*
616944e317aSjoff 	 * Enqueue another.  Don't do more than half the available
617944e317aSjoff 	 * descriptors before telling the MAC about them
618944e317aSjoff 	 */
619944e317aSjoff 	if ((sc->TXDQ_avail - ndq) > 0 && ndq < TX_QLEN / 2) {
6204eeab775Sjoff 		IFQ_POLL(&ifp->if_snd, m);
6217b055612Smsaitoh 		if (m != NULL)
622944e317aSjoff 			goto more;
6234eeab775Sjoff 	}
6244eeab775Sjoff stop:
6254eeab775Sjoff 	if (ndq > 0) {
6264eeab775Sjoff 		sc->TXDQ_avail -= ndq;
6274eeab775Sjoff 		sc->TXDQ_cur = &sc->TXDQ[bi];
62808a4aba7Sskrll 		CTRLPAGE_DMASYNC(0, TX_QLEN * 2 * sizeof(uint32_t),
6294eeab775Sjoff 			BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
6304eeab775Sjoff 		EPE_WRITE(TXDEnq, ndq);
6314eeab775Sjoff 	}
6324eeab775Sjoff 
633944e317aSjoff 	if (IFQ_IS_EMPTY(&ifp->if_snd) == 0)
634944e317aSjoff 		goto start;
635944e317aSjoff 
636944e317aSjoff 	splx(s);
6374eeab775Sjoff 	return;
6384eeab775Sjoff }
6394eeab775Sjoff 
6404eeab775Sjoff static void
epe_ifwatchdog(struct ifnet * ifp)641454af1c0Sdsl epe_ifwatchdog(struct ifnet *ifp)
6424eeab775Sjoff {
6434eeab775Sjoff 	struct epe_softc *sc = (struct epe_softc *)ifp->if_softc;
6444eeab775Sjoff 
6454eeab775Sjoff 	if ((ifp->if_flags & IFF_RUNNING) == 0)
6464eeab775Sjoff 		return;
6474eeab775Sjoff 	printf("%s: device timeout, BMCtl = 0x%08x, BMSts = 0x%08x\n",
6480bc32000Smatt 		device_xname(sc->sc_dev), EPE_READ(BMCtl), EPE_READ(BMSts));
6494eeab775Sjoff }
6504eeab775Sjoff 
6514eeab775Sjoff static int
epe_ifinit(struct ifnet * ifp)652454af1c0Sdsl epe_ifinit(struct ifnet *ifp)
6534eeab775Sjoff {
6544eeab775Sjoff 	struct epe_softc *sc = ifp->if_softc;
655b480b622Sdyoung 	int rc, s = splnet();
6564eeab775Sjoff 
6574eeab775Sjoff 	callout_stop(&sc->epe_tick_ch);
6584eeab775Sjoff 	EPE_WRITE(RXCtl, RXCtl_IA0 | RXCtl_BA | RXCtl_RCRCA | RXCtl_SRxON);
6594eeab775Sjoff 	EPE_WRITE(TXCtl, TXCtl_STxON);
6604eeab775Sjoff 	EPE_WRITE(GIIntMsk, GIIntMsk_INT); /* start interrupting */
661b480b622Sdyoung 
662b480b622Sdyoung 	if ((rc = mii_mediachg(&sc->sc_mii)) == ENXIO)
663b480b622Sdyoung 		rc = 0;
664b480b622Sdyoung 	else if (rc != 0)
665b480b622Sdyoung 		goto out;
666b480b622Sdyoung 
6674eeab775Sjoff 	callout_reset(&sc->epe_tick_ch, hz, epe_tick, sc);
6684eeab775Sjoff 	ifp->if_flags |= IFF_RUNNING;
669b480b622Sdyoung out:
6704eeab775Sjoff 	splx(s);
6714eeab775Sjoff 	return 0;
6724eeab775Sjoff }
6734eeab775Sjoff 
6744eeab775Sjoff static void
epe_ifstop(struct ifnet * ifp,int disable)675454af1c0Sdsl epe_ifstop(struct ifnet *ifp, int disable)
6764eeab775Sjoff {
6774eeab775Sjoff 	struct epe_softc *sc = ifp->if_softc;
6784eeab775Sjoff 
6794eeab775Sjoff 
6804eeab775Sjoff 	EPE_WRITE(RXCtl, 0);
6814eeab775Sjoff 	EPE_WRITE(TXCtl, 0);
6824eeab775Sjoff 	EPE_WRITE(GIIntMsk, 0);
6834eeab775Sjoff 	callout_stop(&sc->epe_tick_ch);
6844eeab775Sjoff 
6854eeab775Sjoff 	/* Down the MII. */
6864eeab775Sjoff 	mii_down(&sc->sc_mii);
6874eeab775Sjoff 
68840db1b6eSthorpej 	ifp->if_flags &= ~IFF_RUNNING;
6894eeab775Sjoff 	ifp->if_timer = 0;
6904eeab775Sjoff 	sc->sc_mii.mii_media_status &= ~IFM_ACTIVE;
6914eeab775Sjoff }
6924eeab775Sjoff 
6934eeab775Sjoff static void
epe_setaddr(struct ifnet * ifp)694454af1c0Sdsl epe_setaddr(struct ifnet *ifp)
6954eeab775Sjoff {
6964eeab775Sjoff 	struct epe_softc *sc = ifp->if_softc;
6977b055612Smsaitoh 	struct ethercom *ec = &sc->sc_ec;
6984eeab775Sjoff 	struct ether_multi *enm;
6994eeab775Sjoff 	struct ether_multistep step;
70008a4aba7Sskrll 	uint8_t ias[2][ETHER_ADDR_LEN];
70108a4aba7Sskrll 	uint32_t h, nma = 0, hashes[2] = { 0, 0 };
70208a4aba7Sskrll 	uint32_t rxctl = EPE_READ(RXCtl);
7034eeab775Sjoff 
7044eeab775Sjoff 	/* disable receiver temporarily */
7054eeab775Sjoff 	EPE_WRITE(RXCtl, rxctl & ~RXCtl_SRxON);
7064eeab775Sjoff 
7074eeab775Sjoff 	rxctl &= ~(RXCtl_MA | RXCtl_PA | RXCtl_IA2 | RXCtl_IA3);
7084eeab775Sjoff 
7097b055612Smsaitoh 	if (ifp->if_flags & IFF_PROMISC)
7104eeab775Sjoff 		rxctl |= RXCtl_PA;
7114eeab775Sjoff 
7124eeab775Sjoff 	ifp->if_flags &= ~IFF_ALLMULTI;
7134eeab775Sjoff 
71483759283Smsaitoh 	ETHER_LOCK(ec);
7157b055612Smsaitoh 	ETHER_FIRST_MULTI(step, ec, enm);
7164eeab775Sjoff 	while (enm != NULL) {
7174eeab775Sjoff 		if (memcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) {
7184eeab775Sjoff 			/*
7194eeab775Sjoff 			 * We must listen to a range of multicast addresses.
7204eeab775Sjoff 			 * For now, just accept all multicasts, rather than
7214eeab775Sjoff 			 * trying to set only those filter bits needed to match
7224eeab775Sjoff 			 * the range.  (At this time, the only use of address
7234eeab775Sjoff 			 * ranges is for IP multicast routing, for which the
7244eeab775Sjoff 			 * range is big enough to require all bits set.)
7254eeab775Sjoff 			 */
7264eeab775Sjoff 			rxctl &= ~(RXCtl_IA2 | RXCtl_IA3);
7274eeab775Sjoff 			rxctl |= RXCtl_MA;
7284eeab775Sjoff 			hashes[0] = 0xffffffffUL;
7294eeab775Sjoff 			hashes[1] = 0xffffffffUL;
7304eeab775Sjoff 			ifp->if_flags |= IFF_ALLMULTI;
7314eeab775Sjoff 			break;
7324eeab775Sjoff 		}
7334eeab775Sjoff 
7344eeab775Sjoff 		if (nma < 2) {
7354eeab775Sjoff 			/* We can program 2 perfect address filters for mcast */
7364eeab775Sjoff 			memcpy(ias[nma], enm->enm_addrlo, ETHER_ADDR_LEN);
7374eeab775Sjoff 			rxctl |= (1 << (nma + 2));
7384eeab775Sjoff 		} else {
7394eeab775Sjoff 			/*
7404eeab775Sjoff 			 * XXX: Datasheet is not very clear here, I'm not sure
7414eeab775Sjoff 			 * if I'm doing this right.  --joff
7424eeab775Sjoff 			 */
7434eeab775Sjoff 			h = ether_crc32_le(enm->enm_addrlo, ETHER_ADDR_LEN);
7444eeab775Sjoff 
7454eeab775Sjoff 			/* Just want the 6 most-significant bits. */
7464eeab775Sjoff 			h = h >> 26;
7474eeab775Sjoff 
7484eeab775Sjoff 			hashes[ h / 32 ] |=  (1 << (h % 32));
7494eeab775Sjoff 			rxctl |= RXCtl_MA;
7504eeab775Sjoff 		}
7514eeab775Sjoff 		ETHER_NEXT_MULTI(step, enm);
7524eeab775Sjoff 		nma++;
7534eeab775Sjoff 	}
75483759283Smsaitoh 	ETHER_UNLOCK(ec);
7554eeab775Sjoff 
7564eeab775Sjoff 	EPE_WRITE(AFP, 0);
7574eeab775Sjoff 	bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, EPE_IndAd,
7584eeab775Sjoff 		sc->sc_enaddr, ETHER_ADDR_LEN);
7594eeab775Sjoff 	if (rxctl & RXCtl_IA2) {
7604eeab775Sjoff 		EPE_WRITE(AFP, 2);
7614eeab775Sjoff 		bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, EPE_IndAd,
7624eeab775Sjoff 			ias[0], ETHER_ADDR_LEN);
7634eeab775Sjoff 	}
7644eeab775Sjoff 	if (rxctl & RXCtl_IA3) {
7654eeab775Sjoff 		EPE_WRITE(AFP, 3);
7664eeab775Sjoff 		bus_space_write_region_1(sc->sc_iot, sc->sc_ioh, EPE_IndAd,
7674eeab775Sjoff 			ias[1], ETHER_ADDR_LEN);
7684eeab775Sjoff 	}
7694eeab775Sjoff 	if (hashes[0] != 0 && hashes[1] != 0) {
7704eeab775Sjoff 		EPE_WRITE(AFP, 7);
7714eeab775Sjoff 		EPE_WRITE(HashTbl, hashes[0]);
7724eeab775Sjoff 		EPE_WRITE(HashTbl + 4, hashes[1]);
7734eeab775Sjoff 	}
7744eeab775Sjoff 	EPE_WRITE(RXCtl, rxctl);
7754eeab775Sjoff }
776