if_lpe.c revision 243882
1239278Sgonzo/*-
2239278Sgonzo * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
3239278Sgonzo * All rights reserved.
4239278Sgonzo *
5239278Sgonzo * Redistribution and use in source and binary forms, with or without
6239278Sgonzo * modification, are permitted provided that the following conditions
7239278Sgonzo * are met:
8239278Sgonzo * 1. Redistributions of source code must retain the above copyright
9239278Sgonzo *    notice, this list of conditions and the following disclaimer.
10239278Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239278Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239278Sgonzo *    documentation and/or other materials provided with the distribution.
13239278Sgonzo *
14239278Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239278Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239278Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239278Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239278Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239278Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239278Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239278Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239278Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239278Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239278Sgonzo * SUCH DAMAGE.
25239278Sgonzo *
26239278Sgonzo */
27239278Sgonzo#include <sys/cdefs.h>
28239278Sgonzo__FBSDID("$FreeBSD: head/sys/arm/lpc/if_lpe.c 243882 2012-12-05 08:04:20Z glebius $");
29239278Sgonzo
30239278Sgonzo#include <sys/param.h>
31239278Sgonzo#include <sys/endian.h>
32239278Sgonzo#include <sys/systm.h>
33239278Sgonzo#include <sys/sockio.h>
34239278Sgonzo#include <sys/mbuf.h>
35239278Sgonzo#include <sys/malloc.h>
36239278Sgonzo#include <sys/kernel.h>
37239278Sgonzo#include <sys/module.h>
38239278Sgonzo#include <sys/lock.h>
39239278Sgonzo#include <sys/mutex.h>
40239278Sgonzo#include <sys/rman.h>
41239278Sgonzo#include <sys/bus.h>
42239278Sgonzo#include <sys/socket.h>
43239278Sgonzo#include <machine/bus.h>
44239278Sgonzo#include <machine/intr.h>
45239278Sgonzo
46239278Sgonzo#include <net/if.h>
47239278Sgonzo#include <net/if_arp.h>
48239278Sgonzo#include <net/ethernet.h>
49239278Sgonzo#include <net/if_dl.h>
50239278Sgonzo#include <net/if_media.h>
51239278Sgonzo#include <net/if_types.h>
52239278Sgonzo
53239278Sgonzo#include <net/bpf.h>
54239278Sgonzo
55239278Sgonzo#include <dev/ofw/ofw_bus.h>
56239278Sgonzo#include <dev/ofw/ofw_bus_subr.h>
57239278Sgonzo
58239278Sgonzo#include <dev/mii/mii.h>
59239278Sgonzo#include <dev/mii/miivar.h>
60239278Sgonzo
61239278Sgonzo#include <arm/lpc/lpcreg.h>
62239278Sgonzo#include <arm/lpc/lpcvar.h>
63239278Sgonzo#include <arm/lpc/if_lpereg.h>
64239278Sgonzo
65239278Sgonzo#include "miibus_if.h"
66239278Sgonzo
67239278Sgonzo#define	DEBUG
68239278Sgonzo#undef	DEBUG
69239278Sgonzo
70239278Sgonzo#ifdef DEBUG
71239278Sgonzo#define debugf(fmt, args...) do { printf("%s(): ", __func__);   \
72239278Sgonzo    printf(fmt,##args); } while (0)
73239278Sgonzo#else
74239278Sgonzo#define debugf(fmt, args...)
75239278Sgonzo#endif
76239278Sgonzo
77239278Sgonzostruct lpe_dmamap_arg {
78239278Sgonzo	bus_addr_t		lpe_dma_busaddr;
79239278Sgonzo};
80239278Sgonzo
81239278Sgonzostruct lpe_rxdesc {
82239278Sgonzo	struct mbuf *		lpe_rxdesc_mbuf;
83239278Sgonzo	bus_dmamap_t		lpe_rxdesc_dmamap;
84239278Sgonzo};
85239278Sgonzo
86239278Sgonzostruct lpe_txdesc {
87239278Sgonzo	int			lpe_txdesc_first;
88239278Sgonzo	struct mbuf *		lpe_txdesc_mbuf;
89239278Sgonzo	bus_dmamap_t		lpe_txdesc_dmamap;
90239278Sgonzo};
91239278Sgonzo
92239278Sgonzostruct lpe_chain_data {
93239278Sgonzo	bus_dma_tag_t		lpe_parent_tag;
94239278Sgonzo	bus_dma_tag_t		lpe_tx_ring_tag;
95239278Sgonzo	bus_dmamap_t		lpe_tx_ring_map;
96239278Sgonzo	bus_dma_tag_t		lpe_tx_status_tag;
97239278Sgonzo	bus_dmamap_t		lpe_tx_status_map;
98239278Sgonzo	bus_dma_tag_t		lpe_tx_buf_tag;
99239278Sgonzo	bus_dma_tag_t		lpe_rx_ring_tag;
100239278Sgonzo	bus_dmamap_t		lpe_rx_ring_map;
101239278Sgonzo	bus_dma_tag_t		lpe_rx_status_tag;
102239278Sgonzo	bus_dmamap_t		lpe_rx_status_map;
103239278Sgonzo	bus_dma_tag_t		lpe_rx_buf_tag;
104239278Sgonzo	struct lpe_rxdesc	lpe_rx_desc[LPE_RXDESC_NUM];
105239278Sgonzo	struct lpe_txdesc	lpe_tx_desc[LPE_TXDESC_NUM];
106239278Sgonzo	int			lpe_tx_prod;
107239278Sgonzo	int			lpe_tx_last;
108239278Sgonzo	int			lpe_tx_used;
109239278Sgonzo};
110239278Sgonzo
111239278Sgonzostruct lpe_ring_data {
112239278Sgonzo	struct lpe_hwdesc *	lpe_rx_ring;
113239278Sgonzo	struct lpe_hwstatus *	lpe_rx_status;
114239278Sgonzo	bus_addr_t		lpe_rx_ring_phys;
115239278Sgonzo	bus_addr_t		lpe_rx_status_phys;
116239278Sgonzo	struct lpe_hwdesc *	lpe_tx_ring;
117239278Sgonzo	struct lpe_hwstatus *	lpe_tx_status;
118239278Sgonzo	bus_addr_t		lpe_tx_ring_phys;
119239278Sgonzo	bus_addr_t		lpe_tx_status_phys;
120239278Sgonzo};
121239278Sgonzo
122239278Sgonzostruct lpe_softc {
123239278Sgonzo	struct ifnet *		lpe_ifp;
124239278Sgonzo	struct mtx		lpe_mtx;
125239278Sgonzo	phandle_t		lpe_ofw;
126239278Sgonzo	device_t		lpe_dev;
127239278Sgonzo	device_t		lpe_miibus;
128239278Sgonzo	uint8_t			lpe_enaddr[6];
129239278Sgonzo	struct resource	*	lpe_mem_res;
130239278Sgonzo	struct resource *	lpe_irq_res;
131239278Sgonzo	void *			lpe_intrhand;
132239278Sgonzo	bus_space_tag_t		lpe_bst;
133239278Sgonzo	bus_space_handle_t	lpe_bsh;
134239278Sgonzo#define	LPE_FLAG_LINK		(1 << 0)
135239278Sgonzo	uint32_t		lpe_flags;
136239278Sgonzo	int			lpe_watchdog_timer;
137239278Sgonzo	struct callout		lpe_tick;
138239278Sgonzo	struct lpe_chain_data	lpe_cdata;
139239278Sgonzo	struct lpe_ring_data	lpe_rdata;
140239278Sgonzo};
141239278Sgonzo
142239278Sgonzostatic int lpe_probe(device_t);
143239278Sgonzostatic int lpe_attach(device_t);
144239278Sgonzostatic int lpe_detach(device_t);
145239278Sgonzostatic int lpe_miibus_readreg(device_t, int, int);
146239278Sgonzostatic int lpe_miibus_writereg(device_t, int, int, int);
147239278Sgonzostatic void lpe_miibus_statchg(device_t);
148239278Sgonzo
149239278Sgonzostatic void lpe_reset(struct lpe_softc *);
150239278Sgonzostatic void lpe_init(void *);
151239278Sgonzostatic void lpe_init_locked(struct lpe_softc *);
152239278Sgonzostatic void lpe_start(struct ifnet *);
153239278Sgonzostatic void lpe_start_locked(struct ifnet *);
154239278Sgonzostatic void lpe_stop(struct lpe_softc *);
155239278Sgonzostatic void lpe_stop_locked(struct lpe_softc *);
156239278Sgonzostatic int lpe_ioctl(struct ifnet *, u_long, caddr_t);
157239278Sgonzostatic void lpe_set_rxmode(struct lpe_softc *);
158239278Sgonzostatic void lpe_set_rxfilter(struct lpe_softc *);
159239278Sgonzostatic void lpe_intr(void *);
160239278Sgonzostatic void lpe_rxintr(struct lpe_softc *);
161239278Sgonzostatic void lpe_txintr(struct lpe_softc *);
162239278Sgonzostatic void lpe_tick(void *);
163239278Sgonzostatic void lpe_watchdog(struct lpe_softc *);
164239278Sgonzostatic int lpe_encap(struct lpe_softc *, struct mbuf **);
165239278Sgonzostatic int lpe_dma_alloc(struct lpe_softc *);
166239278Sgonzostatic int lpe_dma_alloc_rx(struct lpe_softc *);
167239278Sgonzostatic int lpe_dma_alloc_tx(struct lpe_softc *);
168239278Sgonzostatic int lpe_init_rx(struct lpe_softc *);
169239278Sgonzostatic int lpe_init_rxbuf(struct lpe_softc *, int);
170239278Sgonzostatic void lpe_discard_rxbuf(struct lpe_softc *, int);
171239278Sgonzostatic void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int);
172239278Sgonzostatic int lpe_ifmedia_upd(struct ifnet *);
173239278Sgonzostatic void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
174239278Sgonzo
175239278Sgonzo#define	lpe_lock(_sc)		mtx_lock(&(_sc)->lpe_mtx)
176239278Sgonzo#define	lpe_unlock(_sc)		mtx_unlock(&(_sc)->lpe_mtx)
177239278Sgonzo#define	lpe_lock_assert(sc)	mtx_assert(&(_sc)->lpe_mtx, MA_OWNED)
178239278Sgonzo
179239278Sgonzo#define	lpe_read_4(_sc, _reg)		\
180239278Sgonzo    bus_space_read_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg))
181239278Sgonzo#define	lpe_write_4(_sc, _reg, _val)	\
182239278Sgonzo    bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val))
183239278Sgonzo
184239278Sgonzo#define	LPE_HWDESC_RXERRS	(LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \
185239278Sgonzo    LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \
186239278Sgonzo    LPE_HWDESC_RXNODESCR)
187239278Sgonzo
188239278Sgonzo#define	LPE_HWDESC_TXERRS	(LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \
189239278Sgonzo    LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR)
190239278Sgonzo
191239278Sgonzostatic int
192239278Sgonzolpe_probe(device_t dev)
193239278Sgonzo{
194239278Sgonzo
195239278Sgonzo	if (!ofw_bus_is_compatible(dev, "lpc,ethernet"))
196239278Sgonzo		return (ENXIO);
197239278Sgonzo
198239278Sgonzo	device_set_desc(dev, "LPC32x0 10/100 Ethernet");
199239278Sgonzo	return (BUS_PROBE_DEFAULT);
200239278Sgonzo}
201239278Sgonzo
202239278Sgonzostatic int
203239278Sgonzolpe_attach(device_t dev)
204239278Sgonzo{
205239278Sgonzo	struct lpe_softc *sc = device_get_softc(dev);
206239278Sgonzo	struct ifnet *ifp;
207239278Sgonzo	int rid, i;
208239278Sgonzo	uint32_t val;
209239278Sgonzo
210239278Sgonzo	sc->lpe_dev = dev;
211239278Sgonzo	sc->lpe_ofw = ofw_bus_get_node(dev);
212239278Sgonzo
213239278Sgonzo	i = OF_getprop(sc->lpe_ofw, "local-mac-address", (void *)&sc->lpe_enaddr, 6);
214239278Sgonzo	if (i != 6) {
215239278Sgonzo		sc->lpe_enaddr[0] = 0x00;
216239278Sgonzo		sc->lpe_enaddr[1] = 0x11;
217239278Sgonzo		sc->lpe_enaddr[2] = 0x22;
218239278Sgonzo		sc->lpe_enaddr[3] = 0x33;
219239278Sgonzo		sc->lpe_enaddr[4] = 0x44;
220239278Sgonzo		sc->lpe_enaddr[5] = 0x55;
221239278Sgonzo	}
222239278Sgonzo
223239278Sgonzo	mtx_init(&sc->lpe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
224239278Sgonzo	    MTX_DEF);
225239278Sgonzo
226239278Sgonzo	callout_init_mtx(&sc->lpe_tick, &sc->lpe_mtx, 0);
227239278Sgonzo
228239278Sgonzo	rid = 0;
229239278Sgonzo	sc->lpe_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
230239278Sgonzo	    RF_ACTIVE);
231239278Sgonzo	if (!sc->lpe_mem_res) {
232239278Sgonzo		device_printf(dev, "cannot allocate memory window\n");
233239278Sgonzo		goto fail;
234239278Sgonzo	}
235239278Sgonzo
236239278Sgonzo	sc->lpe_bst = rman_get_bustag(sc->lpe_mem_res);
237239278Sgonzo	sc->lpe_bsh = rman_get_bushandle(sc->lpe_mem_res);
238239278Sgonzo
239239278Sgonzo	rid = 0;
240239278Sgonzo	sc->lpe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
241239278Sgonzo	    RF_ACTIVE);
242239278Sgonzo	if (!sc->lpe_irq_res) {
243239278Sgonzo		device_printf(dev, "cannot allocate interrupt\n");
244239278Sgonzo		goto fail;
245239278Sgonzo	}
246239278Sgonzo
247239278Sgonzo	sc->lpe_ifp = if_alloc(IFT_ETHER);
248239278Sgonzo	if (!sc->lpe_ifp) {
249239278Sgonzo		device_printf(dev, "cannot allocated ifnet\n");
250239278Sgonzo		goto fail;
251239278Sgonzo	}
252239278Sgonzo
253239278Sgonzo	ifp = sc->lpe_ifp;
254239278Sgonzo
255239278Sgonzo	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
256239278Sgonzo	ifp->if_softc = sc;
257239278Sgonzo	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
258239278Sgonzo	ifp->if_start = lpe_start;
259239278Sgonzo	ifp->if_ioctl = lpe_ioctl;
260239278Sgonzo	ifp->if_init = lpe_init;
261239278Sgonzo	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
262239278Sgonzo	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
263239278Sgonzo	IFQ_SET_READY(&ifp->if_snd);
264239278Sgonzo
265239278Sgonzo	ether_ifattach(ifp, sc->lpe_enaddr);
266239278Sgonzo
267239278Sgonzo	if (bus_setup_intr(dev, sc->lpe_irq_res, INTR_TYPE_NET, NULL,
268239278Sgonzo	    lpe_intr, sc, &sc->lpe_intrhand)) {
269239278Sgonzo		device_printf(dev, "cannot establish interrupt handler\n");
270239278Sgonzo		ether_ifdetach(ifp);
271239278Sgonzo		goto fail;
272239278Sgonzo	}
273239278Sgonzo
274239278Sgonzo	/* Enable Ethernet clock */
275239278Sgonzo	lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL,
276239278Sgonzo	    LPC_CLKPWR_MACCLK_CTRL_REG |
277239278Sgonzo	    LPC_CLKPWR_MACCLK_CTRL_SLAVE |
278239278Sgonzo	    LPC_CLKPWR_MACCLK_CTRL_MASTER |
279239278Sgonzo	    LPC_CLKPWR_MACCLK_CTRL_HDWINF(3));
280239278Sgonzo
281239278Sgonzo	/* Reset chip */
282239278Sgonzo	lpe_reset(sc);
283239278Sgonzo
284239278Sgonzo	/* Initialize MII */
285239278Sgonzo	val = lpe_read_4(sc, LPE_COMMAND);
286239278Sgonzo	lpe_write_4(sc, LPE_COMMAND, val | LPE_COMMAND_RMII);
287239278Sgonzo
288239278Sgonzo	if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd,
289239278Sgonzo	    lpe_ifmedia_sts, BMSR_DEFCAPMASK, 0x01,
290239278Sgonzo	    MII_OFFSET_ANY, 0)) {
291239278Sgonzo		device_printf(dev, "cannot find PHY\n");
292239278Sgonzo		goto fail;
293239278Sgonzo	}
294239278Sgonzo
295239278Sgonzo	lpe_dma_alloc(sc);
296239278Sgonzo
297239278Sgonzo	return (0);
298239278Sgonzo
299239278Sgonzofail:
300239278Sgonzo	if (sc->lpe_ifp)
301239278Sgonzo		if_free(sc->lpe_ifp);
302239278Sgonzo	if (sc->lpe_intrhand)
303239278Sgonzo		bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
304239278Sgonzo	if (sc->lpe_irq_res)
305239278Sgonzo		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
306239278Sgonzo	if (sc->lpe_mem_res)
307239278Sgonzo		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
308239278Sgonzo	return (ENXIO);
309239278Sgonzo}
310239278Sgonzo
311239278Sgonzostatic int
312239278Sgonzolpe_detach(device_t dev)
313239278Sgonzo{
314239278Sgonzo	struct lpe_softc *sc = device_get_softc(dev);
315239278Sgonzo
316239278Sgonzo	lpe_stop(sc);
317239278Sgonzo
318239278Sgonzo	if_free(sc->lpe_ifp);
319239278Sgonzo	bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
320239278Sgonzo	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
321239278Sgonzo	bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
322239278Sgonzo
323239278Sgonzo	return (0);
324239278Sgonzo}
325239278Sgonzo
326239278Sgonzostatic int
327239278Sgonzolpe_miibus_readreg(device_t dev, int phy, int reg)
328239278Sgonzo{
329239278Sgonzo	struct lpe_softc *sc = device_get_softc(dev);
330239278Sgonzo	uint32_t val;
331239278Sgonzo	int result;
332239278Sgonzo
333239278Sgonzo	lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ);
334239278Sgonzo	lpe_write_4(sc, LPE_MADR,
335239278Sgonzo	    (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT |
336239278Sgonzo	    (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT);
337239278Sgonzo
338239278Sgonzo	val = lpe_read_4(sc, LPE_MIND);
339239278Sgonzo
340239278Sgonzo	/* Wait until request is completed */
341239278Sgonzo	while (val & LPE_MIND_BUSY) {
342239278Sgonzo		val = lpe_read_4(sc, LPE_MIND);
343239278Sgonzo		DELAY(10);
344239278Sgonzo	}
345239278Sgonzo
346239278Sgonzo	if (val & LPE_MIND_INVALID)
347239278Sgonzo		return (0);
348239278Sgonzo
349239278Sgonzo	lpe_write_4(sc, LPE_MCMD, 0);
350239278Sgonzo	result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
351239278Sgonzo	debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result);
352239278Sgonzo
353239278Sgonzo	return (result);
354239278Sgonzo}
355239278Sgonzo
356239278Sgonzostatic int
357239278Sgonzolpe_miibus_writereg(device_t dev, int phy, int reg, int data)
358239278Sgonzo{
359239278Sgonzo	struct lpe_softc *sc = device_get_softc(dev);
360239278Sgonzo	uint32_t val;
361239278Sgonzo
362239278Sgonzo	debugf("phy=%d reg=%d data=0x%04x\n", phy, reg, data);
363239278Sgonzo
364239278Sgonzo	lpe_write_4(sc, LPE_MCMD, LPE_MCMD_WRITE);
365239278Sgonzo	lpe_write_4(sc, LPE_MADR,
366239278Sgonzo	    (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT |
367239278Sgonzo	    (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT);
368239278Sgonzo
369239278Sgonzo	lpe_write_4(sc, LPE_MWTD, (data & LPE_MWTD_DATAMASK));
370239278Sgonzo
371239278Sgonzo	val = lpe_read_4(sc, LPE_MIND);
372239278Sgonzo
373239278Sgonzo	/* Wait until request is completed */
374239278Sgonzo	while (val & LPE_MIND_BUSY) {
375239278Sgonzo		val = lpe_read_4(sc, LPE_MIND);
376239278Sgonzo		DELAY(10);
377239278Sgonzo	}
378239278Sgonzo
379239278Sgonzo	return (0);
380239278Sgonzo}
381239278Sgonzo
382239278Sgonzostatic void
383239278Sgonzolpe_miibus_statchg(device_t dev)
384239278Sgonzo{
385239278Sgonzo	struct lpe_softc *sc = device_get_softc(dev);
386239278Sgonzo	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
387239278Sgonzo
388239278Sgonzo	lpe_lock(sc);
389239278Sgonzo
390239278Sgonzo	if ((mii->mii_media_status & IFM_ACTIVE) &&
391239278Sgonzo	    (mii->mii_media_status & IFM_AVALID))
392239278Sgonzo		sc->lpe_flags |= LPE_FLAG_LINK;
393239278Sgonzo	else
394239278Sgonzo		sc->lpe_flags &= ~LPE_FLAG_LINK;
395239278Sgonzo
396239278Sgonzo	lpe_unlock(sc);
397239278Sgonzo}
398239278Sgonzo
399239278Sgonzostatic void
400239278Sgonzolpe_reset(struct lpe_softc *sc)
401239278Sgonzo{
402239278Sgonzo	uint32_t mac1;
403239278Sgonzo
404239278Sgonzo	/* Enter soft reset mode */
405239278Sgonzo	mac1 = lpe_read_4(sc, LPE_MAC1);
406239278Sgonzo	lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
407239278Sgonzo	    LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX);
408239278Sgonzo
409239278Sgonzo	/* Reset registers, Tx path and Rx path */
410239278Sgonzo	lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET |
411239278Sgonzo	    LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET);
412239278Sgonzo
413239278Sgonzo	/* Set station address */
414239278Sgonzo	lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]);
415239278Sgonzo	lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]);
416239278Sgonzo	lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]);
417239278Sgonzo
418239278Sgonzo	/* Leave soft reset mode */
419239278Sgonzo	mac1 = lpe_read_4(sc, LPE_MAC1);
420239278Sgonzo	lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
421239278Sgonzo	    LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX));
422239278Sgonzo}
423239278Sgonzo
424239278Sgonzostatic void
425239278Sgonzolpe_init(void *arg)
426239278Sgonzo{
427239278Sgonzo	struct lpe_softc *sc = (struct lpe_softc *)arg;
428239278Sgonzo
429239278Sgonzo	lpe_lock(sc);
430239278Sgonzo	lpe_init_locked(sc);
431239278Sgonzo	lpe_unlock(sc);
432239278Sgonzo}
433239278Sgonzo
434239278Sgonzostatic void
435239278Sgonzolpe_init_locked(struct lpe_softc *sc)
436239278Sgonzo{
437239278Sgonzo	struct ifnet *ifp = sc->lpe_ifp;
438239278Sgonzo	uint32_t cmd, mac1;
439239278Sgonzo
440239278Sgonzo	lpe_lock_assert(sc);
441239278Sgonzo
442239278Sgonzo	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
443239278Sgonzo		return;
444239278Sgonzo
445239278Sgonzo	/* Enable Tx and Rx */
446239278Sgonzo	cmd = lpe_read_4(sc, LPE_COMMAND);
447239278Sgonzo	lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE |
448239278Sgonzo	    LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME);
449239278Sgonzo
450239278Sgonzo	/* Enable receive */
451239278Sgonzo	mac1 = lpe_read_4(sc, LPE_MAC1);
452239278Sgonzo	lpe_write_4(sc, LPE_MAC1, /*mac1 |*/ LPE_MAC1_RXENABLE | LPE_MAC1_PASSALL);
453239278Sgonzo
454239278Sgonzo	lpe_write_4(sc, LPE_MAC2, LPE_MAC2_CRCENABLE | LPE_MAC2_PADCRCENABLE |
455239278Sgonzo	    LPE_MAC2_FULLDUPLEX);
456239278Sgonzo
457239278Sgonzo	lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7));
458239278Sgonzo
459239278Sgonzo	/* Set up Rx filter */
460239278Sgonzo	lpe_set_rxmode(sc);
461239278Sgonzo
462239278Sgonzo	/* Enable interrupts */
463239278Sgonzo	lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR |
464239278Sgonzo	    LPE_INT_RXFINISH | LPE_INT_RXDONE | LPE_INT_TXUNDERRUN |
465239278Sgonzo	    LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE);
466239278Sgonzo
467239278Sgonzo	sc->lpe_cdata.lpe_tx_prod = 0;
468239278Sgonzo	sc->lpe_cdata.lpe_tx_last = 0;
469239278Sgonzo	sc->lpe_cdata.lpe_tx_used = 0;
470239278Sgonzo
471239278Sgonzo	lpe_init_rx(sc);
472239278Sgonzo
473239278Sgonzo	/* Initialize Rx packet and status descriptor heads */
474239278Sgonzo	lpe_write_4(sc, LPE_RXDESC, sc->lpe_rdata.lpe_rx_ring_phys);
475239278Sgonzo	lpe_write_4(sc, LPE_RXSTATUS, sc->lpe_rdata.lpe_rx_status_phys);
476239278Sgonzo	lpe_write_4(sc, LPE_RXDESC_NUMBER, LPE_RXDESC_NUM - 1);
477239278Sgonzo	lpe_write_4(sc, LPE_RXDESC_CONS, 0);
478239278Sgonzo
479239278Sgonzo	/* Initialize Tx packet and status descriptor heads */
480239278Sgonzo	lpe_write_4(sc, LPE_TXDESC, sc->lpe_rdata.lpe_tx_ring_phys);
481239278Sgonzo	lpe_write_4(sc, LPE_TXSTATUS, sc->lpe_rdata.lpe_tx_status_phys);
482239278Sgonzo	lpe_write_4(sc, LPE_TXDESC_NUMBER, LPE_TXDESC_NUM - 1);
483239278Sgonzo	lpe_write_4(sc, LPE_TXDESC_PROD, 0);
484239278Sgonzo
485239278Sgonzo	ifp->if_drv_flags |= IFF_DRV_RUNNING;
486239278Sgonzo	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
487239278Sgonzo
488239278Sgonzo	callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
489239278Sgonzo}
490239278Sgonzo
491239278Sgonzostatic void
492239278Sgonzolpe_start(struct ifnet *ifp)
493239278Sgonzo{
494239278Sgonzo	struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc;
495239278Sgonzo
496239278Sgonzo	lpe_lock(sc);
497239278Sgonzo	lpe_start_locked(ifp);
498239278Sgonzo	lpe_unlock(sc);
499239278Sgonzo}
500239278Sgonzo
501239278Sgonzostatic void
502239278Sgonzolpe_start_locked(struct ifnet *ifp)
503239278Sgonzo{
504239278Sgonzo	struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc;
505239278Sgonzo	struct mbuf *m_head;
506239278Sgonzo	int encap = 0;
507239278Sgonzo
508239278Sgonzo	lpe_lock_assert(sc);
509239278Sgonzo
510239278Sgonzo	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
511239278Sgonzo		if (lpe_read_4(sc, LPE_TXDESC_PROD) ==
512239278Sgonzo		    lpe_read_4(sc, LPE_TXDESC_CONS) - 5)
513239278Sgonzo			break;
514239278Sgonzo
515239278Sgonzo		/* Dequeue first packet */
516239278Sgonzo		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
517239278Sgonzo		if (!m_head)
518239278Sgonzo			break;
519239278Sgonzo
520239278Sgonzo		lpe_encap(sc, &m_head);
521239278Sgonzo
522239278Sgonzo		encap++;
523239278Sgonzo	}
524239278Sgonzo
525239278Sgonzo	/* Submit new descriptor list */
526239278Sgonzo	if (encap) {
527239278Sgonzo		lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod);
528239278Sgonzo		sc->lpe_watchdog_timer = 5;
529239278Sgonzo	}
530239278Sgonzo
531239278Sgonzo}
532239278Sgonzo
533239278Sgonzostatic int
534239278Sgonzolpe_encap(struct lpe_softc *sc, struct mbuf **m_head)
535239278Sgonzo{
536239278Sgonzo	struct lpe_txdesc *txd;
537239278Sgonzo	struct lpe_hwdesc *hwd;
538239278Sgonzo	bus_dma_segment_t segs[LPE_MAXFRAGS];
539239278Sgonzo	int i, err, nsegs, prod;
540239278Sgonzo
541239278Sgonzo	lpe_lock_assert(sc);
542239278Sgonzo	M_ASSERTPKTHDR((*m_head));
543239278Sgonzo
544239278Sgonzo	prod = sc->lpe_cdata.lpe_tx_prod;
545239278Sgonzo	txd = &sc->lpe_cdata.lpe_tx_desc[prod];
546239278Sgonzo
547239278Sgonzo	debugf("starting with prod=%d\n", prod);
548239278Sgonzo
549239278Sgonzo	err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag,
550239278Sgonzo	    txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
551239278Sgonzo
552239278Sgonzo	if (err)
553239278Sgonzo		return (err);
554239278Sgonzo
555239278Sgonzo	if (nsegs == 0) {
556239278Sgonzo		m_freem(*m_head);
557239278Sgonzo		*m_head = NULL;
558239278Sgonzo		return (EIO);
559239278Sgonzo	}
560239278Sgonzo
561239278Sgonzo        bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, txd->lpe_txdesc_dmamap,
562239278Sgonzo          BUS_DMASYNC_PREREAD);
563239278Sgonzo        bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map,
564239278Sgonzo            BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
565239278Sgonzo
566239278Sgonzo	txd->lpe_txdesc_first = 1;
567239278Sgonzo	txd->lpe_txdesc_mbuf = *m_head;
568239278Sgonzo
569239278Sgonzo	for (i = 0; i < nsegs; i++) {
570239278Sgonzo		hwd = &sc->lpe_rdata.lpe_tx_ring[prod];
571239278Sgonzo		hwd->lhr_data = segs[i].ds_addr;
572239278Sgonzo		hwd->lhr_control = segs[i].ds_len - 1;
573239278Sgonzo
574239278Sgonzo		if (i == nsegs - 1) {
575239278Sgonzo			hwd->lhr_control |= LPE_HWDESC_LASTFLAG;
576239278Sgonzo			hwd->lhr_control |= LPE_HWDESC_INTERRUPT;
577239278Sgonzo			hwd->lhr_control |= LPE_HWDESC_CRC;
578239278Sgonzo			hwd->lhr_control |= LPE_HWDESC_PAD;
579239278Sgonzo		}
580239278Sgonzo
581239278Sgonzo		LPE_INC(prod, LPE_TXDESC_NUM);
582239278Sgonzo	}
583239278Sgonzo
584239278Sgonzo	bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map,
585239278Sgonzo	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
586239278Sgonzo
587239278Sgonzo	sc->lpe_cdata.lpe_tx_used += nsegs;
588239278Sgonzo	sc->lpe_cdata.lpe_tx_prod = prod;
589239278Sgonzo
590239278Sgonzo	return (0);
591239278Sgonzo}
592239278Sgonzo
593239278Sgonzostatic void
594239278Sgonzolpe_stop(struct lpe_softc *sc)
595239278Sgonzo{
596239278Sgonzo	lpe_lock(sc);
597239278Sgonzo	lpe_stop_locked(sc);
598239278Sgonzo	lpe_unlock(sc);
599239278Sgonzo}
600239278Sgonzo
601239278Sgonzostatic void
602239278Sgonzolpe_stop_locked(struct lpe_softc *sc)
603239278Sgonzo{
604239278Sgonzo	lpe_lock_assert(sc);
605239278Sgonzo
606239278Sgonzo	callout_stop(&sc->lpe_tick);
607239278Sgonzo
608239278Sgonzo	/* Disable interrupts */
609239278Sgonzo	lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff);
610239278Sgonzo
611239278Sgonzo	/* Stop EMAC */
612239278Sgonzo	lpe_write_4(sc, LPE_MAC1, 0);
613239278Sgonzo	lpe_write_4(sc, LPE_MAC2, 0);
614239278Sgonzo	lpe_write_4(sc, LPE_COMMAND, 0);
615239278Sgonzo
616239278Sgonzo	sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
617239278Sgonzo	sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
618239278Sgonzo}
619239278Sgonzo
620239278Sgonzostatic int
621239278Sgonzolpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
622239278Sgonzo{
623239278Sgonzo	struct lpe_softc *sc = ifp->if_softc;
624239278Sgonzo	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
625239278Sgonzo	struct ifreq *ifr = (struct ifreq *)data;
626239278Sgonzo	int err = 0;
627239278Sgonzo
628239278Sgonzo	switch (cmd) {
629239278Sgonzo	case SIOCSIFFLAGS:
630239278Sgonzo		lpe_lock(sc);
631239278Sgonzo		if (ifp->if_flags & IFF_UP) {
632239278Sgonzo			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
633239278Sgonzo				lpe_set_rxmode(sc);
634239278Sgonzo				lpe_set_rxfilter(sc);
635239278Sgonzo			} else
636239278Sgonzo				lpe_init_locked(sc);
637239278Sgonzo		} else
638239278Sgonzo			lpe_stop(sc);
639239278Sgonzo		lpe_unlock(sc);
640239278Sgonzo		break;
641239278Sgonzo	case SIOCADDMULTI:
642239278Sgonzo	case SIOCDELMULTI:
643239278Sgonzo		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
644239278Sgonzo			lpe_lock(sc);
645239278Sgonzo			lpe_set_rxfilter(sc);
646239278Sgonzo			lpe_unlock(sc);
647239278Sgonzo		}
648239278Sgonzo		break;
649239278Sgonzo	case SIOCGIFMEDIA:
650239278Sgonzo	case SIOCSIFMEDIA:
651239278Sgonzo		err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
652239278Sgonzo		break;
653239278Sgonzo	default:
654239278Sgonzo		err = ether_ioctl(ifp, cmd, data);
655239278Sgonzo		break;
656239278Sgonzo	}
657239278Sgonzo
658239278Sgonzo	return (err);
659239278Sgonzo}
660239278Sgonzo
661239278Sgonzostatic void lpe_set_rxmode(struct lpe_softc *sc)
662239278Sgonzo{
663239278Sgonzo	struct ifnet *ifp = sc->lpe_ifp;
664239278Sgonzo	uint32_t rxfilt;
665239278Sgonzo
666239278Sgonzo	rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT;
667239278Sgonzo
668239278Sgonzo	if (ifp->if_flags & IFF_BROADCAST)
669239278Sgonzo		rxfilt |= LPE_RXFILTER_BROADCAST;
670239278Sgonzo
671239278Sgonzo	if (ifp->if_flags & IFF_PROMISC)
672239278Sgonzo		rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST;
673239278Sgonzo
674239278Sgonzo	if (ifp->if_flags & IFF_ALLMULTI)
675239278Sgonzo		rxfilt |= LPE_RXFILTER_MULTICAST;
676239278Sgonzo
677239278Sgonzo	lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt);
678239278Sgonzo}
679239278Sgonzo
680239278Sgonzostatic void lpe_set_rxfilter(struct lpe_softc *sc)
681239278Sgonzo{
682239278Sgonzo	struct ifnet *ifp = sc->lpe_ifp;
683239278Sgonzo	struct ifmultiaddr *ifma;
684239278Sgonzo	int index;
685239278Sgonzo	uint32_t hashl, hashh;
686239278Sgonzo
687239278Sgonzo	hashl = 0;
688239278Sgonzo	hashh = 0;
689239278Sgonzo
690239278Sgonzo	if_maddr_rlock(ifp);
691239278Sgonzo	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
692239278Sgonzo		if (ifma->ifma_addr->sa_family != AF_LINK)
693239278Sgonzo			continue;
694239278Sgonzo
695239278Sgonzo		index = ether_crc32_be(LLADDR((struct sockaddr_dl *)
696239278Sgonzo		    ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f;
697239278Sgonzo
698239278Sgonzo		if (index > 31)
699239278Sgonzo			hashh |= (1 << (index - 32));
700239278Sgonzo		else
701239278Sgonzo			hashl |= (1 << index);
702239278Sgonzo	}
703239278Sgonzo	if_maddr_runlock(ifp);
704239278Sgonzo
705239278Sgonzo	/* Program new hash filter */
706239278Sgonzo	lpe_write_4(sc, LPE_HASHFILTER_L, hashl);
707239278Sgonzo	lpe_write_4(sc, LPE_HASHFILTER_H, hashh);
708239278Sgonzo}
709239278Sgonzo
710239278Sgonzostatic void
711239278Sgonzolpe_intr(void *arg)
712239278Sgonzo{
713239278Sgonzo	struct lpe_softc *sc = (struct lpe_softc *)arg;
714239278Sgonzo	uint32_t intstatus;
715239278Sgonzo
716239278Sgonzo	debugf("status=0x%08x\n", lpe_read_4(sc, LPE_INTSTATUS));
717239278Sgonzo
718239278Sgonzo	lpe_lock(sc);
719239278Sgonzo
720239278Sgonzo	while ((intstatus = lpe_read_4(sc, LPE_INTSTATUS))) {
721239278Sgonzo		if (intstatus & LPE_INT_RXDONE)
722239278Sgonzo			lpe_rxintr(sc);
723239278Sgonzo
724239278Sgonzo		if (intstatus & LPE_INT_TXDONE)
725239278Sgonzo			lpe_txintr(sc);
726239278Sgonzo
727239278Sgonzo		lpe_write_4(sc, LPE_INTCLEAR, 0xffff);
728239278Sgonzo	}
729239278Sgonzo
730239278Sgonzo	lpe_unlock(sc);
731239278Sgonzo}
732239278Sgonzo
733239278Sgonzostatic void
734239278Sgonzolpe_rxintr(struct lpe_softc *sc)
735239278Sgonzo{
736239278Sgonzo	struct ifnet *ifp = sc->lpe_ifp;
737239278Sgonzo	struct lpe_hwdesc *hwd;
738239278Sgonzo	struct lpe_hwstatus *hws;
739239278Sgonzo	struct lpe_rxdesc *rxd;
740239278Sgonzo	struct mbuf *m;
741239278Sgonzo	int prod, cons;
742239278Sgonzo
743239278Sgonzo	for (;;) {
744239278Sgonzo		prod = lpe_read_4(sc, LPE_RXDESC_PROD);
745239278Sgonzo		cons = lpe_read_4(sc, LPE_RXDESC_CONS);
746239278Sgonzo
747239278Sgonzo		if (prod == cons)
748239278Sgonzo			break;
749239278Sgonzo
750239278Sgonzo		rxd = &sc->lpe_cdata.lpe_rx_desc[cons];
751239278Sgonzo		hwd = &sc->lpe_rdata.lpe_rx_ring[cons];
752239278Sgonzo		hws = &sc->lpe_rdata.lpe_rx_status[cons];
753239278Sgonzo
754239278Sgonzo		/* Check received frame for errors */
755239278Sgonzo		if (hws->lhs_info & LPE_HWDESC_RXERRS) {
756239278Sgonzo			ifp->if_ierrors++;
757239278Sgonzo			lpe_discard_rxbuf(sc, cons);
758239278Sgonzo			lpe_init_rxbuf(sc, cons);
759239278Sgonzo			goto skip;
760239278Sgonzo		}
761239278Sgonzo
762239278Sgonzo		m = rxd->lpe_rxdesc_mbuf;
763239278Sgonzo		m->m_pkthdr.rcvif = ifp;
764239278Sgonzo		m->m_data += 2;
765239278Sgonzo
766239278Sgonzo		ifp->if_ipackets++;
767239278Sgonzo
768239278Sgonzo		lpe_unlock(sc);
769239278Sgonzo		(*ifp->if_input)(ifp, m);
770239278Sgonzo		lpe_lock(sc);
771239278Sgonzo
772239278Sgonzo		lpe_init_rxbuf(sc, cons);
773239278Sgonzoskip:
774239278Sgonzo		LPE_INC(cons, LPE_RXDESC_NUM);
775239278Sgonzo		lpe_write_4(sc, LPE_RXDESC_CONS, cons);
776239278Sgonzo	}
777239278Sgonzo}
778239278Sgonzo
779239278Sgonzostatic void
780239278Sgonzolpe_txintr(struct lpe_softc *sc)
781239278Sgonzo{
782239278Sgonzo	struct ifnet *ifp = sc->lpe_ifp;
783239278Sgonzo	struct lpe_hwdesc *hwd;
784239278Sgonzo	struct lpe_hwstatus *hws;
785239278Sgonzo	struct lpe_txdesc *txd;
786239278Sgonzo	int cons, last;
787239278Sgonzo
788239278Sgonzo	for (;;) {
789239278Sgonzo		cons = lpe_read_4(sc, LPE_TXDESC_CONS);
790239278Sgonzo		last = sc->lpe_cdata.lpe_tx_last;
791239278Sgonzo
792239278Sgonzo		if (cons == last)
793239278Sgonzo			break;
794239278Sgonzo
795239278Sgonzo		txd = &sc->lpe_cdata.lpe_tx_desc[last];
796239278Sgonzo		hwd = &sc->lpe_rdata.lpe_tx_ring[last];
797239278Sgonzo		hws = &sc->lpe_rdata.lpe_tx_status[last];
798239278Sgonzo
799239278Sgonzo		bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag,
800239278Sgonzo		    txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE);
801239278Sgonzo
802239278Sgonzo		ifp->if_collisions += LPE_HWDESC_COLLISIONS(hws->lhs_info);
803239278Sgonzo
804239278Sgonzo		if (hws->lhs_info & LPE_HWDESC_TXERRS)
805239278Sgonzo			ifp->if_oerrors++;
806239278Sgonzo		else
807239278Sgonzo			ifp->if_opackets++;
808239278Sgonzo
809239278Sgonzo		if (txd->lpe_txdesc_first) {
810239278Sgonzo			bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag,
811239278Sgonzo			    txd->lpe_txdesc_dmamap);
812239278Sgonzo
813239278Sgonzo			m_freem(txd->lpe_txdesc_mbuf);
814239278Sgonzo			txd->lpe_txdesc_mbuf = NULL;
815239278Sgonzo			txd->lpe_txdesc_first = 0;
816239278Sgonzo		}
817239278Sgonzo
818239278Sgonzo		sc->lpe_cdata.lpe_tx_used--;
819239278Sgonzo		LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM);
820239278Sgonzo	}
821239278Sgonzo
822239278Sgonzo	if (!sc->lpe_cdata.lpe_tx_used)
823239278Sgonzo		sc->lpe_watchdog_timer = 0;
824239278Sgonzo}
825239278Sgonzo
826239278Sgonzostatic void
827239278Sgonzolpe_tick(void *arg)
828239278Sgonzo{
829239278Sgonzo	struct lpe_softc *sc = (struct lpe_softc *)arg;
830239278Sgonzo	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
831239278Sgonzo
832239278Sgonzo	lpe_lock_assert(sc);
833239278Sgonzo
834239278Sgonzo	mii_tick(mii);
835239278Sgonzo	lpe_watchdog(sc);
836239278Sgonzo
837239278Sgonzo	callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
838239278Sgonzo}
839239278Sgonzo
840239278Sgonzostatic void
841239278Sgonzolpe_watchdog(struct lpe_softc *sc)
842239278Sgonzo{
843239278Sgonzo	struct ifnet *ifp = sc->lpe_ifp;
844239278Sgonzo
845239278Sgonzo	lpe_lock_assert(sc);
846239278Sgonzo
847239278Sgonzo	if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--)
848239278Sgonzo		return;
849239278Sgonzo
850239278Sgonzo	/* Chip has stopped responding */
851239278Sgonzo	device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n");
852239278Sgonzo	lpe_stop_locked(sc);
853239278Sgonzo	lpe_init_locked(sc);
854239278Sgonzo
855239278Sgonzo	/* Try to resend packets */
856239278Sgonzo	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
857239278Sgonzo		lpe_start_locked(ifp);
858239278Sgonzo}
859239278Sgonzo
860239278Sgonzostatic int
861239278Sgonzolpe_dma_alloc(struct lpe_softc *sc)
862239278Sgonzo{
863239278Sgonzo	int err;
864239278Sgonzo
865239278Sgonzo	/* Create parent DMA tag */
866239278Sgonzo	err = bus_dma_tag_create(
867239278Sgonzo	    bus_get_dma_tag(sc->lpe_dev),
868239278Sgonzo	    1, 0,			/* alignment, boundary */
869239278Sgonzo	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
870239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
871239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
872239278Sgonzo	    BUS_SPACE_MAXSIZE_32BIT, 0,	/* maxsize, nsegments */
873239278Sgonzo	    BUS_SPACE_MAXSIZE_32BIT, 0,	/* maxsegsize, flags */
874239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
875239278Sgonzo	    &sc->lpe_cdata.lpe_parent_tag);
876239278Sgonzo
877239278Sgonzo	if (err) {
878239278Sgonzo		device_printf(sc->lpe_dev, "cannot create parent DMA tag\n");
879239278Sgonzo		return (err);
880239278Sgonzo	}
881239278Sgonzo
882239278Sgonzo	err = lpe_dma_alloc_rx(sc);
883239278Sgonzo	if (err)
884239278Sgonzo		return (err);
885239278Sgonzo
886239278Sgonzo	err = lpe_dma_alloc_tx(sc);
887239278Sgonzo	if (err)
888239278Sgonzo		return (err);
889239278Sgonzo
890239278Sgonzo	return (0);
891239278Sgonzo}
892239278Sgonzo
893239278Sgonzostatic int
894239278Sgonzolpe_dma_alloc_rx(struct lpe_softc *sc)
895239278Sgonzo{
896239278Sgonzo	struct lpe_rxdesc *rxd;
897239278Sgonzo	struct lpe_dmamap_arg ctx;
898239278Sgonzo	int err, i;
899239278Sgonzo
900239278Sgonzo	/* Create tag for Rx ring */
901239278Sgonzo	err = bus_dma_tag_create(
902239278Sgonzo	    sc->lpe_cdata.lpe_parent_tag,
903239278Sgonzo	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
904239278Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
905239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
906239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
907239278Sgonzo	    LPE_RXDESC_SIZE, 1,		/* maxsize, nsegments */
908239278Sgonzo	    LPE_RXDESC_SIZE, 0,		/* maxsegsize, flags */
909239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
910239278Sgonzo	    &sc->lpe_cdata.lpe_rx_ring_tag);
911239278Sgonzo
912239278Sgonzo	if (err) {
913239278Sgonzo		device_printf(sc->lpe_dev, "cannot create Rx ring DMA tag\n");
914239278Sgonzo		goto fail;
915239278Sgonzo	}
916239278Sgonzo
917239278Sgonzo	/* Create tag for Rx status ring */
918239278Sgonzo	err = bus_dma_tag_create(
919239278Sgonzo	    sc->lpe_cdata.lpe_parent_tag,
920239278Sgonzo	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
921239278Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
922239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
923239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
924239278Sgonzo	    LPE_RXSTATUS_SIZE, 1,	/* maxsize, nsegments */
925239278Sgonzo	    LPE_RXSTATUS_SIZE, 0,	/* maxsegsize, flags */
926239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
927239278Sgonzo	    &sc->lpe_cdata.lpe_rx_status_tag);
928239278Sgonzo
929239278Sgonzo	if (err) {
930239278Sgonzo		device_printf(sc->lpe_dev, "cannot create Rx status ring DMA tag\n");
931239278Sgonzo		goto fail;
932239278Sgonzo	}
933239278Sgonzo
934239278Sgonzo	/* Create tag for Rx buffers */
935239278Sgonzo	err = bus_dma_tag_create(
936239278Sgonzo	    sc->lpe_cdata.lpe_parent_tag,
937239278Sgonzo	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
938239278Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
939239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
940239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
941239278Sgonzo	    MCLBYTES * LPE_RXDESC_NUM,	/* maxsize */
942239278Sgonzo	    LPE_RXDESC_NUM,		/* segments */
943239278Sgonzo	    MCLBYTES, 0,		/* maxsegsize, flags */
944239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
945239278Sgonzo	    &sc->lpe_cdata.lpe_rx_buf_tag);
946239278Sgonzo
947239278Sgonzo	if (err) {
948239278Sgonzo		device_printf(sc->lpe_dev, "cannot create Rx buffers DMA tag\n");
949239278Sgonzo		goto fail;
950239278Sgonzo	}
951239278Sgonzo
952239278Sgonzo	/* Allocate Rx DMA ring */
953239278Sgonzo	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_ring_tag,
954239278Sgonzo	    (void **)&sc->lpe_rdata.lpe_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
955239278Sgonzo	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_ring_map);
956239278Sgonzo
957239278Sgonzo	err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_ring_tag,
958239278Sgonzo	    sc->lpe_cdata.lpe_rx_ring_map, sc->lpe_rdata.lpe_rx_ring,
959239278Sgonzo	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
960239278Sgonzo
961239278Sgonzo	sc->lpe_rdata.lpe_rx_ring_phys = ctx.lpe_dma_busaddr;
962239278Sgonzo
963239278Sgonzo	/* Allocate Rx status ring */
964239278Sgonzo	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_status_tag,
965239278Sgonzo	    (void **)&sc->lpe_rdata.lpe_rx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
966239278Sgonzo	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_status_map);
967239278Sgonzo
968239278Sgonzo	err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_status_tag,
969239278Sgonzo	    sc->lpe_cdata.lpe_rx_status_map, sc->lpe_rdata.lpe_rx_status,
970239278Sgonzo	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
971239278Sgonzo
972239278Sgonzo	sc->lpe_rdata.lpe_rx_status_phys = ctx.lpe_dma_busaddr;
973239278Sgonzo
974239278Sgonzo
975239278Sgonzo	/* Create Rx buffers DMA map */
976239278Sgonzo	for (i = 0; i < LPE_RXDESC_NUM; i++) {
977239278Sgonzo		rxd = &sc->lpe_cdata.lpe_rx_desc[i];
978239278Sgonzo		rxd->lpe_rxdesc_mbuf = NULL;
979239278Sgonzo		rxd->lpe_rxdesc_dmamap = NULL;
980239278Sgonzo
981239278Sgonzo		err = bus_dmamap_create(sc->lpe_cdata.lpe_rx_buf_tag, 0,
982239278Sgonzo		    &rxd->lpe_rxdesc_dmamap);
983239278Sgonzo
984239278Sgonzo		if (err) {
985239278Sgonzo			device_printf(sc->lpe_dev, "cannot create Rx DMA map\n");
986239278Sgonzo			return (err);
987239278Sgonzo		}
988239278Sgonzo	}
989239278Sgonzo
990239278Sgonzo	return (0);
991239278Sgonzofail:
992239278Sgonzo	return (err);
993239278Sgonzo}
994239278Sgonzo
995239278Sgonzostatic int
996239278Sgonzolpe_dma_alloc_tx(struct lpe_softc *sc)
997239278Sgonzo{
998239278Sgonzo	struct lpe_txdesc *txd;
999239278Sgonzo	struct lpe_dmamap_arg ctx;
1000239278Sgonzo	int err, i;
1001239278Sgonzo
1002239278Sgonzo	/* Create tag for Tx ring */
1003239278Sgonzo	err = bus_dma_tag_create(
1004239278Sgonzo	    sc->lpe_cdata.lpe_parent_tag,
1005239278Sgonzo	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
1006239278Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1007239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1008239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
1009239278Sgonzo	    LPE_TXDESC_SIZE, 1,		/* maxsize, nsegments */
1010239278Sgonzo	    LPE_TXDESC_SIZE, 0,		/* maxsegsize, flags */
1011239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1012239278Sgonzo	    &sc->lpe_cdata.lpe_tx_ring_tag);
1013239278Sgonzo
1014239278Sgonzo	if (err) {
1015239278Sgonzo		device_printf(sc->lpe_dev, "cannot create Tx ring DMA tag\n");
1016239278Sgonzo		goto fail;
1017239278Sgonzo	}
1018239278Sgonzo
1019239278Sgonzo	/* Create tag for Tx status ring */
1020239278Sgonzo	err = bus_dma_tag_create(
1021239278Sgonzo	    sc->lpe_cdata.lpe_parent_tag,
1022239278Sgonzo	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
1023239278Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1024239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1025239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
1026239278Sgonzo	    LPE_TXSTATUS_SIZE, 1,	/* maxsize, nsegments */
1027239278Sgonzo	    LPE_TXSTATUS_SIZE, 0,	/* maxsegsize, flags */
1028239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1029239278Sgonzo	    &sc->lpe_cdata.lpe_tx_status_tag);
1030239278Sgonzo
1031239278Sgonzo	if (err) {
1032239278Sgonzo		device_printf(sc->lpe_dev, "cannot create Tx status ring DMA tag\n");
1033239278Sgonzo		goto fail;
1034239278Sgonzo	}
1035239278Sgonzo
1036239278Sgonzo	/* Create tag for Tx buffers */
1037239278Sgonzo	err = bus_dma_tag_create(
1038239278Sgonzo	    sc->lpe_cdata.lpe_parent_tag,
1039239278Sgonzo	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
1040239278Sgonzo	    BUS_SPACE_MAXADDR,		/* lowaddr */
1041239278Sgonzo	    BUS_SPACE_MAXADDR,		/* highaddr */
1042239278Sgonzo	    NULL, NULL,			/* filter, filterarg */
1043239278Sgonzo	    MCLBYTES * LPE_TXDESC_NUM,	/* maxsize */
1044239278Sgonzo	    LPE_TXDESC_NUM,		/* segments */
1045239278Sgonzo	    MCLBYTES, 0,		/* maxsegsize, flags */
1046239278Sgonzo	    NULL, NULL,			/* lockfunc, lockarg */
1047239278Sgonzo	    &sc->lpe_cdata.lpe_tx_buf_tag);
1048239278Sgonzo
1049239278Sgonzo	if (err) {
1050239278Sgonzo		device_printf(sc->lpe_dev, "cannot create Tx buffers DMA tag\n");
1051239278Sgonzo		goto fail;
1052239278Sgonzo	}
1053239278Sgonzo
1054239278Sgonzo	/* Allocate Tx DMA ring */
1055239278Sgonzo	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_ring_tag,
1056239278Sgonzo	    (void **)&sc->lpe_rdata.lpe_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
1057239278Sgonzo	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_ring_map);
1058239278Sgonzo
1059239278Sgonzo	err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_ring_tag,
1060239278Sgonzo	    sc->lpe_cdata.lpe_tx_ring_map, sc->lpe_rdata.lpe_tx_ring,
1061239278Sgonzo	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
1062239278Sgonzo
1063239278Sgonzo	sc->lpe_rdata.lpe_tx_ring_phys = ctx.lpe_dma_busaddr;
1064239278Sgonzo
1065239278Sgonzo	/* Allocate Tx status ring */
1066239278Sgonzo	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_status_tag,
1067239278Sgonzo	    (void **)&sc->lpe_rdata.lpe_tx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
1068239278Sgonzo	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_status_map);
1069239278Sgonzo
1070239278Sgonzo	err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_status_tag,
1071239278Sgonzo	    sc->lpe_cdata.lpe_tx_status_map, sc->lpe_rdata.lpe_tx_status,
1072239278Sgonzo	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
1073239278Sgonzo
1074239278Sgonzo	sc->lpe_rdata.lpe_tx_status_phys = ctx.lpe_dma_busaddr;
1075239278Sgonzo
1076239278Sgonzo
1077239278Sgonzo	/* Create Tx buffers DMA map */
1078239278Sgonzo	for (i = 0; i < LPE_TXDESC_NUM; i++) {
1079239278Sgonzo		txd = &sc->lpe_cdata.lpe_tx_desc[i];
1080239278Sgonzo		txd->lpe_txdesc_mbuf = NULL;
1081239278Sgonzo		txd->lpe_txdesc_dmamap = NULL;
1082239278Sgonzo		txd->lpe_txdesc_first = 0;
1083239278Sgonzo
1084239278Sgonzo		err = bus_dmamap_create(sc->lpe_cdata.lpe_tx_buf_tag, 0,
1085239278Sgonzo		    &txd->lpe_txdesc_dmamap);
1086239278Sgonzo
1087239278Sgonzo		if (err) {
1088239278Sgonzo			device_printf(sc->lpe_dev, "cannot create Tx DMA map\n");
1089239278Sgonzo			return (err);
1090239278Sgonzo		}
1091239278Sgonzo	}
1092239278Sgonzo
1093239278Sgonzo	return (0);
1094239278Sgonzofail:
1095239278Sgonzo	return (err);
1096239278Sgonzo}
1097239278Sgonzo
1098239278Sgonzostatic int
1099239278Sgonzolpe_init_rx(struct lpe_softc *sc)
1100239278Sgonzo{
1101239278Sgonzo	int i, err;
1102239278Sgonzo
1103239278Sgonzo	for (i = 0; i < LPE_RXDESC_NUM; i++) {
1104239278Sgonzo		err = lpe_init_rxbuf(sc, i);
1105239278Sgonzo		if (err)
1106239278Sgonzo			return (err);
1107239278Sgonzo	}
1108239278Sgonzo
1109239278Sgonzo	return (0);
1110239278Sgonzo}
1111239278Sgonzo
1112239278Sgonzostatic int
1113239278Sgonzolpe_init_rxbuf(struct lpe_softc *sc, int n)
1114239278Sgonzo{
1115239278Sgonzo	struct lpe_rxdesc *rxd;
1116239278Sgonzo	struct lpe_hwdesc *hwd;
1117239278Sgonzo	struct lpe_hwstatus *hws;
1118239278Sgonzo	struct mbuf *m;
1119239278Sgonzo	bus_dma_segment_t segs[1];
1120239278Sgonzo	int nsegs;
1121239278Sgonzo
1122239278Sgonzo	rxd = &sc->lpe_cdata.lpe_rx_desc[n];
1123239278Sgonzo	hwd = &sc->lpe_rdata.lpe_rx_ring[n];
1124239278Sgonzo	hws = &sc->lpe_rdata.lpe_rx_status[n];
1125243882Sglebius	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
1126239278Sgonzo
1127239278Sgonzo	if (!m) {
1128239278Sgonzo		device_printf(sc->lpe_dev, "WARNING: mbufs exhausted!\n");
1129239278Sgonzo		return (ENOBUFS);
1130239278Sgonzo	}
1131239278Sgonzo
1132239278Sgonzo	m->m_len = m->m_pkthdr.len = MCLBYTES;
1133239278Sgonzo
1134239278Sgonzo	bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
1135239278Sgonzo
1136239278Sgonzo	if (bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_rx_buf_tag,
1137239278Sgonzo	    rxd->lpe_rxdesc_dmamap, m, segs, &nsegs, 0)) {
1138239278Sgonzo		m_freem(m);
1139239278Sgonzo		return (ENOBUFS);
1140239278Sgonzo	}
1141239278Sgonzo
1142239278Sgonzo	bus_dmamap_sync(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap,
1143239278Sgonzo	    BUS_DMASYNC_PREREAD);
1144239278Sgonzo
1145239278Sgonzo	rxd->lpe_rxdesc_mbuf = m;
1146239278Sgonzo	hwd->lhr_data = segs[0].ds_addr + 2;
1147239278Sgonzo	hwd->lhr_control = (segs[0].ds_len - 1) | LPE_HWDESC_INTERRUPT;
1148239278Sgonzo
1149239278Sgonzo	return (0);
1150239278Sgonzo}
1151239278Sgonzo
1152239278Sgonzostatic void
1153239278Sgonzolpe_discard_rxbuf(struct lpe_softc *sc, int n)
1154239278Sgonzo{
1155239278Sgonzo	struct lpe_rxdesc *rxd;
1156239278Sgonzo	struct lpe_hwdesc *hwd;
1157239278Sgonzo
1158239278Sgonzo	rxd = &sc->lpe_cdata.lpe_rx_desc[n];
1159239278Sgonzo	hwd = &sc->lpe_rdata.lpe_rx_ring[n];
1160239278Sgonzo
1161239278Sgonzo	bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
1162239278Sgonzo
1163239278Sgonzo	hwd->lhr_data = 0;
1164239278Sgonzo	hwd->lhr_control = 0;
1165239278Sgonzo
1166239278Sgonzo	if (rxd->lpe_rxdesc_mbuf) {
1167239278Sgonzo		m_freem(rxd->lpe_rxdesc_mbuf);
1168239278Sgonzo		rxd->lpe_rxdesc_mbuf = NULL;
1169239278Sgonzo	}
1170239278Sgonzo}
1171239278Sgonzo
1172239278Sgonzostatic void
1173239278Sgonzolpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1174239278Sgonzo{
1175239278Sgonzo	struct lpe_dmamap_arg *ctx;
1176239278Sgonzo
1177239278Sgonzo	if (error)
1178239278Sgonzo		return;
1179239278Sgonzo
1180239278Sgonzo	ctx = (struct lpe_dmamap_arg *)arg;
1181239278Sgonzo	ctx->lpe_dma_busaddr = segs[0].ds_addr;
1182239278Sgonzo}
1183239278Sgonzo
1184239278Sgonzostatic int
1185239278Sgonzolpe_ifmedia_upd(struct ifnet *ifp)
1186239278Sgonzo{
1187239278Sgonzo	return (0);
1188239278Sgonzo}
1189239278Sgonzo
1190239278Sgonzostatic void
1191239278Sgonzolpe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
1192239278Sgonzo{
1193239278Sgonzo	struct lpe_softc *sc = ifp->if_softc;
1194239278Sgonzo	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
1195239278Sgonzo
1196239278Sgonzo	lpe_lock(sc);
1197239278Sgonzo	mii_pollstat(mii);
1198239278Sgonzo	ifmr->ifm_active = mii->mii_media_active;
1199239278Sgonzo	ifmr->ifm_status = mii->mii_media_status;
1200239278Sgonzo	lpe_unlock(sc);
1201239278Sgonzo}
1202239278Sgonzo
1203239278Sgonzostatic device_method_t lpe_methods[] = {
1204239278Sgonzo	/* Device interface */
1205239278Sgonzo	DEVMETHOD(device_probe,		lpe_probe),
1206239278Sgonzo	DEVMETHOD(device_attach,	lpe_attach),
1207239278Sgonzo	DEVMETHOD(device_detach,	lpe_detach),
1208239278Sgonzo
1209239278Sgonzo	/* Bus interface */
1210239278Sgonzo	DEVMETHOD(bus_print_child,	bus_generic_print_child),
1211239278Sgonzo
1212239278Sgonzo	/* MII interface */
1213239278Sgonzo	DEVMETHOD(miibus_readreg,	lpe_miibus_readreg),
1214239278Sgonzo	DEVMETHOD(miibus_writereg,	lpe_miibus_writereg),
1215239278Sgonzo	DEVMETHOD(miibus_statchg,	lpe_miibus_statchg),
1216239278Sgonzo	{ 0, 0 }
1217239278Sgonzo};
1218239278Sgonzo
1219239278Sgonzostatic driver_t lpe_driver = {
1220239278Sgonzo	"lpe",
1221239278Sgonzo	lpe_methods,
1222239278Sgonzo	sizeof(struct lpe_softc),
1223239278Sgonzo};
1224239278Sgonzo
1225239278Sgonzostatic devclass_t lpe_devclass;
1226239278Sgonzo
1227239278SgonzoDRIVER_MODULE(lpe, simplebus, lpe_driver, lpe_devclass, 0, 0);
1228239278SgonzoDRIVER_MODULE(miibus, lpe, miibus_driver, miibus_devclass, 0, 0);
1229239278SgonzoMODULE_DEPEND(lpe, obio, 1, 1, 1);
1230239278SgonzoMODULE_DEPEND(lpe, miibus, 1, 1, 1);
1231239278SgonzoMODULE_DEPEND(lpe, ether, 1, 1, 1);
1232