if_lpe.c revision 266152
152284Sobrien/*-
250397Sobrien * Copyright (c) 2011 Jakub Wojciech Klama <jceel@FreeBSD.org>
3169689Skan * All rights reserved.
490075Sobrien *
550397Sobrien * Redistribution and use in source and binary forms, with or without
690075Sobrien * modification, are permitted provided that the following conditions
750397Sobrien * are met:
890075Sobrien * 1. Redistributions of source code must retain the above copyright
990075Sobrien *    notice, this list of conditions and the following disclaimer.
1090075Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1190075Sobrien *    notice, this list of conditions and the following disclaimer in the
1250397Sobrien *    documentation and/or other materials provided with the distribution.
1390075Sobrien *
1490075Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1590075Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1690075Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1750397Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1850397Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1990075Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20169689Skan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21169689Skan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2250397Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2350397Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2450397Sobrien * SUCH DAMAGE.
2550397Sobrien *
2650397Sobrien */
2750397Sobrien#include <sys/cdefs.h>
2852284Sobrien__FBSDID("$FreeBSD: stable/10/sys/arm/lpc/if_lpe.c 266152 2014-05-15 16:11:06Z ian $");
2952284Sobrien
3052284Sobrien#include <sys/param.h>
3150397Sobrien#include <sys/endian.h>
3250397Sobrien#include <sys/systm.h>
3350397Sobrien#include <sys/sockio.h>
3450397Sobrien#include <sys/mbuf.h>
3550397Sobrien#include <sys/malloc.h>
3650397Sobrien#include <sys/kernel.h>
3750397Sobrien#include <sys/module.h>
3850397Sobrien#include <sys/lock.h>
3950397Sobrien#include <sys/mutex.h>
4050397Sobrien#include <sys/rman.h>
4150397Sobrien#include <sys/bus.h>
4250397Sobrien#include <sys/socket.h>
4350397Sobrien#include <machine/bus.h>
4450397Sobrien#include <machine/intr.h>
4550397Sobrien
4650397Sobrien#include <net/if.h>
4750397Sobrien#include <net/if_arp.h>
4850397Sobrien#include <net/ethernet.h>
4950397Sobrien#include <net/if_dl.h>
5050397Sobrien#include <net/if_media.h>
5150397Sobrien#include <net/if_types.h>
5250397Sobrien#include <net/if_var.h>
5350397Sobrien
5450397Sobrien#include <net/bpf.h>
5550397Sobrien
5650397Sobrien#include <dev/ofw/ofw_bus.h>
5750397Sobrien#include <dev/ofw/ofw_bus_subr.h>
5850397Sobrien
5950397Sobrien#include <dev/mii/mii.h>
6050397Sobrien#include <dev/mii/miivar.h>
6150397Sobrien
6250397Sobrien#include <arm/lpc/lpcreg.h>
6350397Sobrien#include <arm/lpc/lpcvar.h>
6450397Sobrien#include <arm/lpc/if_lpereg.h>
6550397Sobrien
6650397Sobrien#include "miibus_if.h"
6750397Sobrien
6850397Sobrien#ifdef DEBUG
6950397Sobrien#define debugf(fmt, args...) do { printf("%s(): ", __func__);   \
7050397Sobrien    printf(fmt,##args); } while (0)
7150397Sobrien#else
7250397Sobrien#define debugf(fmt, args...)
7350397Sobrien#endif
7450397Sobrien
7550397Sobrienstruct lpe_dmamap_arg {
7650397Sobrien	bus_addr_t		lpe_dma_busaddr;
7750397Sobrien};
7850397Sobrien
7950397Sobrienstruct lpe_rxdesc {
8050397Sobrien	struct mbuf *		lpe_rxdesc_mbuf;
8150397Sobrien	bus_dmamap_t		lpe_rxdesc_dmamap;
8250397Sobrien};
8350397Sobrien
8450397Sobrienstruct lpe_txdesc {
8550397Sobrien	int			lpe_txdesc_first;
8650397Sobrien	struct mbuf *		lpe_txdesc_mbuf;
8750397Sobrien	bus_dmamap_t		lpe_txdesc_dmamap;
8850397Sobrien};
8950397Sobrien
9050397Sobrienstruct lpe_chain_data {
9150397Sobrien	bus_dma_tag_t		lpe_parent_tag;
9250397Sobrien	bus_dma_tag_t		lpe_tx_ring_tag;
9350397Sobrien	bus_dmamap_t		lpe_tx_ring_map;
9450397Sobrien	bus_dma_tag_t		lpe_tx_status_tag;
9550397Sobrien	bus_dmamap_t		lpe_tx_status_map;
9650397Sobrien	bus_dma_tag_t		lpe_tx_buf_tag;
9750397Sobrien	bus_dma_tag_t		lpe_rx_ring_tag;
9850397Sobrien	bus_dmamap_t		lpe_rx_ring_map;
9950397Sobrien	bus_dma_tag_t		lpe_rx_status_tag;
10050397Sobrien	bus_dmamap_t		lpe_rx_status_map;
10150397Sobrien	bus_dma_tag_t		lpe_rx_buf_tag;
10250397Sobrien	struct lpe_rxdesc	lpe_rx_desc[LPE_RXDESC_NUM];
10350397Sobrien	struct lpe_txdesc	lpe_tx_desc[LPE_TXDESC_NUM];
10450397Sobrien	int			lpe_tx_prod;
10550397Sobrien	int			lpe_tx_last;
10650397Sobrien	int			lpe_tx_used;
10750397Sobrien};
10850397Sobrien
10950397Sobrienstruct lpe_ring_data {
11050397Sobrien	struct lpe_hwdesc *	lpe_rx_ring;
11150397Sobrien	struct lpe_hwstatus *	lpe_rx_status;
11250397Sobrien	bus_addr_t		lpe_rx_ring_phys;
11350397Sobrien	bus_addr_t		lpe_rx_status_phys;
11450397Sobrien	struct lpe_hwdesc *	lpe_tx_ring;
11550397Sobrien	struct lpe_hwstatus *	lpe_tx_status;
11650397Sobrien	bus_addr_t		lpe_tx_ring_phys;
11750397Sobrien	bus_addr_t		lpe_tx_status_phys;
11850397Sobrien};
11950397Sobrien
12050397Sobrienstruct lpe_softc {
12150397Sobrien	struct ifnet *		lpe_ifp;
12250397Sobrien	struct mtx		lpe_mtx;
12350397Sobrien	phandle_t		lpe_ofw;
12450397Sobrien	device_t		lpe_dev;
12552284Sobrien	device_t		lpe_miibus;
12652284Sobrien	uint8_t			lpe_enaddr[6];
12752284Sobrien	struct resource	*	lpe_mem_res;
12852284Sobrien	struct resource *	lpe_irq_res;
12990075Sobrien	void *			lpe_intrhand;
13090075Sobrien	bus_space_tag_t		lpe_bst;
13190075Sobrien	bus_space_handle_t	lpe_bsh;
13290075Sobrien#define	LPE_FLAG_LINK		(1 << 0)
13352284Sobrien	uint32_t		lpe_flags;
13452284Sobrien	int			lpe_watchdog_timer;
13552284Sobrien	struct callout		lpe_tick;
13652284Sobrien	struct lpe_chain_data	lpe_cdata;
13752284Sobrien	struct lpe_ring_data	lpe_rdata;
13852284Sobrien};
13952284Sobrien
14052284Sobrienstatic int lpe_probe(device_t);
14152284Sobrienstatic int lpe_attach(device_t);
14250397Sobrienstatic int lpe_detach(device_t);
14350397Sobrienstatic int lpe_miibus_readreg(device_t, int, int);
14450397Sobrienstatic int lpe_miibus_writereg(device_t, int, int, int);
14550397Sobrienstatic void lpe_miibus_statchg(device_t);
14650397Sobrien
14750397Sobrienstatic void lpe_reset(struct lpe_softc *);
148132718Skanstatic void lpe_init(void *);
149132718Skanstatic void lpe_init_locked(struct lpe_softc *);
15052284Sobrienstatic void lpe_start(struct ifnet *);
15150397Sobrienstatic void lpe_start_locked(struct ifnet *);
15250397Sobrienstatic void lpe_stop(struct lpe_softc *);
153132718Skanstatic void lpe_stop_locked(struct lpe_softc *);
15490075Sobrienstatic int lpe_ioctl(struct ifnet *, u_long, caddr_t);
15550397Sobrienstatic void lpe_set_rxmode(struct lpe_softc *);
15650397Sobrienstatic void lpe_set_rxfilter(struct lpe_softc *);
15750397Sobrienstatic void lpe_intr(void *);
15850397Sobrienstatic void lpe_rxintr(struct lpe_softc *);
15950397Sobrienstatic void lpe_txintr(struct lpe_softc *);
16050397Sobrienstatic void lpe_tick(void *);
16150397Sobrienstatic void lpe_watchdog(struct lpe_softc *);
16250397Sobrienstatic int lpe_encap(struct lpe_softc *, struct mbuf **);
16390075Sobrienstatic int lpe_dma_alloc(struct lpe_softc *);
164117395Skanstatic int lpe_dma_alloc_rx(struct lpe_softc *);
16596263Sobrienstatic int lpe_dma_alloc_tx(struct lpe_softc *);
16690075Sobrienstatic int lpe_init_rx(struct lpe_softc *);
16790075Sobrienstatic int lpe_init_rxbuf(struct lpe_softc *, int);
168117395Skanstatic void lpe_discard_rxbuf(struct lpe_softc *, int);
169132718Skanstatic void lpe_dmamap_cb(void *, bus_dma_segment_t *, int, int);
17050397Sobrienstatic int lpe_ifmedia_upd(struct ifnet *);
171169689Skanstatic void lpe_ifmedia_sts(struct ifnet *, struct ifmediareq *);
172169689Skan
173169689Skan#define	lpe_lock(_sc)		mtx_lock(&(_sc)->lpe_mtx)
17450397Sobrien#define	lpe_unlock(_sc)		mtx_unlock(&(_sc)->lpe_mtx)
17550397Sobrien#define	lpe_lock_assert(sc)	mtx_assert(&(_sc)->lpe_mtx, MA_OWNED)
17650397Sobrien
17750397Sobrien#define	lpe_read_4(_sc, _reg)		\
17850397Sobrien    bus_space_read_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg))
17950397Sobrien#define	lpe_write_4(_sc, _reg, _val)	\
18050397Sobrien    bus_space_write_4((_sc)->lpe_bst, (_sc)->lpe_bsh, (_reg), (_val))
18150397Sobrien
18250397Sobrien#define	LPE_HWDESC_RXERRS	(LPE_HWDESC_CRCERROR | LPE_HWDESC_SYMBOLERROR | \
18350397Sobrien    LPE_HWDESC_LENGTHERROR | LPE_HWDESC_ALIGNERROR | LPE_HWDESC_OVERRUN | \
18450397Sobrien    LPE_HWDESC_RXNODESCR)
18550397Sobrien
18650397Sobrien#define	LPE_HWDESC_TXERRS	(LPE_HWDESC_EXCDEFER | LPE_HWDESC_EXCCOLL | \
18752284Sobrien    LPE_HWDESC_LATECOLL | LPE_HWDESC_UNDERRUN | LPE_HWDESC_TXNODESCR)
18852284Sobrien
18950397Sobrienstatic int
19052284Sobrienlpe_probe(device_t dev)
19150397Sobrien{
19250397Sobrien
19350397Sobrien	if (!ofw_bus_status_okay(dev))
19450397Sobrien		return (ENXIO);
19550397Sobrien
19650397Sobrien	if (!ofw_bus_is_compatible(dev, "lpc,ethernet"))
19750397Sobrien		return (ENXIO);
198169689Skan
199169689Skan	device_set_desc(dev, "LPC32x0 10/100 Ethernet");
20050397Sobrien	return (BUS_PROBE_DEFAULT);
20152284Sobrien}
20250397Sobrien
20350397Sobrienstatic int
20450397Sobrienlpe_attach(device_t dev)
20550397Sobrien{
20650397Sobrien	struct lpe_softc *sc = device_get_softc(dev);
20750397Sobrien	struct ifnet *ifp;
20850397Sobrien	int rid, i;
20950397Sobrien	uint32_t val;
21050397Sobrien
21150397Sobrien	sc->lpe_dev = dev;
21250397Sobrien	sc->lpe_ofw = ofw_bus_get_node(dev);
21350397Sobrien
21450397Sobrien	i = OF_getprop(sc->lpe_ofw, "local-mac-address", (void *)&sc->lpe_enaddr, 6);
21552284Sobrien	if (i != 6) {
21650397Sobrien		sc->lpe_enaddr[0] = 0x00;
21750397Sobrien		sc->lpe_enaddr[1] = 0x11;
21850397Sobrien		sc->lpe_enaddr[2] = 0x22;
21950397Sobrien		sc->lpe_enaddr[3] = 0x33;
22050397Sobrien		sc->lpe_enaddr[4] = 0x44;
22150397Sobrien		sc->lpe_enaddr[5] = 0x55;
22250397Sobrien	}
22350397Sobrien
22450397Sobrien	mtx_init(&sc->lpe_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
22550397Sobrien	    MTX_DEF);
22650397Sobrien
22750397Sobrien	callout_init_mtx(&sc->lpe_tick, &sc->lpe_mtx, 0);
22850397Sobrien
22950397Sobrien	rid = 0;
23050397Sobrien	sc->lpe_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
23150397Sobrien	    RF_ACTIVE);
23250397Sobrien	if (!sc->lpe_mem_res) {
23350397Sobrien		device_printf(dev, "cannot allocate memory window\n");
23450397Sobrien		goto fail;
23550397Sobrien	}
23650397Sobrien
23750397Sobrien	sc->lpe_bst = rman_get_bustag(sc->lpe_mem_res);
238169689Skan	sc->lpe_bsh = rman_get_bushandle(sc->lpe_mem_res);
239169689Skan
24050397Sobrien	rid = 0;
24150397Sobrien	sc->lpe_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
24250397Sobrien	    RF_ACTIVE);
24350397Sobrien	if (!sc->lpe_irq_res) {
24450397Sobrien		device_printf(dev, "cannot allocate interrupt\n");
24550397Sobrien		goto fail;
24650397Sobrien	}
24750397Sobrien
24850397Sobrien	sc->lpe_ifp = if_alloc(IFT_ETHER);
24950397Sobrien	if (!sc->lpe_ifp) {
25050397Sobrien		device_printf(dev, "cannot allocated ifnet\n");
25150397Sobrien		goto fail;
25250397Sobrien	}
25350397Sobrien
25450397Sobrien	ifp = sc->lpe_ifp;
25550397Sobrien
25650397Sobrien	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
25750397Sobrien	ifp->if_softc = sc;
25850397Sobrien	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
25950397Sobrien	ifp->if_start = lpe_start;
26050397Sobrien	ifp->if_ioctl = lpe_ioctl;
26150397Sobrien	ifp->if_init = lpe_init;
26250397Sobrien	IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN);
26350397Sobrien	ifp->if_snd.ifq_drv_maxlen = IFQ_MAXLEN;
26452284Sobrien	IFQ_SET_READY(&ifp->if_snd);
26550397Sobrien
26650397Sobrien	ether_ifattach(ifp, sc->lpe_enaddr);
26750397Sobrien
26850397Sobrien	if (bus_setup_intr(dev, sc->lpe_irq_res, INTR_TYPE_NET, NULL,
269169689Skan	    lpe_intr, sc, &sc->lpe_intrhand)) {
270169689Skan		device_printf(dev, "cannot establish interrupt handler\n");
27150397Sobrien		ether_ifdetach(ifp);
27250397Sobrien		goto fail;
27350397Sobrien	}
27452284Sobrien
27552284Sobrien	/* Enable Ethernet clock */
27652284Sobrien	lpc_pwr_write(dev, LPC_CLKPWR_MACCLK_CTRL,
27752284Sobrien	    LPC_CLKPWR_MACCLK_CTRL_REG |
27852284Sobrien	    LPC_CLKPWR_MACCLK_CTRL_SLAVE |
27952284Sobrien	    LPC_CLKPWR_MACCLK_CTRL_MASTER |
28052284Sobrien	    LPC_CLKPWR_MACCLK_CTRL_HDWINF(3));
28152284Sobrien
28250397Sobrien	/* Reset chip */
28350397Sobrien	lpe_reset(sc);
28450397Sobrien
28590075Sobrien	/* Initialize MII */
28690075Sobrien	val = lpe_read_4(sc, LPE_COMMAND);
28750397Sobrien	lpe_write_4(sc, LPE_COMMAND, val | LPE_COMMAND_RMII);
28850397Sobrien
28950397Sobrien	if (mii_attach(dev, &sc->lpe_miibus, ifp, lpe_ifmedia_upd,
29050397Sobrien	    lpe_ifmedia_sts, BMSR_DEFCAPMASK, 0x01,
29150397Sobrien	    MII_OFFSET_ANY, 0)) {
29250397Sobrien		device_printf(dev, "cannot find PHY\n");
29350397Sobrien		goto fail;
29450397Sobrien	}
29550397Sobrien
29650397Sobrien	lpe_dma_alloc(sc);
29750397Sobrien
29850397Sobrien	return (0);
29952284Sobrien
30052284Sobrienfail:
30152284Sobrien	if (sc->lpe_ifp)
30250397Sobrien		if_free(sc->lpe_ifp);
30350397Sobrien	if (sc->lpe_intrhand)
30450397Sobrien		bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
30550397Sobrien	if (sc->lpe_irq_res)
30650397Sobrien		bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
30750397Sobrien	if (sc->lpe_mem_res)
30850397Sobrien		bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
30950397Sobrien	return (ENXIO);
31050397Sobrien}
31150397Sobrien
31250397Sobrienstatic int
31350397Sobrienlpe_detach(device_t dev)
31450397Sobrien{
31550397Sobrien	struct lpe_softc *sc = device_get_softc(dev);
31650397Sobrien
31750397Sobrien	lpe_stop(sc);
31850397Sobrien
31950397Sobrien	if_free(sc->lpe_ifp);
32050397Sobrien	bus_teardown_intr(dev, sc->lpe_irq_res, sc->lpe_intrhand);
32150397Sobrien	bus_release_resource(dev, SYS_RES_IRQ, 0, sc->lpe_irq_res);
32250397Sobrien	bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->lpe_mem_res);
32350397Sobrien
324117395Skan	return (0);
32550397Sobrien}
326117395Skan
32750397Sobrienstatic int
32850397Sobrienlpe_miibus_readreg(device_t dev, int phy, int reg)
32950397Sobrien{
33050397Sobrien	struct lpe_softc *sc = device_get_softc(dev);
33150397Sobrien	uint32_t val;
33250397Sobrien	int result;
33350397Sobrien
33450397Sobrien	lpe_write_4(sc, LPE_MCMD, LPE_MCMD_READ);
33550397Sobrien	lpe_write_4(sc, LPE_MADR,
33650397Sobrien	    (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT |
33750397Sobrien	    (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT);
33850397Sobrien
33950397Sobrien	val = lpe_read_4(sc, LPE_MIND);
34090075Sobrien
34150397Sobrien	/* Wait until request is completed */
342117395Skan	while (val & LPE_MIND_BUSY) {
343117395Skan		val = lpe_read_4(sc, LPE_MIND);
344117395Skan		DELAY(10);
345117395Skan	}
346117395Skan
34790075Sobrien	if (val & LPE_MIND_INVALID)
348117395Skan		return (0);
349117395Skan
35050397Sobrien	lpe_write_4(sc, LPE_MCMD, 0);
351117395Skan	result = (lpe_read_4(sc, LPE_MRDD) & LPE_MRDD_DATAMASK);
352117395Skan	debugf("phy=%d reg=%d result=0x%04x\n", phy, reg, result);
35390075Sobrien
354117395Skan	return (result);
355117395Skan}
356117395Skan
35750397Sobrienstatic int
358117395Skanlpe_miibus_writereg(device_t dev, int phy, int reg, int data)
359117395Skan{
360117395Skan	struct lpe_softc *sc = device_get_softc(dev);
361117395Skan	uint32_t val;
362117395Skan
363117395Skan	debugf("phy=%d reg=%d data=0x%04x\n", phy, reg, data);
36450397Sobrien
36550397Sobrien	lpe_write_4(sc, LPE_MCMD, LPE_MCMD_WRITE);
36650397Sobrien	lpe_write_4(sc, LPE_MADR,
36750397Sobrien	    (reg & LPE_MADR_REGMASK) << LPE_MADR_REGSHIFT |
36850397Sobrien	    (phy & LPE_MADR_PHYMASK) << LPE_MADR_PHYSHIFT);
36950397Sobrien
37050397Sobrien	lpe_write_4(sc, LPE_MWTD, (data & LPE_MWTD_DATAMASK));
37150397Sobrien
37290075Sobrien	val = lpe_read_4(sc, LPE_MIND);
373169689Skan
374169689Skan	/* Wait until request is completed */
37590075Sobrien	while (val & LPE_MIND_BUSY) {
37650397Sobrien		val = lpe_read_4(sc, LPE_MIND);
37790075Sobrien		DELAY(10);
37850397Sobrien	}
37950397Sobrien
38050397Sobrien	return (0);
38150397Sobrien}
38250397Sobrien
38350397Sobrienstatic void
38450397Sobrienlpe_miibus_statchg(device_t dev)
38550397Sobrien{
38650397Sobrien	struct lpe_softc *sc = device_get_softc(dev);
38750397Sobrien	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
38850397Sobrien
38950397Sobrien	lpe_lock(sc);
39050397Sobrien
39190075Sobrien	if ((mii->mii_media_status & IFM_ACTIVE) &&
39250397Sobrien	    (mii->mii_media_status & IFM_AVALID))
39350397Sobrien		sc->lpe_flags |= LPE_FLAG_LINK;
39490075Sobrien	else
39550397Sobrien		sc->lpe_flags &= ~LPE_FLAG_LINK;
39650397Sobrien
39750397Sobrien	lpe_unlock(sc);
39850397Sobrien}
39950397Sobrien
40052284Sobrienstatic void
40150397Sobrienlpe_reset(struct lpe_softc *sc)
40290075Sobrien{
40390075Sobrien	uint32_t mac1;
40490075Sobrien
40590075Sobrien	/* Enter soft reset mode */
40690075Sobrien	mac1 = lpe_read_4(sc, LPE_MAC1);
40750397Sobrien	lpe_write_4(sc, LPE_MAC1, mac1 | LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
40850397Sobrien	    LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX);
40950397Sobrien
41050397Sobrien	/* Reset registers, Tx path and Rx path */
41150397Sobrien	lpe_write_4(sc, LPE_COMMAND, LPE_COMMAND_REGRESET |
41250397Sobrien	    LPE_COMMAND_TXRESET | LPE_COMMAND_RXRESET);
41390075Sobrien
41490075Sobrien	/* Set station address */
41550397Sobrien	lpe_write_4(sc, LPE_SA2, sc->lpe_enaddr[1] << 8 | sc->lpe_enaddr[0]);
41690075Sobrien	lpe_write_4(sc, LPE_SA1, sc->lpe_enaddr[3] << 8 | sc->lpe_enaddr[2]);
41790075Sobrien	lpe_write_4(sc, LPE_SA0, sc->lpe_enaddr[5] << 8 | sc->lpe_enaddr[4]);
41850397Sobrien
41950397Sobrien	/* Leave soft reset mode */
420169689Skan	mac1 = lpe_read_4(sc, LPE_MAC1);
421169689Skan	lpe_write_4(sc, LPE_MAC1, mac1 & ~(LPE_MAC1_SOFTRESET | LPE_MAC1_RESETTX |
42250397Sobrien	    LPE_MAC1_RESETMCSTX | LPE_MAC1_RESETRX | LPE_MAC1_RESETMCSRX));
42390075Sobrien}
42450397Sobrien
42590075Sobrienstatic void
42650397Sobrienlpe_init(void *arg)
42750397Sobrien{
42850397Sobrien	struct lpe_softc *sc = (struct lpe_softc *)arg;
42950397Sobrien
43090075Sobrien	lpe_lock(sc);
43150397Sobrien	lpe_init_locked(sc);
43250397Sobrien	lpe_unlock(sc);
43350397Sobrien}
43490075Sobrien
435117395Skanstatic void
43690075Sobrienlpe_init_locked(struct lpe_softc *sc)
437169689Skan{
438117395Skan	struct ifnet *ifp = sc->lpe_ifp;
43990075Sobrien	uint32_t cmd, mac1;
44090075Sobrien
441117395Skan	lpe_lock_assert(sc);
44290075Sobrien
44390075Sobrien	if (ifp->if_drv_flags & IFF_DRV_RUNNING)
44490075Sobrien		return;
44590075Sobrien
44690075Sobrien	/* Enable Tx and Rx */
44790075Sobrien	cmd = lpe_read_4(sc, LPE_COMMAND);
44890075Sobrien	lpe_write_4(sc, LPE_COMMAND, cmd | LPE_COMMAND_RXENABLE |
449132718Skan	    LPE_COMMAND_TXENABLE | LPE_COMMAND_PASSRUNTFRAME);
45090075Sobrien
45190075Sobrien	/* Enable receive */
45290075Sobrien	mac1 = lpe_read_4(sc, LPE_MAC1);
45390075Sobrien	lpe_write_4(sc, LPE_MAC1, /*mac1 |*/ LPE_MAC1_RXENABLE | LPE_MAC1_PASSALL);
45490075Sobrien
455132718Skan	lpe_write_4(sc, LPE_MAC2, LPE_MAC2_CRCENABLE | LPE_MAC2_PADCRCENABLE |
45690075Sobrien	    LPE_MAC2_FULLDUPLEX);
45790075Sobrien
45890075Sobrien	lpe_write_4(sc, LPE_MCFG, LPE_MCFG_CLKSEL(7));
459132718Skan
460132718Skan	/* Set up Rx filter */
461132718Skan	lpe_set_rxmode(sc);
46290075Sobrien
46390075Sobrien	/* Enable interrupts */
46490075Sobrien	lpe_write_4(sc, LPE_INTENABLE, LPE_INT_RXOVERRUN | LPE_INT_RXERROR |
465169689Skan	    LPE_INT_RXFINISH | LPE_INT_RXDONE | LPE_INT_TXUNDERRUN |
466169689Skan	    LPE_INT_TXERROR | LPE_INT_TXFINISH | LPE_INT_TXDONE);
467169689Skan
46850397Sobrien	sc->lpe_cdata.lpe_tx_prod = 0;
46950397Sobrien	sc->lpe_cdata.lpe_tx_last = 0;
47050397Sobrien	sc->lpe_cdata.lpe_tx_used = 0;
47190075Sobrien
47250397Sobrien	lpe_init_rx(sc);
47350397Sobrien
474169689Skan	/* Initialize Rx packet and status descriptor heads */
47550397Sobrien	lpe_write_4(sc, LPE_RXDESC, sc->lpe_rdata.lpe_rx_ring_phys);
47650397Sobrien	lpe_write_4(sc, LPE_RXSTATUS, sc->lpe_rdata.lpe_rx_status_phys);
47750397Sobrien	lpe_write_4(sc, LPE_RXDESC_NUMBER, LPE_RXDESC_NUM - 1);
47850397Sobrien	lpe_write_4(sc, LPE_RXDESC_CONS, 0);
47950397Sobrien
48090075Sobrien	/* Initialize Tx packet and status descriptor heads */
48190075Sobrien	lpe_write_4(sc, LPE_TXDESC, sc->lpe_rdata.lpe_tx_ring_phys);
48290075Sobrien	lpe_write_4(sc, LPE_TXSTATUS, sc->lpe_rdata.lpe_tx_status_phys);
483169689Skan	lpe_write_4(sc, LPE_TXDESC_NUMBER, LPE_TXDESC_NUM - 1);
48450397Sobrien	lpe_write_4(sc, LPE_TXDESC_PROD, 0);
48590075Sobrien
48690075Sobrien	ifp->if_drv_flags |= IFF_DRV_RUNNING;
487169689Skan	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
488169689Skan
489169689Skan	callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
490169689Skan}
491169689Skan
49250397Sobrienstatic void
49350397Sobrienlpe_start(struct ifnet *ifp)
49450397Sobrien{
49550397Sobrien	struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc;
49650397Sobrien
49750397Sobrien	lpe_lock(sc);
49890075Sobrien	lpe_start_locked(ifp);
49950397Sobrien	lpe_unlock(sc);
50050397Sobrien}
50150397Sobrien
50250397Sobrienstatic void
503169689Skanlpe_start_locked(struct ifnet *ifp)
504169689Skan{
505169689Skan	struct lpe_softc *sc = (struct lpe_softc *)ifp->if_softc;
506169689Skan	struct mbuf *m_head;
507169689Skan	int encap = 0;
508169689Skan
509169689Skan	lpe_lock_assert(sc);
510169689Skan
51150397Sobrien	while (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
512169689Skan		if (lpe_read_4(sc, LPE_TXDESC_PROD) ==
513169689Skan		    lpe_read_4(sc, LPE_TXDESC_CONS) - 5)
51450397Sobrien			break;
515132718Skan
516132718Skan		/* Dequeue first packet */
517132718Skan		IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
518132718Skan		if (!m_head)
519132718Skan			break;
520169689Skan
521132718Skan		lpe_encap(sc, &m_head);
522132718Skan
523132718Skan		encap++;
524132718Skan	}
525132718Skan
526169689Skan	/* Submit new descriptor list */
527132718Skan	if (encap) {
528132718Skan		lpe_write_4(sc, LPE_TXDESC_PROD, sc->lpe_cdata.lpe_tx_prod);
529132718Skan		sc->lpe_watchdog_timer = 5;
530132718Skan	}
531132718Skan
532169689Skan}
533132718Skan
534132718Skanstatic int
535132718Skanlpe_encap(struct lpe_softc *sc, struct mbuf **m_head)
536132718Skan{
537132718Skan	struct lpe_txdesc *txd;
538132718Skan	struct lpe_hwdesc *hwd;
539132718Skan	bus_dma_segment_t segs[LPE_MAXFRAGS];
540132718Skan	int i, err, nsegs, prod;
541132718Skan
542132718Skan	lpe_lock_assert(sc);
543132718Skan	M_ASSERTPKTHDR((*m_head));
544132718Skan
545132718Skan	prod = sc->lpe_cdata.lpe_tx_prod;
546132718Skan	txd = &sc->lpe_cdata.lpe_tx_desc[prod];
547132718Skan
548132718Skan	debugf("starting with prod=%d\n", prod);
549132718Skan
550132718Skan	err = bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_tx_buf_tag,
551132718Skan	    txd->lpe_txdesc_dmamap, *m_head, segs, &nsegs, BUS_DMA_NOWAIT);
552132718Skan
553132718Skan	if (err)
554132718Skan		return (err);
555132718Skan
556132718Skan	if (nsegs == 0) {
557132718Skan		m_freem(*m_head);
558132718Skan		*m_head = NULL;
559132718Skan		return (EIO);
560132718Skan	}
561132718Skan
562132718Skan        bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag, txd->lpe_txdesc_dmamap,
563132718Skan          BUS_DMASYNC_PREREAD);
564132718Skan        bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map,
565132718Skan            BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
566132718Skan
567132718Skan	txd->lpe_txdesc_first = 1;
568132718Skan	txd->lpe_txdesc_mbuf = *m_head;
569132718Skan
570132718Skan	for (i = 0; i < nsegs; i++) {
571132718Skan		hwd = &sc->lpe_rdata.lpe_tx_ring[prod];
572132718Skan		hwd->lhr_data = segs[i].ds_addr;
573132718Skan		hwd->lhr_control = segs[i].ds_len - 1;
574132718Skan
575132718Skan		if (i == nsegs - 1) {
576169689Skan			hwd->lhr_control |= LPE_HWDESC_LASTFLAG;
577169689Skan			hwd->lhr_control |= LPE_HWDESC_INTERRUPT;
578132718Skan			hwd->lhr_control |= LPE_HWDESC_CRC;
579132718Skan			hwd->lhr_control |= LPE_HWDESC_PAD;
580132718Skan		}
581132718Skan
582132718Skan		LPE_INC(prod, LPE_TXDESC_NUM);
583132718Skan	}
584132718Skan
585132718Skan	bus_dmamap_sync(sc->lpe_cdata.lpe_tx_ring_tag, sc->lpe_cdata.lpe_tx_ring_map,
586132718Skan	    BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
587132718Skan
588132718Skan	sc->lpe_cdata.lpe_tx_used += nsegs;
589132718Skan	sc->lpe_cdata.lpe_tx_prod = prod;
590132718Skan
591132718Skan	return (0);
592132718Skan}
593132718Skan
594132718Skanstatic void
595132718Skanlpe_stop(struct lpe_softc *sc)
596132718Skan{
597132718Skan	lpe_lock(sc);
598132718Skan	lpe_stop_locked(sc);
599132718Skan	lpe_unlock(sc);
600132718Skan}
601132718Skan
602132718Skanstatic void
603132718Skanlpe_stop_locked(struct lpe_softc *sc)
604132718Skan{
605132718Skan	lpe_lock_assert(sc);
606132718Skan
607132718Skan	callout_stop(&sc->lpe_tick);
608132718Skan
609132718Skan	/* Disable interrupts */
610132718Skan	lpe_write_4(sc, LPE_INTCLEAR, 0xffffffff);
611132718Skan
612132718Skan	/* Stop EMAC */
613132718Skan	lpe_write_4(sc, LPE_MAC1, 0);
614132718Skan	lpe_write_4(sc, LPE_MAC2, 0);
615132718Skan	lpe_write_4(sc, LPE_COMMAND, 0);
616132718Skan
617132718Skan	sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
618132718Skan	sc->lpe_ifp->if_drv_flags &= ~IFF_DRV_RUNNING;
619132718Skan}
620132718Skan
621132718Skanstatic int
622132718Skanlpe_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
623132718Skan{
624132718Skan	struct lpe_softc *sc = ifp->if_softc;
625132718Skan	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
626132718Skan	struct ifreq *ifr = (struct ifreq *)data;
627132718Skan	int err = 0;
628132718Skan
629132718Skan	switch (cmd) {
630132718Skan	case SIOCSIFFLAGS:
631132718Skan		lpe_lock(sc);
632132718Skan		if (ifp->if_flags & IFF_UP) {
633132718Skan			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
634132718Skan				lpe_set_rxmode(sc);
635132718Skan				lpe_set_rxfilter(sc);
636132718Skan			} else
637132718Skan				lpe_init_locked(sc);
638132718Skan		} else
639132718Skan			lpe_stop(sc);
640132718Skan		lpe_unlock(sc);
641132718Skan		break;
642132718Skan	case SIOCADDMULTI:
643169689Skan	case SIOCDELMULTI:
644132718Skan		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
645169689Skan			lpe_lock(sc);
646132718Skan			lpe_set_rxfilter(sc);
64750397Sobrien			lpe_unlock(sc);
648132718Skan		}
64950397Sobrien		break;
650169689Skan	case SIOCGIFMEDIA:
651169689Skan	case SIOCSIFMEDIA:
65250397Sobrien		err = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd);
653169689Skan		break;
654169689Skan	default:
65550397Sobrien		err = ether_ioctl(ifp, cmd, data);
65650397Sobrien		break;
65750397Sobrien	}
65850397Sobrien
65950397Sobrien	return (err);
66050397Sobrien}
66150397Sobrien
66250397Sobrienstatic void lpe_set_rxmode(struct lpe_softc *sc)
66350397Sobrien{
66452284Sobrien	struct ifnet *ifp = sc->lpe_ifp;
66552284Sobrien	uint32_t rxfilt;
66650397Sobrien
66752284Sobrien	rxfilt = LPE_RXFILTER_UNIHASH | LPE_RXFILTER_MULTIHASH | LPE_RXFILTER_PERFECT;
668117395Skan
66952284Sobrien	if (ifp->if_flags & IFF_BROADCAST)
67052284Sobrien		rxfilt |= LPE_RXFILTER_BROADCAST;
67152284Sobrien
67252284Sobrien	if (ifp->if_flags & IFF_PROMISC)
67352284Sobrien		rxfilt |= LPE_RXFILTER_UNICAST | LPE_RXFILTER_MULTICAST;
67450397Sobrien
67550397Sobrien	if (ifp->if_flags & IFF_ALLMULTI)
676169689Skan		rxfilt |= LPE_RXFILTER_MULTICAST;
677169689Skan
67890075Sobrien	lpe_write_4(sc, LPE_RXFILTER_CTRL, rxfilt);
679132718Skan}
680169689Skan
681169689Skanstatic void lpe_set_rxfilter(struct lpe_softc *sc)
68290075Sobrien{
683169689Skan	struct ifnet *ifp = sc->lpe_ifp;
68450397Sobrien	struct ifmultiaddr *ifma;
68590075Sobrien	int index;
68650397Sobrien	uint32_t hashl, hashh;
68790075Sobrien
68890075Sobrien	hashl = 0;
68990075Sobrien	hashh = 0;
69090075Sobrien
69190075Sobrien	if_maddr_rlock(ifp);
69290075Sobrien	TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
69350397Sobrien		if (ifma->ifma_addr->sa_family != AF_LINK)
69490075Sobrien			continue;
69590075Sobrien
69652284Sobrien		index = ether_crc32_be(LLADDR((struct sockaddr_dl *)
69752284Sobrien		    ifma->ifma_addr), ETHER_ADDR_LEN) >> 23 & 0x3f;
69850397Sobrien
699169689Skan		if (index > 31)
70050397Sobrien			hashh |= (1 << (index - 32));
70150397Sobrien		else
70250397Sobrien			hashl |= (1 << index);
70350397Sobrien	}
70450397Sobrien	if_maddr_runlock(ifp);
70550397Sobrien
70690075Sobrien	/* Program new hash filter */
70750397Sobrien	lpe_write_4(sc, LPE_HASHFILTER_L, hashl);
70850397Sobrien	lpe_write_4(sc, LPE_HASHFILTER_H, hashh);
709169689Skan}
710169689Skan
71150397Sobrienstatic void
71250397Sobrienlpe_intr(void *arg)
71350397Sobrien{
71450397Sobrien	struct lpe_softc *sc = (struct lpe_softc *)arg;
71550397Sobrien	uint32_t intstatus;
71650397Sobrien
71750397Sobrien	debugf("status=0x%08x\n", lpe_read_4(sc, LPE_INTSTATUS));
71850397Sobrien
719169689Skan	lpe_lock(sc);
72050397Sobrien
72152284Sobrien	while ((intstatus = lpe_read_4(sc, LPE_INTSTATUS))) {
72252284Sobrien		if (intstatus & LPE_INT_RXDONE)
723169689Skan			lpe_rxintr(sc);
724169689Skan
725169689Skan		if (intstatus & LPE_INT_TXDONE)
72650397Sobrien			lpe_txintr(sc);
72750397Sobrien
728169689Skan		lpe_write_4(sc, LPE_INTCLEAR, 0xffff);
72950397Sobrien	}
730117395Skan
731169689Skan	lpe_unlock(sc);
73290075Sobrien}
73390075Sobrien
73490075Sobrienstatic void
73590075Sobrienlpe_rxintr(struct lpe_softc *sc)
73690075Sobrien{
73790075Sobrien	struct ifnet *ifp = sc->lpe_ifp;
73890075Sobrien	struct lpe_hwdesc *hwd;
739132718Skan	struct lpe_hwstatus *hws;
740132718Skan	struct lpe_rxdesc *rxd;
74190075Sobrien	struct mbuf *m;
74290075Sobrien	int prod, cons;
74390075Sobrien
744169689Skan	for (;;) {
74590075Sobrien		prod = lpe_read_4(sc, LPE_RXDESC_PROD);
746169689Skan		cons = lpe_read_4(sc, LPE_RXDESC_CONS);
74790075Sobrien
74850397Sobrien		if (prod == cons)
74950397Sobrien			break;
75050397Sobrien
75150397Sobrien		rxd = &sc->lpe_cdata.lpe_rx_desc[cons];
75290075Sobrien		hwd = &sc->lpe_rdata.lpe_rx_ring[cons];
75390075Sobrien		hws = &sc->lpe_rdata.lpe_rx_status[cons];
75490075Sobrien
75590075Sobrien		/* Check received frame for errors */
75650397Sobrien		if (hws->lhs_info & LPE_HWDESC_RXERRS) {
75750397Sobrien			ifp->if_ierrors++;
758132718Skan			lpe_discard_rxbuf(sc, cons);
75990075Sobrien			lpe_init_rxbuf(sc, cons);
76090075Sobrien			goto skip;
761169689Skan		}
76290075Sobrien
763117395Skan		m = rxd->lpe_rxdesc_mbuf;
764169689Skan		m->m_pkthdr.rcvif = ifp;
76590075Sobrien		m->m_data += 2;
766169689Skan
76790075Sobrien		ifp->if_ipackets++;
76890075Sobrien
76990075Sobrien		lpe_unlock(sc);
77090075Sobrien		(*ifp->if_input)(ifp, m);
77190075Sobrien		lpe_lock(sc);
772169689Skan
773117395Skan		lpe_init_rxbuf(sc, cons);
77490075Sobrienskip:
775169689Skan		LPE_INC(cons, LPE_RXDESC_NUM);
77650397Sobrien		lpe_write_4(sc, LPE_RXDESC_CONS, cons);
777169689Skan	}
778169689Skan}
77950397Sobrien
78090075Sobrienstatic void
78150397Sobrienlpe_txintr(struct lpe_softc *sc)
78250397Sobrien{
78350397Sobrien	struct ifnet *ifp = sc->lpe_ifp;
78450397Sobrien	struct lpe_hwdesc *hwd;
78552284Sobrien	struct lpe_hwstatus *hws;
78652284Sobrien	struct lpe_txdesc *txd;
78750397Sobrien	int cons, last;
78852284Sobrien
789169689Skan	for (;;) {
79052284Sobrien		cons = lpe_read_4(sc, LPE_TXDESC_CONS);
791169689Skan		last = sc->lpe_cdata.lpe_tx_last;
792169689Skan
793169689Skan		if (cons == last)
79452284Sobrien			break;
79552284Sobrien
796169689Skan		txd = &sc->lpe_cdata.lpe_tx_desc[last];
79750397Sobrien		hwd = &sc->lpe_rdata.lpe_tx_ring[last];
798169689Skan		hws = &sc->lpe_rdata.lpe_tx_status[last];
799132718Skan
800169689Skan		bus_dmamap_sync(sc->lpe_cdata.lpe_tx_buf_tag,
80150397Sobrien		    txd->lpe_txdesc_dmamap, BUS_DMASYNC_POSTWRITE);
80250397Sobrien
80350397Sobrien		ifp->if_collisions += LPE_HWDESC_COLLISIONS(hws->lhs_info);
80490075Sobrien
80550397Sobrien		if (hws->lhs_info & LPE_HWDESC_TXERRS)
806169689Skan			ifp->if_oerrors++;
80790075Sobrien		else
80890075Sobrien			ifp->if_opackets++;
80990075Sobrien
81090075Sobrien		if (txd->lpe_txdesc_first) {
811132718Skan			bus_dmamap_unload(sc->lpe_cdata.lpe_tx_buf_tag,
812169689Skan			    txd->lpe_txdesc_dmamap);
813169689Skan
814169689Skan			m_freem(txd->lpe_txdesc_mbuf);
815169689Skan			txd->lpe_txdesc_mbuf = NULL;
816169689Skan			txd->lpe_txdesc_first = 0;
817132718Skan		}
81890075Sobrien
81952284Sobrien		sc->lpe_cdata.lpe_tx_used--;
82050397Sobrien		LPE_INC(sc->lpe_cdata.lpe_tx_last, LPE_TXDESC_NUM);
82150397Sobrien	}
82250397Sobrien
82350397Sobrien	if (!sc->lpe_cdata.lpe_tx_used)
824132718Skan		sc->lpe_watchdog_timer = 0;
825132718Skan}
826132718Skan
827132718Skanstatic void
828132718Skanlpe_tick(void *arg)
82950397Sobrien{
83050397Sobrien	struct lpe_softc *sc = (struct lpe_softc *)arg;
83150397Sobrien	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
832132718Skan
83350397Sobrien	lpe_lock_assert(sc);
83450397Sobrien
83550397Sobrien	mii_tick(mii);
83690075Sobrien	lpe_watchdog(sc);
83750397Sobrien
838132718Skan	callout_reset(&sc->lpe_tick, hz, lpe_tick, sc);
83950397Sobrien}
84050397Sobrien
84150397Sobrienstatic void
84290075Sobrienlpe_watchdog(struct lpe_softc *sc)
84390075Sobrien{
84450397Sobrien	struct ifnet *ifp = sc->lpe_ifp;
845132718Skan
84650397Sobrien	lpe_lock_assert(sc);
84790075Sobrien
84890075Sobrien	if (sc->lpe_watchdog_timer == 0 || sc->lpe_watchdog_timer--)
84990075Sobrien		return;
850132718Skan
85150397Sobrien	/* Chip has stopped responding */
85290075Sobrien	device_printf(sc->lpe_dev, "WARNING: chip hangup, restarting...\n");
85390075Sobrien	lpe_stop_locked(sc);
854132718Skan	lpe_init_locked(sc);
85590075Sobrien
85650397Sobrien	/* Try to resend packets */
85750397Sobrien	if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd))
858132718Skan		lpe_start_locked(ifp);
859132718Skan}
860132718Skan
861132718Skanstatic int
862132718Skanlpe_dma_alloc(struct lpe_softc *sc)
863132718Skan{
864132718Skan	int err;
865132718Skan
866132718Skan	/* Create parent DMA tag */
867132718Skan	err = bus_dma_tag_create(
868132718Skan	    bus_get_dma_tag(sc->lpe_dev),
869132718Skan	    1, 0,			/* alignment, boundary */
870132718Skan	    BUS_SPACE_MAXADDR_32BIT,	/* lowaddr */
871132718Skan	    BUS_SPACE_MAXADDR,		/* highaddr */
872132718Skan	    NULL, NULL,			/* filter, filterarg */
873132718Skan	    BUS_SPACE_MAXSIZE_32BIT, 0,	/* maxsize, nsegments */
87450397Sobrien	    BUS_SPACE_MAXSIZE_32BIT, 0,	/* maxsegsize, flags */
87550397Sobrien	    NULL, NULL,			/* lockfunc, lockarg */
87650397Sobrien	    &sc->lpe_cdata.lpe_parent_tag);
877132718Skan
878132718Skan	if (err) {
87950397Sobrien		device_printf(sc->lpe_dev, "cannot create parent DMA tag\n");
88050397Sobrien		return (err);
88150397Sobrien	}
88250397Sobrien
88350397Sobrien	err = lpe_dma_alloc_rx(sc);
884132718Skan	if (err)
885132718Skan		return (err);
886132718Skan
887132718Skan	err = lpe_dma_alloc_tx(sc);
888132718Skan	if (err)
889132718Skan		return (err);
890132718Skan
891132718Skan	return (0);
892132718Skan}
89350397Sobrien
89450397Sobrienstatic int
89550397Sobrienlpe_dma_alloc_rx(struct lpe_softc *sc)
89650397Sobrien{
897132718Skan	struct lpe_rxdesc *rxd;
898132718Skan	struct lpe_dmamap_arg ctx;
89950397Sobrien	int err, i;
90050397Sobrien
90150397Sobrien	/* Create tag for Rx ring */
90250397Sobrien	err = bus_dma_tag_create(
903117395Skan	    sc->lpe_cdata.lpe_parent_tag,
90450397Sobrien	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
905132718Skan	    BUS_SPACE_MAXADDR,		/* lowaddr */
906132718Skan	    BUS_SPACE_MAXADDR,		/* highaddr */
90750397Sobrien	    NULL, NULL,			/* filter, filterarg */
908117395Skan	    LPE_RXDESC_SIZE, 1,		/* maxsize, nsegments */
909132718Skan	    LPE_RXDESC_SIZE, 0,		/* maxsegsize, flags */
91050397Sobrien	    NULL, NULL,			/* lockfunc, lockarg */
91150397Sobrien	    &sc->lpe_cdata.lpe_rx_ring_tag);
91250397Sobrien
91350397Sobrien	if (err) {
91450397Sobrien		device_printf(sc->lpe_dev, "cannot create Rx ring DMA tag\n");
91550397Sobrien		goto fail;
91650397Sobrien	}
91750397Sobrien
918169689Skan	/* Create tag for Rx status ring */
91950397Sobrien	err = bus_dma_tag_create(
920132718Skan	    sc->lpe_cdata.lpe_parent_tag,
921169689Skan	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
92250397Sobrien	    BUS_SPACE_MAXADDR,		/* lowaddr */
92350397Sobrien	    BUS_SPACE_MAXADDR,		/* highaddr */
92450397Sobrien	    NULL, NULL,			/* filter, filterarg */
92550397Sobrien	    LPE_RXSTATUS_SIZE, 1,	/* maxsize, nsegments */
926169689Skan	    LPE_RXSTATUS_SIZE, 0,	/* maxsegsize, flags */
927169689Skan	    NULL, NULL,			/* lockfunc, lockarg */
928169689Skan	    &sc->lpe_cdata.lpe_rx_status_tag);
92950397Sobrien
93050397Sobrien	if (err) {
931132718Skan		device_printf(sc->lpe_dev, "cannot create Rx status ring DMA tag\n");
932169689Skan		goto fail;
933169689Skan	}
934169689Skan
935169689Skan	/* Create tag for Rx buffers */
936169689Skan	err = bus_dma_tag_create(
937169689Skan	    sc->lpe_cdata.lpe_parent_tag,
938169689Skan	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
939169689Skan	    BUS_SPACE_MAXADDR,		/* lowaddr */
940169689Skan	    BUS_SPACE_MAXADDR,		/* highaddr */
94150397Sobrien	    NULL, NULL,			/* filter, filterarg */
94250397Sobrien	    MCLBYTES * LPE_RXDESC_NUM,	/* maxsize */
94350397Sobrien	    LPE_RXDESC_NUM,		/* segments */
94450397Sobrien	    MCLBYTES, 0,		/* maxsegsize, flags */
945132718Skan	    NULL, NULL,			/* lockfunc, lockarg */
946169689Skan	    &sc->lpe_cdata.lpe_rx_buf_tag);
947169689Skan
948169689Skan	if (err) {
949169689Skan		device_printf(sc->lpe_dev, "cannot create Rx buffers DMA tag\n");
950169689Skan		goto fail;
95150397Sobrien	}
95250397Sobrien
953169689Skan	/* Allocate Rx DMA ring */
95450397Sobrien	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_ring_tag,
95550397Sobrien	    (void **)&sc->lpe_rdata.lpe_rx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
956132718Skan	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_ring_map);
95790075Sobrien
95890075Sobrien	err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_ring_tag,
959132718Skan	    sc->lpe_cdata.lpe_rx_ring_map, sc->lpe_rdata.lpe_rx_ring,
960132718Skan	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
961169689Skan
962169689Skan	sc->lpe_rdata.lpe_rx_ring_phys = ctx.lpe_dma_busaddr;
96350397Sobrien
96450397Sobrien	/* Allocate Rx status ring */
96550397Sobrien	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_rx_status_tag,
96650397Sobrien	    (void **)&sc->lpe_rdata.lpe_rx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
96750397Sobrien	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_rx_status_map);
968132718Skan
96950397Sobrien	err = bus_dmamap_load(sc->lpe_cdata.lpe_rx_status_tag,
97050397Sobrien	    sc->lpe_cdata.lpe_rx_status_map, sc->lpe_rdata.lpe_rx_status,
97150397Sobrien	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
97250397Sobrien
973169689Skan	sc->lpe_rdata.lpe_rx_status_phys = ctx.lpe_dma_busaddr;
97450397Sobrien
97590075Sobrien
97690075Sobrien	/* Create Rx buffers DMA map */
977169689Skan	for (i = 0; i < LPE_RXDESC_NUM; i++) {
978169689Skan		rxd = &sc->lpe_cdata.lpe_rx_desc[i];
97950397Sobrien		rxd->lpe_rxdesc_mbuf = NULL;
98052284Sobrien		rxd->lpe_rxdesc_dmamap = NULL;
98152284Sobrien
98252284Sobrien		err = bus_dmamap_create(sc->lpe_cdata.lpe_rx_buf_tag, 0,
98390075Sobrien		    &rxd->lpe_rxdesc_dmamap);
98490075Sobrien
98590075Sobrien		if (err) {
98652284Sobrien			device_printf(sc->lpe_dev, "cannot create Rx DMA map\n");
98752284Sobrien			return (err);
98852284Sobrien		}
98952284Sobrien	}
99052284Sobrien
99152284Sobrien	return (0);
99252284Sobrienfail:
99352284Sobrien	return (err);
99452284Sobrien}
99552284Sobrien
99652284Sobrienstatic int
99790075Sobrienlpe_dma_alloc_tx(struct lpe_softc *sc)
99890075Sobrien{
99952284Sobrien	struct lpe_txdesc *txd;
100090075Sobrien	struct lpe_dmamap_arg ctx;
100190075Sobrien	int err, i;
100290075Sobrien
100352284Sobrien	/* Create tag for Tx ring */
1004117395Skan	err = bus_dma_tag_create(
1005117395Skan	    sc->lpe_cdata.lpe_parent_tag,
100690075Sobrien	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
1007117395Skan	    BUS_SPACE_MAXADDR,		/* lowaddr */
100852284Sobrien	    BUS_SPACE_MAXADDR,		/* highaddr */
1009169689Skan	    NULL, NULL,			/* filter, filterarg */
1010169689Skan	    LPE_TXDESC_SIZE, 1,		/* maxsize, nsegments */
101150397Sobrien	    LPE_TXDESC_SIZE, 0,		/* maxsegsize, flags */
1012117395Skan	    NULL, NULL,			/* lockfunc, lockarg */
1013117395Skan	    &sc->lpe_cdata.lpe_tx_ring_tag);
101452284Sobrien
101552284Sobrien	if (err) {
101652284Sobrien		device_printf(sc->lpe_dev, "cannot create Tx ring DMA tag\n");
1017117395Skan		goto fail;
1018117395Skan	}
101952284Sobrien
1020117395Skan	/* Create tag for Tx status ring */
102152284Sobrien	err = bus_dma_tag_create(
102290075Sobrien	    sc->lpe_cdata.lpe_parent_tag,
102352284Sobrien	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
1024117395Skan	    BUS_SPACE_MAXADDR,		/* lowaddr */
102552284Sobrien	    BUS_SPACE_MAXADDR,		/* highaddr */
1026117395Skan	    NULL, NULL,			/* filter, filterarg */
102750397Sobrien	    LPE_TXSTATUS_SIZE, 1,	/* maxsize, nsegments */
1028117395Skan	    LPE_TXSTATUS_SIZE, 0,	/* maxsegsize, flags */
102950397Sobrien	    NULL, NULL,			/* lockfunc, lockarg */
103052284Sobrien	    &sc->lpe_cdata.lpe_tx_status_tag);
103152284Sobrien
1032117395Skan	if (err) {
103352284Sobrien		device_printf(sc->lpe_dev, "cannot create Tx status ring DMA tag\n");
103490075Sobrien		goto fail;
103552284Sobrien	}
103652284Sobrien
103752284Sobrien	/* Create tag for Tx buffers */
103852284Sobrien	err = bus_dma_tag_create(
103952284Sobrien	    sc->lpe_cdata.lpe_parent_tag,
104052284Sobrien	    LPE_DESC_ALIGN, 0,		/* alignment, boundary */
1041117395Skan	    BUS_SPACE_MAXADDR,		/* lowaddr */
104252284Sobrien	    BUS_SPACE_MAXADDR,		/* highaddr */
104352284Sobrien	    NULL, NULL,			/* filter, filterarg */
1044117395Skan	    MCLBYTES * LPE_TXDESC_NUM,	/* maxsize */
104552284Sobrien	    LPE_TXDESC_NUM,		/* segments */
104690075Sobrien	    MCLBYTES, 0,		/* maxsegsize, flags */
104790075Sobrien	    NULL, NULL,			/* lockfunc, lockarg */
104890075Sobrien	    &sc->lpe_cdata.lpe_tx_buf_tag);
104952284Sobrien
105090075Sobrien	if (err) {
105190075Sobrien		device_printf(sc->lpe_dev, "cannot create Tx buffers DMA tag\n");
105290075Sobrien		goto fail;
105390075Sobrien	}
105452284Sobrien
105552284Sobrien	/* Allocate Tx DMA ring */
1056117395Skan	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_ring_tag,
105752284Sobrien	    (void **)&sc->lpe_rdata.lpe_tx_ring, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
105890075Sobrien	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_ring_map);
105990075Sobrien
106090075Sobrien	err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_ring_tag,
106152284Sobrien	    sc->lpe_cdata.lpe_tx_ring_map, sc->lpe_rdata.lpe_tx_ring,
106290075Sobrien	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
106390075Sobrien
106490075Sobrien	sc->lpe_rdata.lpe_tx_ring_phys = ctx.lpe_dma_busaddr;
106590075Sobrien
106652284Sobrien	/* Allocate Tx status ring */
106752284Sobrien	err = bus_dmamem_alloc(sc->lpe_cdata.lpe_tx_status_tag,
106852284Sobrien	    (void **)&sc->lpe_rdata.lpe_tx_status, BUS_DMA_WAITOK | BUS_DMA_COHERENT |
106952284Sobrien	    BUS_DMA_ZERO, &sc->lpe_cdata.lpe_tx_status_map);
107052284Sobrien
107150397Sobrien	err = bus_dmamap_load(sc->lpe_cdata.lpe_tx_status_tag,
107250397Sobrien	    sc->lpe_cdata.lpe_tx_status_map, sc->lpe_rdata.lpe_tx_status,
107350397Sobrien	    LPE_RXDESC_SIZE, lpe_dmamap_cb, &ctx, 0);
107450397Sobrien
107550397Sobrien	sc->lpe_rdata.lpe_tx_status_phys = ctx.lpe_dma_busaddr;
107650397Sobrien
107750397Sobrien
107850397Sobrien	/* Create Tx buffers DMA map */
107950397Sobrien	for (i = 0; i < LPE_TXDESC_NUM; i++) {
108050397Sobrien		txd = &sc->lpe_cdata.lpe_tx_desc[i];
108150397Sobrien		txd->lpe_txdesc_mbuf = NULL;
1082132718Skan		txd->lpe_txdesc_dmamap = NULL;
108350397Sobrien		txd->lpe_txdesc_first = 0;
108450397Sobrien
1085132718Skan		err = bus_dmamap_create(sc->lpe_cdata.lpe_tx_buf_tag, 0,
108650397Sobrien		    &txd->lpe_txdesc_dmamap);
108750397Sobrien
108850397Sobrien		if (err) {
108950397Sobrien			device_printf(sc->lpe_dev, "cannot create Tx DMA map\n");
109050397Sobrien			return (err);
1091132718Skan		}
109250397Sobrien	}
109350397Sobrien
109490075Sobrien	return (0);
109550397Sobrienfail:
109650397Sobrien	return (err);
109750397Sobrien}
109850397Sobrien
109950397Sobrienstatic int
1100132718Skanlpe_init_rx(struct lpe_softc *sc)
110150397Sobrien{
110290075Sobrien	int i, err;
110390075Sobrien
110450397Sobrien	for (i = 0; i < LPE_RXDESC_NUM; i++) {
110550397Sobrien		err = lpe_init_rxbuf(sc, i);
110650397Sobrien		if (err)
110750397Sobrien			return (err);
110850397Sobrien	}
110990075Sobrien
1110132718Skan	return (0);
1111132718Skan}
1112132718Skan
111390075Sobrienstatic int
111450397Sobrienlpe_init_rxbuf(struct lpe_softc *sc, int n)
111550397Sobrien{
111650397Sobrien	struct lpe_rxdesc *rxd;
1117132718Skan	struct lpe_hwdesc *hwd;
111850397Sobrien	struct lpe_hwstatus *hws;
1119169689Skan	struct mbuf *m;
112090075Sobrien	bus_dma_segment_t segs[1];
112190075Sobrien	int nsegs;
112250397Sobrien
112350397Sobrien	rxd = &sc->lpe_cdata.lpe_rx_desc[n];
112490075Sobrien	hwd = &sc->lpe_rdata.lpe_rx_ring[n];
112590075Sobrien	hws = &sc->lpe_rdata.lpe_rx_status[n];
112690075Sobrien	m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
112750397Sobrien
112850397Sobrien	if (!m) {
1129132718Skan		device_printf(sc->lpe_dev, "WARNING: mbufs exhausted!\n");
113050397Sobrien		return (ENOBUFS);
113190075Sobrien	}
113250397Sobrien
1133169689Skan	m->m_len = m->m_pkthdr.len = MCLBYTES;
113490075Sobrien
113550397Sobrien	bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
113650397Sobrien
113750397Sobrien	if (bus_dmamap_load_mbuf_sg(sc->lpe_cdata.lpe_rx_buf_tag,
113850397Sobrien	    rxd->lpe_rxdesc_dmamap, m, segs, &nsegs, 0)) {
113990075Sobrien		m_freem(m);
1140132718Skan		return (ENOBUFS);
114150397Sobrien	}
114250397Sobrien
1143169689Skan	bus_dmamap_sync(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap,
114450397Sobrien	    BUS_DMASYNC_PREREAD);
1145169689Skan
114690075Sobrien	rxd->lpe_rxdesc_mbuf = m;
114750397Sobrien	hwd->lhr_data = segs[0].ds_addr + 2;
1148169689Skan	hwd->lhr_control = (segs[0].ds_len - 1) | LPE_HWDESC_INTERRUPT;
1149169689Skan
1150169689Skan	return (0);
1151169689Skan}
115250397Sobrien
115350397Sobrienstatic void
115450397Sobrienlpe_discard_rxbuf(struct lpe_softc *sc, int n)
115550397Sobrien{
115690075Sobrien	struct lpe_rxdesc *rxd;
115790075Sobrien	struct lpe_hwdesc *hwd;
1158117395Skan
115990075Sobrien	rxd = &sc->lpe_cdata.lpe_rx_desc[n];
116090075Sobrien	hwd = &sc->lpe_rdata.lpe_rx_ring[n];
116190075Sobrien
116250397Sobrien	bus_dmamap_unload(sc->lpe_cdata.lpe_rx_buf_tag, rxd->lpe_rxdesc_dmamap);
116390075Sobrien
1164117395Skan	hwd->lhr_data = 0;
116550397Sobrien	hwd->lhr_control = 0;
116650397Sobrien
116790075Sobrien	if (rxd->lpe_rxdesc_mbuf) {
116890075Sobrien		m_freem(rxd->lpe_rxdesc_mbuf);
116990075Sobrien		rxd->lpe_rxdesc_mbuf = NULL;
117050397Sobrien	}
1171132718Skan}
117250397Sobrien
1173169689Skanstatic void
1174169689Skanlpe_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
1175169689Skan{
1176169689Skan	struct lpe_dmamap_arg *ctx;
1177169689Skan
1178169689Skan	if (error)
1179169689Skan		return;
118050397Sobrien
118190075Sobrien	ctx = (struct lpe_dmamap_arg *)arg;
118250397Sobrien	ctx->lpe_dma_busaddr = segs[0].ds_addr;
118350397Sobrien}
118450397Sobrien
118550397Sobrienstatic int
118650397Sobrienlpe_ifmedia_upd(struct ifnet *ifp)
118796263Sobrien{
118850397Sobrien	return (0);
118950397Sobrien}
119050397Sobrien
119150397Sobrienstatic void
1192169689Skanlpe_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
119350397Sobrien{
1194169689Skan	struct lpe_softc *sc = ifp->if_softc;
119550397Sobrien	struct mii_data *mii = device_get_softc(sc->lpe_miibus);
1196169689Skan
1197169689Skan	lpe_lock(sc);
1198169689Skan	mii_pollstat(mii);
1199169689Skan	ifmr->ifm_active = mii->mii_media_active;
1200169689Skan	ifmr->ifm_status = mii->mii_media_status;
1201169689Skan	lpe_unlock(sc);
1202169689Skan}
1203169689Skan
1204169689Skanstatic device_method_t lpe_methods[] = {
1205169689Skan	/* Device interface */
1206169689Skan	DEVMETHOD(device_probe,		lpe_probe),
1207169689Skan	DEVMETHOD(device_attach,	lpe_attach),
120890075Sobrien	DEVMETHOD(device_detach,	lpe_detach),
120990075Sobrien
121090075Sobrien	/* Bus interface */
121190075Sobrien	DEVMETHOD(bus_print_child,	bus_generic_print_child),
121290075Sobrien
121390075Sobrien	/* MII interface */
121490075Sobrien	DEVMETHOD(miibus_readreg,	lpe_miibus_readreg),
121590075Sobrien	DEVMETHOD(miibus_writereg,	lpe_miibus_writereg),
121690075Sobrien	DEVMETHOD(miibus_statchg,	lpe_miibus_statchg),
121790075Sobrien	{ 0, 0 }
121890075Sobrien};
121990075Sobrien
122090075Sobrienstatic driver_t lpe_driver = {
122190075Sobrien	"lpe",
122290075Sobrien	lpe_methods,
122390075Sobrien	sizeof(struct lpe_softc),
122490075Sobrien};
122590075Sobrien
122690075Sobrienstatic devclass_t lpe_devclass;
122790075Sobrien
122890075SobrienDRIVER_MODULE(lpe, simplebus, lpe_driver, lpe_devclass, 0, 0);
122990075SobrienDRIVER_MODULE(miibus, lpe, miibus_driver, miibus_devclass, 0, 0);
123090075SobrienMODULE_DEPEND(lpe, obio, 1, 1, 1);
123190075SobrienMODULE_DEPEND(lpe, miibus, 1, 1, 1);
123250397SobrienMODULE_DEPEND(lpe, ether, 1, 1, 1);
123350397Sobrien