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