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