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: releng/10.3/sys/arm/lpc/if_lpe.c 266152 2014-05-15 16:11:06Z ian $"); 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> 52259342Sian#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) 175239278Sgonzo#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 193266152Sian if (!ofw_bus_status_okay(dev)) 194266152Sian return (ENXIO); 195266152Sian 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) { 757239278Sgonzo ifp->if_ierrors++; 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 767239278Sgonzo ifp->if_ipackets++; 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 803239278Sgonzo ifp->if_collisions += LPE_HWDESC_COLLISIONS(hws->lhs_info); 804239278Sgonzo 805239278Sgonzo if (hws->lhs_info & LPE_HWDESC_TXERRS) 806239278Sgonzo ifp->if_oerrors++; 807239278Sgonzo else 808239278Sgonzo ifp->if_opackets++; 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