1164426Ssam/*- 2177505Ssam * Copyright (c) 2006-2008 Sam Leffler. All rights reserved. 3164426Ssam * 4164426Ssam * Redistribution and use in source and binary forms, with or without 5164426Ssam * modification, are permitted provided that the following conditions 6164426Ssam * are met: 7164426Ssam * 1. Redistributions of source code must retain the above copyright 8164426Ssam * notice, this list of conditions and the following disclaimer. 9164426Ssam * 2. Redistributions in binary form must reproduce the above copyright 10164426Ssam * notice, this list of conditions and the following disclaimer in the 11164426Ssam * documentation and/or other materials provided with the distribution. 12164426Ssam * 13164426Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 14164426Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 15164426Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 16164426Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 17164426Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 18164426Ssam * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 19164426Ssam * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 20164426Ssam * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21164426Ssam * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 22164426Ssam * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23164426Ssam */ 24164426Ssam 25164426Ssam#include <sys/cdefs.h> 26164426Ssam__FBSDID("$FreeBSD$"); 27164426Ssam 28164426Ssam/* 29164426Ssam * Intel XScale NPE Ethernet driver. 30164426Ssam * 31164426Ssam * This driver handles the two ports present on the IXP425. 32164426Ssam * Packet processing is done by the Network Processing Engines 33164426Ssam * (NPE's) that work together with a MAC and PHY. The MAC 34164426Ssam * is also mapped to the XScale cpu; the PHY is accessed via 35164426Ssam * the MAC. NPE-XScale communication happens through h/w 36164426Ssam * queues managed by the Q Manager block. 37164426Ssam * 38164426Ssam * The code here replaces the ethAcc, ethMii, and ethDB classes 39164426Ssam * in the Intel Access Library (IAL) and the OS-specific driver. 40164426Ssam * 41164426Ssam * XXX add vlan support 42164426Ssam */ 43164426Ssam#ifdef HAVE_KERNEL_OPTION_HEADERS 44164426Ssam#include "opt_device_polling.h" 45164426Ssam#endif 46164426Ssam 47164426Ssam#include <sys/param.h> 48164426Ssam#include <sys/systm.h> 49164426Ssam#include <sys/bus.h> 50164426Ssam#include <sys/kernel.h> 51164426Ssam#include <sys/mbuf.h> 52164426Ssam#include <sys/malloc.h> 53164426Ssam#include <sys/module.h> 54164426Ssam#include <sys/rman.h> 55164426Ssam#include <sys/socket.h> 56164426Ssam#include <sys/sockio.h> 57164426Ssam#include <sys/sysctl.h> 58164426Ssam#include <sys/endian.h> 59164426Ssam#include <machine/bus.h> 60164426Ssam 61164426Ssam#include <net/ethernet.h> 62164426Ssam#include <net/if.h> 63164426Ssam#include <net/if_arp.h> 64164426Ssam#include <net/if_dl.h> 65164426Ssam#include <net/if_media.h> 66164426Ssam#include <net/if_mib.h> 67164426Ssam#include <net/if_types.h> 68259342Sian#include <net/if_var.h> 69164426Ssam 70164426Ssam#ifdef INET 71164426Ssam#include <netinet/in.h> 72164426Ssam#include <netinet/in_systm.h> 73164426Ssam#include <netinet/in_var.h> 74164426Ssam#include <netinet/ip.h> 75164426Ssam#endif 76164426Ssam 77164426Ssam#include <net/bpf.h> 78164426Ssam#include <net/bpfdesc.h> 79164426Ssam 80164426Ssam#include <arm/xscale/ixp425/ixp425reg.h> 81164426Ssam#include <arm/xscale/ixp425/ixp425var.h> 82164426Ssam#include <arm/xscale/ixp425/ixp425_qmgr.h> 83164426Ssam#include <arm/xscale/ixp425/ixp425_npevar.h> 84164426Ssam 85164426Ssam#include <dev/mii/mii.h> 86164426Ssam#include <dev/mii/miivar.h> 87164426Ssam#include <arm/xscale/ixp425/if_npereg.h> 88164426Ssam 89186352Ssam#include <machine/armreg.h> 90186352Ssam 91164426Ssam#include "miibus_if.h" 92164426Ssam 93236987Simp/* 94236987Simp * XXX: For the main bus dma tag. Can go away if the new method to get the 95166064Scognet * dma tag from the parent got MFC'd into RELENG_6. 96166064Scognet */ 97166064Scognetextern struct ixp425_softc *ixp425_softc; 98166064Scognet 99164426Ssamstruct npebuf { 100164426Ssam struct npebuf *ix_next; /* chain to next buffer */ 101164426Ssam void *ix_m; /* backpointer to mbuf */ 102164426Ssam bus_dmamap_t ix_map; /* bus dma map for associated data */ 103164426Ssam struct npehwbuf *ix_hw; /* associated h/w block */ 104164426Ssam uint32_t ix_neaddr; /* phys address of ix_hw */ 105164426Ssam}; 106164426Ssam 107164426Ssamstruct npedma { 108164426Ssam const char* name; 109164426Ssam int nbuf; /* # npebuf's allocated */ 110164426Ssam bus_dma_tag_t mtag; /* bus dma tag for mbuf data */ 111164426Ssam struct npehwbuf *hwbuf; /* NPE h/w buffers */ 112164426Ssam bus_dma_tag_t buf_tag; /* tag+map for NPE buffers */ 113164426Ssam bus_dmamap_t buf_map; 114164426Ssam bus_addr_t buf_phys; /* phys addr of buffers */ 115164426Ssam struct npebuf *buf; /* s/w buffers (1-1 w/ h/w) */ 116164426Ssam}; 117164426Ssam 118164426Ssamstruct npe_softc { 119164426Ssam /* XXX mii requires this be first; do not move! */ 120164426Ssam struct ifnet *sc_ifp; /* ifnet pointer */ 121164426Ssam struct mtx sc_mtx; /* basically a perimeter lock */ 122164426Ssam device_t sc_dev; 123164426Ssam bus_space_tag_t sc_iot; 124164426Ssam bus_space_handle_t sc_ioh; /* MAC register window */ 125164426Ssam device_t sc_mii; /* child miibus */ 126164426Ssam bus_space_handle_t sc_miih; /* MII register window */ 127186352Ssam int sc_npeid; 128164426Ssam struct ixpnpe_softc *sc_npe; /* NPE support */ 129164426Ssam int sc_debug; /* DPRINTF* control */ 130164426Ssam int sc_tickinterval; 131164426Ssam struct callout tick_ch; /* Tick callout */ 132166339Skevlo int npe_watchdog_timer; 133164426Ssam struct npedma txdma; 134164426Ssam struct npebuf *tx_free; /* list of free tx buffers */ 135164426Ssam struct npedma rxdma; 136164426Ssam bus_addr_t buf_phys; /* XXX for returning a value */ 137164426Ssam int rx_qid; /* rx qid */ 138164426Ssam int rx_freeqid; /* rx free buffers qid */ 139164426Ssam int tx_qid; /* tx qid */ 140164426Ssam int tx_doneqid; /* tx completed qid */ 141164426Ssam struct ifmib_iso_8802_3 mibdata; 142164426Ssam bus_dma_tag_t sc_stats_tag; /* bus dma tag for stats block */ 143164426Ssam struct npestats *sc_stats; 144164426Ssam bus_dmamap_t sc_stats_map; 145164426Ssam bus_addr_t sc_stats_phys; /* phys addr of sc_stats */ 146192660Ssam struct npestats sc_totals; /* accumulated sc_stats */ 147164426Ssam}; 148164426Ssam 149164426Ssam/* 150186352Ssam * Static configuration for IXP425. The tx and 151164426Ssam * rx free Q id's are fixed by the NPE microcode. The 152164426Ssam * rx Q id's are programmed to be separate to simplify 153164426Ssam * multi-port processing. It may be better to handle 154164426Ssam * all traffic through one Q (as done by the Intel drivers). 155164426Ssam * 156194321Ssam * Note that the PHY's are accessible only from MAC B on the 157194321Ssam * IXP425 and from MAC C on other devices. This and other 158194321Ssam * platform-specific assumptions are handled with hints. 159164426Ssam */ 160164426Ssamstatic const struct { 161186352Ssam uint32_t macbase; 162164426Ssam uint32_t miibase; 163177505Ssam int phy; /* phy id */ 164164426Ssam uint8_t rx_qid; 165164426Ssam uint8_t rx_freeqid; 166164426Ssam uint8_t tx_qid; 167164426Ssam uint8_t tx_doneqid; 168186352Ssam} npeconfig[NPE_MAX] = { 169186352Ssam [NPE_A] = { 170186352Ssam .macbase = IXP435_MAC_A_HWBASE, 171186352Ssam .miibase = IXP425_MAC_C_HWBASE, 172186352Ssam .phy = 2, 173186352Ssam .rx_qid = 4, 174186352Ssam .rx_freeqid = 26, 175186352Ssam .tx_qid = 23, 176186352Ssam .tx_doneqid = 31 177186352Ssam }, 178186352Ssam [NPE_B] = { 179186352Ssam .macbase = IXP425_MAC_B_HWBASE, 180194321Ssam .miibase = IXP425_MAC_B_HWBASE, 181177505Ssam .phy = 0, 182164426Ssam .rx_qid = 4, 183164426Ssam .rx_freeqid = 27, 184164426Ssam .tx_qid = 24, 185164426Ssam .tx_doneqid = 31 186164426Ssam }, 187186352Ssam [NPE_C] = { 188186352Ssam .macbase = IXP425_MAC_C_HWBASE, 189194321Ssam .miibase = IXP425_MAC_B_HWBASE, 190177505Ssam .phy = 1, 191164426Ssam .rx_qid = 12, 192164426Ssam .rx_freeqid = 28, 193164426Ssam .tx_qid = 25, 194164426Ssam .tx_doneqid = 31 195164426Ssam }, 196164426Ssam}; 197164426Ssamstatic struct npe_softc *npes[NPE_MAX]; /* NB: indexed by npeid */ 198164426Ssam 199164426Ssamstatic __inline uint32_t 200164426SsamRD4(struct npe_softc *sc, bus_size_t off) 201164426Ssam{ 202164426Ssam return bus_space_read_4(sc->sc_iot, sc->sc_ioh, off); 203164426Ssam} 204164426Ssam 205164426Ssamstatic __inline void 206164426SsamWR4(struct npe_softc *sc, bus_size_t off, uint32_t val) 207164426Ssam{ 208164426Ssam bus_space_write_4(sc->sc_iot, sc->sc_ioh, off, val); 209164426Ssam} 210164426Ssam 211164426Ssam#define NPE_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 212164426Ssam#define NPE_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 213164426Ssam#define NPE_LOCK_INIT(_sc) \ 214164426Ssam mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ 215164426Ssam MTX_NETWORK_LOCK, MTX_DEF) 216164426Ssam#define NPE_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 217164426Ssam#define NPE_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); 218164426Ssam#define NPE_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); 219164426Ssam 220164426Ssamstatic devclass_t npe_devclass; 221164426Ssam 222186352Ssamstatic int override_npeid(device_t, const char *resname, int *val); 223164426Ssamstatic int npe_activate(device_t dev); 224164426Ssamstatic void npe_deactivate(device_t dev); 225164426Ssamstatic int npe_ifmedia_update(struct ifnet *ifp); 226164426Ssamstatic void npe_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr); 227164426Ssamstatic void npe_setmac(struct npe_softc *sc, u_char *eaddr); 228164426Ssamstatic void npe_getmac(struct npe_softc *sc, u_char *eaddr); 229164426Ssamstatic void npe_txdone(int qid, void *arg); 230164426Ssamstatic int npe_rxbuf_init(struct npe_softc *, struct npebuf *, 231164426Ssam struct mbuf *); 232193096Sattiliostatic int npe_rxdone(int qid, void *arg); 233164426Ssamstatic void npeinit(void *); 234164426Ssamstatic void npestart_locked(struct ifnet *); 235164426Ssamstatic void npestart(struct ifnet *); 236164426Ssamstatic void npestop(struct npe_softc *); 237166339Skevlostatic void npewatchdog(struct npe_softc *); 238164426Ssamstatic int npeioctl(struct ifnet * ifp, u_long, caddr_t); 239164426Ssam 240164426Ssamstatic int npe_setrxqosentry(struct npe_softc *, int classix, 241164426Ssam int trafclass, int qid); 242194321Ssamstatic int npe_setportaddress(struct npe_softc *, const uint8_t mac[]); 243186352Ssamstatic int npe_setfirewallmode(struct npe_softc *, int onoff); 244164426Ssamstatic int npe_updatestats(struct npe_softc *); 245164426Ssam#if 0 246164426Ssamstatic int npe_getstats(struct npe_softc *); 247164426Ssamstatic uint32_t npe_getimageid(struct npe_softc *); 248164426Ssamstatic int npe_setloopback(struct npe_softc *, int ena); 249164426Ssam#endif 250164426Ssam 251164426Ssam/* NB: all tx done processing goes through one queue */ 252164426Ssamstatic int tx_doneqid = -1; 253164426Ssam 254227309Sedstatic SYSCTL_NODE(_hw, OID_AUTO, npe, CTLFLAG_RD, 0, 255227309Sed "IXP4XX NPE driver parameters"); 256164426Ssam 257164426Ssamstatic int npe_debug = 0; 258164426SsamSYSCTL_INT(_hw_npe, OID_AUTO, debug, CTLFLAG_RW, &npe_debug, 259186352Ssam 0, "IXP4XX NPE network interface debug msgs"); 260186420SsamTUNABLE_INT("hw.npe.debug", &npe_debug); 261164426Ssam#define DPRINTF(sc, fmt, ...) do { \ 262164426Ssam if (sc->sc_debug) device_printf(sc->sc_dev, fmt, __VA_ARGS__); \ 263164426Ssam} while (0) 264164426Ssam#define DPRINTFn(n, sc, fmt, ...) do { \ 265164426Ssam if (sc->sc_debug >= n) device_printf(sc->sc_dev, fmt, __VA_ARGS__);\ 266164426Ssam} while (0) 267164426Ssamstatic int npe_tickinterval = 3; /* npe_tick frequency (secs) */ 268164426SsamSYSCTL_INT(_hw_npe, OID_AUTO, tickinterval, CTLFLAG_RD, &npe_tickinterval, 269164426Ssam 0, "periodic work interval (secs)"); 270164426SsamTUNABLE_INT("hw.npe.tickinterval", &npe_tickinterval); 271164426Ssam 272164426Ssamstatic int npe_rxbuf = 64; /* # rx buffers to allocate */ 273164426SsamSYSCTL_INT(_hw_npe, OID_AUTO, rxbuf, CTLFLAG_RD, &npe_rxbuf, 274164426Ssam 0, "rx buffers allocated"); 275164426SsamTUNABLE_INT("hw.npe.rxbuf", &npe_rxbuf); 276164426Ssamstatic int npe_txbuf = 128; /* # tx buffers to allocate */ 277164426SsamSYSCTL_INT(_hw_npe, OID_AUTO, txbuf, CTLFLAG_RD, &npe_txbuf, 278164426Ssam 0, "tx buffers allocated"); 279164426SsamTUNABLE_INT("hw.npe.txbuf", &npe_txbuf); 280164426Ssam 281164426Ssamstatic int 282186352Ssamunit2npeid(int unit) 283186352Ssam{ 284186352Ssam static const int npeidmap[2][3] = { 285186352Ssam /* on 425 A is for HSS, B & C are for Ethernet */ 286186352Ssam { NPE_B, NPE_C, -1 }, /* IXP425 */ 287186352Ssam /* 435 only has A & C, order C then A */ 288186352Ssam { NPE_C, NPE_A, -1 }, /* IXP435 */ 289186352Ssam }; 290186352Ssam /* XXX check feature register instead */ 291186352Ssam return (unit < 3 ? npeidmap[ 292186352Ssam (cpu_id() & CPU_ID_CPU_MASK) == CPU_ID_IXP435][unit] : -1); 293186352Ssam} 294186352Ssam 295186352Ssamstatic int 296164426Ssamnpe_probe(device_t dev) 297164426Ssam{ 298186352Ssam static const char *desc[NPE_MAX] = { 299186352Ssam [NPE_A] = "IXP NPE-A", 300186352Ssam [NPE_B] = "IXP NPE-B", 301186352Ssam [NPE_C] = "IXP NPE-C" 302186352Ssam }; 303186420Ssam int unit = device_get_unit(dev); 304186352Ssam int npeid; 305164426Ssam 306236987Simp if (unit > 2 || 307186420Ssam (ixp4xx_read_feature_bits() & 308186420Ssam (unit == 0 ? EXP_FCTRL_ETH0 : EXP_FCTRL_ETH1)) == 0) 309186420Ssam return EINVAL; 310186420Ssam 311186352Ssam npeid = -1; 312186352Ssam if (!override_npeid(dev, "npeid", &npeid)) 313186420Ssam npeid = unit2npeid(unit); 314186352Ssam if (npeid == -1) { 315186420Ssam device_printf(dev, "unit %d not supported\n", unit); 316164426Ssam return EINVAL; 317164426Ssam } 318186352Ssam device_set_desc(dev, desc[npeid]); 319164426Ssam return 0; 320164426Ssam} 321164426Ssam 322164426Ssamstatic int 323164426Ssamnpe_attach(device_t dev) 324164426Ssam{ 325164426Ssam struct npe_softc *sc = device_get_softc(dev); 326164426Ssam struct ixp425_softc *sa = device_get_softc(device_get_parent(dev)); 327164426Ssam struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); 328164426Ssam struct sysctl_oid *tree = device_get_sysctl_tree(dev); 329186352Ssam struct ifnet *ifp; 330164426Ssam int error; 331164426Ssam u_char eaddr[6]; 332164426Ssam 333164426Ssam sc->sc_dev = dev; 334164426Ssam sc->sc_iot = sa->sc_iot; 335164426Ssam NPE_LOCK_INIT(sc); 336164426Ssam callout_init_mtx(&sc->tick_ch, &sc->sc_mtx, 0); 337164426Ssam sc->sc_debug = npe_debug; 338164426Ssam sc->sc_tickinterval = npe_tickinterval; 339164426Ssam 340186352Ssam ifp = if_alloc(IFT_ETHER); 341186352Ssam if (ifp == NULL) { 342186352Ssam device_printf(dev, "cannot allocate ifnet\n"); 343164426Ssam error = EIO; /* XXX */ 344164426Ssam goto out; 345164426Ssam } 346186352Ssam /* NB: must be setup prior to invoking mii code */ 347186352Ssam sc->sc_ifp = ifp; 348164426Ssam 349164426Ssam error = npe_activate(dev); 350186352Ssam if (error) { 351186352Ssam device_printf(dev, "cannot activate npe\n"); 352164426Ssam goto out; 353186352Ssam } 354164426Ssam 355164426Ssam npe_getmac(sc, eaddr); 356164426Ssam 357164426Ssam ifp->if_softc = sc; 358164426Ssam if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 359164426Ssam ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; 360164426Ssam ifp->if_start = npestart; 361164426Ssam ifp->if_ioctl = npeioctl; 362164426Ssam ifp->if_init = npeinit; 363164426Ssam IFQ_SET_MAXLEN(&ifp->if_snd, sc->txdma.nbuf - 1); 364207554Ssobomax ifp->if_snd.ifq_drv_maxlen = ifqmaxlen; 365164426Ssam IFQ_SET_READY(&ifp->if_snd); 366164426Ssam ifp->if_linkmib = &sc->mibdata; 367164426Ssam ifp->if_linkmiblen = sizeof(sc->mibdata); 368164426Ssam sc->mibdata.dot3Compliance = DOT3COMPLIANCE_STATS; 369189645Ssam /* device supports oversided vlan frames */ 370189645Ssam ifp->if_capabilities |= IFCAP_VLAN_MTU; 371189645Ssam ifp->if_capenable = ifp->if_capabilities; 372164426Ssam#ifdef DEVICE_POLLING 373164426Ssam ifp->if_capabilities |= IFCAP_POLLING; 374164426Ssam#endif 375164426Ssam 376164426Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "debug", 377164426Ssam CTLFLAG_RW, &sc->sc_debug, 0, "control debugging printfs"); 378164426Ssam SYSCTL_ADD_INT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "tickinterval", 379164426Ssam CTLFLAG_RW, &sc->sc_tickinterval, 0, "periodic work frequency"); 380192660Ssam SYSCTL_ADD_STRUCT(ctx, SYSCTL_CHILDREN(tree), OID_AUTO, "stats", 381192660Ssam CTLFLAG_RD, &sc->sc_totals, npestats, "onboard stats"); 382164426Ssam 383164426Ssam ether_ifattach(ifp, eaddr); 384164426Ssam return 0; 385164426Ssamout: 386164426Ssam if (ifp != NULL) 387164426Ssam if_free(ifp); 388186352Ssam NPE_LOCK_DESTROY(sc); 389186352Ssam npe_deactivate(dev); 390164426Ssam return error; 391164426Ssam} 392164426Ssam 393164426Ssamstatic int 394164426Ssamnpe_detach(device_t dev) 395164426Ssam{ 396164426Ssam struct npe_softc *sc = device_get_softc(dev); 397164426Ssam struct ifnet *ifp = sc->sc_ifp; 398164426Ssam 399164426Ssam#ifdef DEVICE_POLLING 400164426Ssam if (ifp->if_capenable & IFCAP_POLLING) 401164426Ssam ether_poll_deregister(ifp); 402164426Ssam#endif 403164426Ssam npestop(sc); 404164426Ssam if (ifp != NULL) { 405164426Ssam ether_ifdetach(ifp); 406164426Ssam if_free(ifp); 407164426Ssam } 408164426Ssam NPE_LOCK_DESTROY(sc); 409164426Ssam npe_deactivate(dev); 410164426Ssam return 0; 411164426Ssam} 412164426Ssam 413164426Ssam/* 414164426Ssam * Compute and install the multicast filter. 415164426Ssam */ 416164426Ssamstatic void 417164426Ssamnpe_setmcast(struct npe_softc *sc) 418164426Ssam{ 419164426Ssam struct ifnet *ifp = sc->sc_ifp; 420164426Ssam uint8_t mask[ETHER_ADDR_LEN], addr[ETHER_ADDR_LEN]; 421164426Ssam int i; 422164426Ssam 423164426Ssam if (ifp->if_flags & IFF_PROMISC) { 424164426Ssam memset(mask, 0, ETHER_ADDR_LEN); 425164426Ssam memset(addr, 0, ETHER_ADDR_LEN); 426164426Ssam } else if (ifp->if_flags & IFF_ALLMULTI) { 427164426Ssam static const uint8_t allmulti[ETHER_ADDR_LEN] = 428164426Ssam { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00 }; 429164426Ssam memcpy(mask, allmulti, ETHER_ADDR_LEN); 430164426Ssam memcpy(addr, allmulti, ETHER_ADDR_LEN); 431164426Ssam } else { 432164426Ssam uint8_t clr[ETHER_ADDR_LEN], set[ETHER_ADDR_LEN]; 433164426Ssam struct ifmultiaddr *ifma; 434164426Ssam const uint8_t *mac; 435164426Ssam 436164426Ssam memset(clr, 0, ETHER_ADDR_LEN); 437164426Ssam memset(set, 0xff, ETHER_ADDR_LEN); 438164426Ssam 439195049Srwatson if_maddr_rlock(ifp); 440164426Ssam TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) { 441164426Ssam if (ifma->ifma_addr->sa_family != AF_LINK) 442164426Ssam continue; 443164426Ssam mac = LLADDR((struct sockaddr_dl *) ifma->ifma_addr); 444164426Ssam for (i = 0; i < ETHER_ADDR_LEN; i++) { 445164426Ssam clr[i] |= mac[i]; 446164426Ssam set[i] &= mac[i]; 447164426Ssam } 448164426Ssam } 449195049Srwatson if_maddr_runlock(ifp); 450164426Ssam 451164426Ssam for (i = 0; i < ETHER_ADDR_LEN; i++) { 452164426Ssam mask[i] = set[i] | ~clr[i]; 453164426Ssam addr[i] = set[i]; 454164426Ssam } 455164426Ssam } 456164426Ssam 457164426Ssam /* 458164426Ssam * Write the mask and address registers. 459164426Ssam */ 460164426Ssam for (i = 0; i < ETHER_ADDR_LEN; i++) { 461164426Ssam WR4(sc, NPE_MAC_ADDR_MASK(i), mask[i]); 462164426Ssam WR4(sc, NPE_MAC_ADDR(i), addr[i]); 463164426Ssam } 464164426Ssam} 465164426Ssam 466164426Ssamstatic void 467164426Ssamnpe_getaddr(void *arg, bus_dma_segment_t *segs, int nsegs, int error) 468164426Ssam{ 469164426Ssam struct npe_softc *sc; 470164426Ssam 471164426Ssam if (error != 0) 472164426Ssam return; 473164426Ssam sc = (struct npe_softc *)arg; 474164426Ssam sc->buf_phys = segs[0].ds_addr; 475164426Ssam} 476164426Ssam 477164426Ssamstatic int 478164426Ssamnpe_dma_setup(struct npe_softc *sc, struct npedma *dma, 479164426Ssam const char *name, int nbuf, int maxseg) 480164426Ssam{ 481164426Ssam int error, i; 482164426Ssam 483183886Ssam memset(dma, 0, sizeof(*dma)); 484164426Ssam 485164426Ssam dma->name = name; 486164426Ssam dma->nbuf = nbuf; 487164426Ssam 488164426Ssam /* DMA tag for mapped mbufs */ 489166064Scognet error = bus_dma_tag_create(ixp425_softc->sc_dmat, 1, 0, 490164426Ssam BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 491164426Ssam MCLBYTES, maxseg, MCLBYTES, 0, 492164426Ssam busdma_lock_mutex, &sc->sc_mtx, &dma->mtag); 493164426Ssam if (error != 0) { 494164426Ssam device_printf(sc->sc_dev, "unable to create %s mbuf dma tag, " 495164426Ssam "error %u\n", dma->name, error); 496164426Ssam return error; 497164426Ssam } 498164426Ssam 499164426Ssam /* DMA tag and map for the NPE buffers */ 500236987Simp error = bus_dma_tag_create(ixp425_softc->sc_dmat, sizeof(uint32_t), 0, 501164426Ssam BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 502164426Ssam nbuf * sizeof(struct npehwbuf), 1, 503164426Ssam nbuf * sizeof(struct npehwbuf), 0, 504164426Ssam busdma_lock_mutex, &sc->sc_mtx, &dma->buf_tag); 505164426Ssam if (error != 0) { 506164426Ssam device_printf(sc->sc_dev, 507164426Ssam "unable to create %s npebuf dma tag, error %u\n", 508164426Ssam dma->name, error); 509164426Ssam return error; 510164426Ssam } 511164426Ssam if (bus_dmamem_alloc(dma->buf_tag, (void **)&dma->hwbuf, 512164426Ssam BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, 513164426Ssam &dma->buf_map) != 0) { 514164426Ssam device_printf(sc->sc_dev, 515164426Ssam "unable to allocate memory for %s h/w buffers, error %u\n", 516164426Ssam dma->name, error); 517164426Ssam return error; 518164426Ssam } 519164426Ssam /* XXX M_TEMP */ 520164426Ssam dma->buf = malloc(nbuf * sizeof(struct npebuf), M_TEMP, M_NOWAIT | M_ZERO); 521164426Ssam if (dma->buf == NULL) { 522164426Ssam device_printf(sc->sc_dev, 523164426Ssam "unable to allocate memory for %s s/w buffers\n", 524164426Ssam dma->name); 525164426Ssam return error; 526164426Ssam } 527164426Ssam if (bus_dmamap_load(dma->buf_tag, dma->buf_map, 528164426Ssam dma->hwbuf, nbuf*sizeof(struct npehwbuf), npe_getaddr, sc, 0) != 0) { 529164426Ssam device_printf(sc->sc_dev, 530164426Ssam "unable to map memory for %s h/w buffers, error %u\n", 531164426Ssam dma->name, error); 532164426Ssam return error; 533164426Ssam } 534164426Ssam dma->buf_phys = sc->buf_phys; 535164426Ssam for (i = 0; i < dma->nbuf; i++) { 536164426Ssam struct npebuf *npe = &dma->buf[i]; 537164426Ssam struct npehwbuf *hw = &dma->hwbuf[i]; 538164426Ssam 539164426Ssam /* calculate offset to shared area */ 540164426Ssam npe->ix_neaddr = dma->buf_phys + 541164426Ssam ((uintptr_t)hw - (uintptr_t)dma->hwbuf); 542164426Ssam KASSERT((npe->ix_neaddr & 0x1f) == 0, 543164426Ssam ("ixpbuf misaligned, PA 0x%x", npe->ix_neaddr)); 544164426Ssam error = bus_dmamap_create(dma->mtag, BUS_DMA_NOWAIT, 545164426Ssam &npe->ix_map); 546164426Ssam if (error != 0) { 547164426Ssam device_printf(sc->sc_dev, 548164426Ssam "unable to create dmamap for %s buffer %u, " 549164426Ssam "error %u\n", dma->name, i, error); 550164426Ssam return error; 551164426Ssam } 552164426Ssam npe->ix_hw = hw; 553164426Ssam } 554164426Ssam bus_dmamap_sync(dma->buf_tag, dma->buf_map, BUS_DMASYNC_PREWRITE); 555164426Ssam return 0; 556164426Ssam} 557164426Ssam 558164426Ssamstatic void 559164426Ssamnpe_dma_destroy(struct npe_softc *sc, struct npedma *dma) 560164426Ssam{ 561164426Ssam int i; 562164426Ssam 563164426Ssam if (dma->hwbuf != NULL) { 564164426Ssam for (i = 0; i < dma->nbuf; i++) { 565164426Ssam struct npebuf *npe = &dma->buf[i]; 566164426Ssam bus_dmamap_destroy(dma->mtag, npe->ix_map); 567164426Ssam } 568164426Ssam bus_dmamap_unload(dma->buf_tag, dma->buf_map); 569164426Ssam bus_dmamem_free(dma->buf_tag, dma->hwbuf, dma->buf_map); 570164426Ssam } 571164426Ssam if (dma->buf != NULL) 572164426Ssam free(dma->buf, M_TEMP); 573164426Ssam if (dma->buf_tag) 574164426Ssam bus_dma_tag_destroy(dma->buf_tag); 575164426Ssam if (dma->mtag) 576164426Ssam bus_dma_tag_destroy(dma->mtag); 577164426Ssam memset(dma, 0, sizeof(*dma)); 578164426Ssam} 579164426Ssam 580164426Ssamstatic int 581186352Ssamoverride_addr(device_t dev, const char *resname, int *base) 582177505Ssam{ 583177505Ssam int unit = device_get_unit(dev); 584177505Ssam const char *resval; 585177505Ssam 586177505Ssam /* XXX warn for wrong hint type */ 587177505Ssam if (resource_string_value("npe", unit, resname, &resval) != 0) 588177505Ssam return 0; 589177505Ssam switch (resval[0]) { 590177505Ssam case 'A': 591186352Ssam *base = IXP435_MAC_A_HWBASE; 592177505Ssam break; 593177505Ssam case 'B': 594177505Ssam *base = IXP425_MAC_B_HWBASE; 595177505Ssam break; 596186352Ssam case 'C': 597186352Ssam *base = IXP425_MAC_C_HWBASE; 598186352Ssam break; 599177505Ssam default: 600177505Ssam device_printf(dev, "Warning, bad value %s for " 601177505Ssam "npe.%d.%s ignored\n", resval, unit, resname); 602177505Ssam return 0; 603177505Ssam } 604177505Ssam if (bootverbose) 605177505Ssam device_printf(dev, "using npe.%d.%s=%s override\n", 606177505Ssam unit, resname, resval); 607177505Ssam return 1; 608177505Ssam} 609177505Ssam 610177505Ssamstatic int 611186352Ssamoverride_npeid(device_t dev, const char *resname, int *npeid) 612186352Ssam{ 613186352Ssam int unit = device_get_unit(dev); 614186352Ssam const char *resval; 615186352Ssam 616186352Ssam /* XXX warn for wrong hint type */ 617186352Ssam if (resource_string_value("npe", unit, resname, &resval) != 0) 618186352Ssam return 0; 619186352Ssam switch (resval[0]) { 620186352Ssam case 'A': *npeid = NPE_A; break; 621186352Ssam case 'B': *npeid = NPE_B; break; 622186352Ssam case 'C': *npeid = NPE_C; break; 623186352Ssam default: 624186352Ssam device_printf(dev, "Warning, bad value %s for " 625186352Ssam "npe.%d.%s ignored\n", resval, unit, resname); 626186352Ssam return 0; 627186352Ssam } 628186352Ssam if (bootverbose) 629186352Ssam device_printf(dev, "using npe.%d.%s=%s override\n", 630186352Ssam unit, resname, resval); 631186352Ssam return 1; 632186352Ssam} 633186352Ssam 634186352Ssamstatic int 635177505Ssamoverride_unit(device_t dev, const char *resname, int *val, int min, int max) 636177505Ssam{ 637177505Ssam int unit = device_get_unit(dev); 638177505Ssam int resval; 639177505Ssam 640177505Ssam if (resource_int_value("npe", unit, resname, &resval) != 0) 641177505Ssam return 0; 642177505Ssam if (!(min <= resval && resval <= max)) { 643177505Ssam device_printf(dev, "Warning, bad value %d for npe.%d.%s " 644177505Ssam "ignored (value must be [%d-%d])\n", resval, unit, 645177505Ssam resname, min, max); 646177505Ssam return 0; 647177505Ssam } 648177505Ssam if (bootverbose) 649177505Ssam device_printf(dev, "using npe.%d.%s=%d override\n", 650177505Ssam unit, resname, resval); 651177505Ssam *val = resval; 652177505Ssam return 1; 653177505Ssam} 654177505Ssam 655186352Ssamstatic void 656186352Ssamnpe_mac_reset(struct npe_softc *sc) 657186352Ssam{ 658186352Ssam /* 659186352Ssam * Reset MAC core. 660186352Ssam */ 661186352Ssam WR4(sc, NPE_MAC_CORE_CNTRL, NPE_CORE_RESET); 662186352Ssam DELAY(NPE_MAC_RESET_DELAY); 663186352Ssam /* configure MAC to generate MDC clock */ 664186352Ssam WR4(sc, NPE_MAC_CORE_CNTRL, NPE_CORE_MDC_EN); 665186352Ssam} 666186352Ssam 667186352Ssamstatic int 668164426Ssamnpe_activate(device_t dev) 669164426Ssam{ 670194321Ssam struct npe_softc *sc = device_get_softc(dev); 671213893Smarius int error, i, macbase, miibase, phy; 672164426Ssam 673169954Ssam /* 674186352Ssam * Setup NEP ID, MAC, and MII bindings. We allow override 675186352Ssam * via hints to handle unexpected board configs. 676186352Ssam */ 677186352Ssam if (!override_npeid(dev, "npeid", &sc->sc_npeid)) 678186352Ssam sc->sc_npeid = unit2npeid(device_get_unit(dev)); 679186352Ssam sc->sc_npe = ixpnpe_attach(dev, sc->sc_npeid); 680186352Ssam if (sc->sc_npe == NULL) { 681186352Ssam device_printf(dev, "cannot attach ixpnpe\n"); 682186352Ssam return EIO; /* XXX */ 683186352Ssam } 684186352Ssam 685186352Ssam /* MAC */ 686186352Ssam if (!override_addr(dev, "mac", &macbase)) 687186352Ssam macbase = npeconfig[sc->sc_npeid].macbase; 688186352Ssam device_printf(sc->sc_dev, "MAC at 0x%x\n", macbase); 689186352Ssam if (bus_space_map(sc->sc_iot, macbase, IXP425_REG_SIZE, 0, &sc->sc_ioh)) { 690186352Ssam device_printf(dev, "cannot map mac registers 0x%x:0x%x\n", 691186352Ssam macbase, IXP425_REG_SIZE); 692186352Ssam return ENOMEM; 693186352Ssam } 694186352Ssam 695186352Ssam /* PHY */ 696213893Smarius if (!override_unit(dev, "phy", &phy, 0, MII_NPHY - 1)) 697213893Smarius phy = npeconfig[sc->sc_npeid].phy; 698186352Ssam if (!override_addr(dev, "mii", &miibase)) 699186352Ssam miibase = npeconfig[sc->sc_npeid].miibase; 700186352Ssam device_printf(sc->sc_dev, "MII at 0x%x\n", miibase); 701186352Ssam if (miibase != macbase) { 702186352Ssam /* 703186352Ssam * PHY is mapped through a different MAC, setup an 704186352Ssam * additional mapping for frobbing the PHY registers. 705186352Ssam */ 706186352Ssam if (bus_space_map(sc->sc_iot, miibase, IXP425_REG_SIZE, 0, &sc->sc_miih)) { 707186352Ssam device_printf(dev, 708186352Ssam "cannot map MII registers 0x%x:0x%x\n", 709186352Ssam miibase, IXP425_REG_SIZE); 710186352Ssam return ENOMEM; 711186352Ssam } 712186352Ssam } else 713186352Ssam sc->sc_miih = sc->sc_ioh; 714186352Ssam 715186352Ssam /* 716186420Ssam * Load NPE firmware and start it running. 717169954Ssam */ 718186420Ssam error = ixpnpe_init(sc->sc_npe); 719186420Ssam if (error != 0) { 720186420Ssam device_printf(dev, "cannot init NPE (error %d)\n", error); 721186420Ssam return error; 722169954Ssam } 723164426Ssam 724213893Smarius /* attach PHY */ 725213893Smarius error = mii_attach(dev, &sc->sc_mii, sc->sc_ifp, npe_ifmedia_update, 726213893Smarius npe_ifmedia_status, BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); 727213893Smarius if (error != 0) { 728213893Smarius device_printf(dev, "attaching PHYs failed\n"); 729213893Smarius return error; 730177505Ssam } 731164426Ssam 732164426Ssam error = npe_dma_setup(sc, &sc->txdma, "tx", npe_txbuf, NPE_MAXSEG); 733164426Ssam if (error != 0) 734164426Ssam return error; 735164426Ssam error = npe_dma_setup(sc, &sc->rxdma, "rx", npe_rxbuf, 1); 736164426Ssam if (error != 0) 737164426Ssam return error; 738164426Ssam 739164426Ssam /* setup statistics block */ 740166064Scognet error = bus_dma_tag_create(ixp425_softc->sc_dmat, sizeof(uint32_t), 0, 741164426Ssam BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, 742164426Ssam sizeof(struct npestats), 1, sizeof(struct npestats), 0, 743164426Ssam busdma_lock_mutex, &sc->sc_mtx, &sc->sc_stats_tag); 744164426Ssam if (error != 0) { 745164426Ssam device_printf(sc->sc_dev, "unable to create stats tag, " 746164426Ssam "error %u\n", error); 747164426Ssam return error; 748164426Ssam } 749164426Ssam if (bus_dmamem_alloc(sc->sc_stats_tag, (void **)&sc->sc_stats, 750164426Ssam BUS_DMA_NOWAIT, &sc->sc_stats_map) != 0) { 751164426Ssam device_printf(sc->sc_dev, 752164426Ssam "unable to allocate memory for stats block, error %u\n", 753164426Ssam error); 754164426Ssam return error; 755164426Ssam } 756164426Ssam if (bus_dmamap_load(sc->sc_stats_tag, sc->sc_stats_map, 757164426Ssam sc->sc_stats, sizeof(struct npestats), npe_getaddr, sc, 0) != 0) { 758164426Ssam device_printf(sc->sc_dev, 759164426Ssam "unable to load memory for stats block, error %u\n", 760164426Ssam error); 761164426Ssam return error; 762164426Ssam } 763164426Ssam sc->sc_stats_phys = sc->buf_phys; 764164426Ssam 765164426Ssam /* 766164426Ssam * Setup h/w rx/tx queues. There are four q's: 767164426Ssam * rx inbound q of rx'd frames 768164426Ssam * rx_free pool of ixpbuf's for receiving frames 769164426Ssam * tx outbound q of frames to send 770164426Ssam * tx_done q of tx frames that have been processed 771164426Ssam * 772164426Ssam * The NPE handles the actual tx/rx process and the q manager 773164426Ssam * handles the queues. The driver just writes entries to the 774164426Ssam * q manager mailbox's and gets callbacks when there are rx'd 775164426Ssam * frames to process or tx'd frames to reap. These callbacks 776164426Ssam * are controlled by the q configurations; e.g. we get a 777164426Ssam * callback when tx_done has 2 or more frames to process and 778164426Ssam * when the rx q has at least one frame. These setings can 779164426Ssam * changed at the time the q is configured. 780164426Ssam */ 781186352Ssam sc->rx_qid = npeconfig[sc->sc_npeid].rx_qid; 782164426Ssam ixpqmgr_qconfig(sc->rx_qid, npe_rxbuf, 0, 1, 783193096Sattilio IX_QMGR_Q_SOURCE_ID_NOT_E, (qconfig_hand_t *)npe_rxdone, sc); 784186352Ssam sc->rx_freeqid = npeconfig[sc->sc_npeid].rx_freeqid; 785164426Ssam ixpqmgr_qconfig(sc->rx_freeqid, npe_rxbuf, 0, npe_rxbuf/2, 0, NULL, sc); 786186352Ssam /* 787186352Ssam * Setup the NPE to direct all traffic to rx_qid. 788186352Ssam * When QoS is enabled in the firmware there are 789186352Ssam * 8 traffic classes; otherwise just 4. 790186352Ssam */ 791164426Ssam for (i = 0; i < 8; i++) 792164426Ssam npe_setrxqosentry(sc, i, 0, sc->rx_qid); 793164426Ssam 794186352Ssam /* disable firewall mode just in case (should be off) */ 795186352Ssam npe_setfirewallmode(sc, 0); 796186352Ssam 797186352Ssam sc->tx_qid = npeconfig[sc->sc_npeid].tx_qid; 798186352Ssam sc->tx_doneqid = npeconfig[sc->sc_npeid].tx_doneqid; 799164426Ssam ixpqmgr_qconfig(sc->tx_qid, npe_txbuf, 0, npe_txbuf, 0, NULL, sc); 800164426Ssam if (tx_doneqid == -1) { 801164426Ssam ixpqmgr_qconfig(sc->tx_doneqid, npe_txbuf, 0, 2, 802164426Ssam IX_QMGR_Q_SOURCE_ID_NOT_E, npe_txdone, sc); 803164426Ssam tx_doneqid = sc->tx_doneqid; 804164426Ssam } 805164426Ssam 806186352Ssam KASSERT(npes[sc->sc_npeid] == NULL, 807186352Ssam ("npe %u already setup", sc->sc_npeid)); 808186352Ssam npes[sc->sc_npeid] = sc; 809177505Ssam 810164426Ssam return 0; 811164426Ssam} 812164426Ssam 813164426Ssamstatic void 814164426Ssamnpe_deactivate(device_t dev) 815164426Ssam{ 816164426Ssam struct npe_softc *sc = device_get_softc(dev); 817164426Ssam 818186352Ssam npes[sc->sc_npeid] = NULL; 819164426Ssam 820164426Ssam /* XXX disable q's */ 821186352Ssam if (sc->sc_npe != NULL) { 822164426Ssam ixpnpe_stop(sc->sc_npe); 823186352Ssam ixpnpe_detach(sc->sc_npe); 824186352Ssam } 825164426Ssam if (sc->sc_stats != NULL) { 826164426Ssam bus_dmamap_unload(sc->sc_stats_tag, sc->sc_stats_map); 827164426Ssam bus_dmamem_free(sc->sc_stats_tag, sc->sc_stats, 828164426Ssam sc->sc_stats_map); 829164426Ssam } 830164426Ssam if (sc->sc_stats_tag != NULL) 831164426Ssam bus_dma_tag_destroy(sc->sc_stats_tag); 832164426Ssam npe_dma_destroy(sc, &sc->txdma); 833164426Ssam npe_dma_destroy(sc, &sc->rxdma); 834164426Ssam bus_generic_detach(sc->sc_dev); 835186352Ssam if (sc->sc_mii != NULL) 836164426Ssam device_delete_child(sc->sc_dev, sc->sc_mii); 837164426Ssam} 838164426Ssam 839164426Ssam/* 840164426Ssam * Change media according to request. 841164426Ssam */ 842164426Ssamstatic int 843164426Ssamnpe_ifmedia_update(struct ifnet *ifp) 844164426Ssam{ 845164426Ssam struct npe_softc *sc = ifp->if_softc; 846164426Ssam struct mii_data *mii; 847164426Ssam 848164426Ssam mii = device_get_softc(sc->sc_mii); 849164426Ssam NPE_LOCK(sc); 850164426Ssam mii_mediachg(mii); 851164426Ssam /* XXX push state ourself? */ 852164426Ssam NPE_UNLOCK(sc); 853164426Ssam return (0); 854164426Ssam} 855164426Ssam 856164426Ssam/* 857164426Ssam * Notify the world which media we're using. 858164426Ssam */ 859164426Ssamstatic void 860164426Ssamnpe_ifmedia_status(struct ifnet *ifp, struct ifmediareq *ifmr) 861164426Ssam{ 862164426Ssam struct npe_softc *sc = ifp->if_softc; 863164426Ssam struct mii_data *mii; 864164426Ssam 865164426Ssam mii = device_get_softc(sc->sc_mii); 866164426Ssam NPE_LOCK(sc); 867164426Ssam mii_pollstat(mii); 868164426Ssam ifmr->ifm_active = mii->mii_media_active; 869164426Ssam ifmr->ifm_status = mii->mii_media_status; 870164426Ssam NPE_UNLOCK(sc); 871164426Ssam} 872164426Ssam 873164426Ssamstatic void 874164426Ssamnpe_addstats(struct npe_softc *sc) 875164426Ssam{ 876192660Ssam#define NPEADD(x) sc->sc_totals.x += be32toh(ns->x) 877192660Ssam#define MIBADD(x) do { sc->mibdata.x += be32toh(ns->x); NPEADD(x); } while (0) 878164426Ssam struct ifnet *ifp = sc->sc_ifp; 879164426Ssam struct npestats *ns = sc->sc_stats; 880164426Ssam 881164426Ssam MIBADD(dot3StatsAlignmentErrors); 882164426Ssam MIBADD(dot3StatsFCSErrors); 883192660Ssam MIBADD(dot3StatsInternalMacReceiveErrors); 884192660Ssam NPEADD(RxOverrunDiscards); 885192660Ssam NPEADD(RxLearnedEntryDiscards); 886192660Ssam NPEADD(RxLargeFramesDiscards); 887192660Ssam NPEADD(RxSTPBlockedDiscards); 888192660Ssam NPEADD(RxVLANTypeFilterDiscards); 889192660Ssam NPEADD(RxVLANIdFilterDiscards); 890192660Ssam NPEADD(RxInvalidSourceDiscards); 891192660Ssam NPEADD(RxBlackListDiscards); 892192660Ssam NPEADD(RxWhiteListDiscards); 893192660Ssam NPEADD(RxUnderflowEntryDiscards); 894164426Ssam MIBADD(dot3StatsSingleCollisionFrames); 895164426Ssam MIBADD(dot3StatsMultipleCollisionFrames); 896164426Ssam MIBADD(dot3StatsDeferredTransmissions); 897164426Ssam MIBADD(dot3StatsLateCollisions); 898164426Ssam MIBADD(dot3StatsExcessiveCollisions); 899164426Ssam MIBADD(dot3StatsInternalMacTransmitErrors); 900164426Ssam MIBADD(dot3StatsCarrierSenseErrors); 901192660Ssam NPEADD(TxLargeFrameDiscards); 902192660Ssam NPEADD(TxVLANIdFilterDiscards); 903192660Ssam 904164426Ssam sc->mibdata.dot3StatsFrameTooLongs += 905164426Ssam be32toh(ns->RxLargeFramesDiscards) 906164426Ssam + be32toh(ns->TxLargeFrameDiscards); 907164426Ssam sc->mibdata.dot3StatsMissedFrames += 908164426Ssam be32toh(ns->RxOverrunDiscards) 909164426Ssam + be32toh(ns->RxUnderflowEntryDiscards); 910164426Ssam 911164426Ssam ifp->if_oerrors += 912164426Ssam be32toh(ns->dot3StatsInternalMacTransmitErrors) 913164426Ssam + be32toh(ns->dot3StatsCarrierSenseErrors) 914164426Ssam + be32toh(ns->TxVLANIdFilterDiscards) 915164426Ssam ; 916164426Ssam ifp->if_ierrors += be32toh(ns->dot3StatsFCSErrors) 917164426Ssam + be32toh(ns->dot3StatsInternalMacReceiveErrors) 918164426Ssam + be32toh(ns->RxOverrunDiscards) 919164426Ssam + be32toh(ns->RxUnderflowEntryDiscards) 920164426Ssam ; 921164426Ssam ifp->if_collisions += 922164426Ssam be32toh(ns->dot3StatsSingleCollisionFrames) 923164426Ssam + be32toh(ns->dot3StatsMultipleCollisionFrames) 924164426Ssam ; 925192660Ssam#undef NPEADD 926164426Ssam#undef MIBADD 927164426Ssam} 928164426Ssam 929164426Ssamstatic void 930164426Ssamnpe_tick(void *xsc) 931164426Ssam{ 932164426Ssam#define ACK (NPE_RESETSTATS << NPE_MAC_MSGID_SHL) 933164426Ssam struct npe_softc *sc = xsc; 934164426Ssam struct mii_data *mii = device_get_softc(sc->sc_mii); 935164426Ssam uint32_t msg[2]; 936164426Ssam 937164426Ssam NPE_ASSERT_LOCKED(sc); 938164426Ssam 939164426Ssam /* 940164426Ssam * NB: to avoid sleeping with the softc lock held we 941164426Ssam * split the NPE msg processing into two parts. The 942164426Ssam * request for statistics is sent w/o waiting for a 943164426Ssam * reply and then on the next tick we retrieve the 944164426Ssam * results. This works because npe_tick is the only 945164426Ssam * code that talks via the mailbox's (except at setup). 946164426Ssam * This likely can be handled better. 947164426Ssam */ 948186352Ssam if (ixpnpe_recvmsg_async(sc->sc_npe, msg) == 0 && msg[0] == ACK) { 949164426Ssam bus_dmamap_sync(sc->sc_stats_tag, sc->sc_stats_map, 950164426Ssam BUS_DMASYNC_POSTREAD); 951164426Ssam npe_addstats(sc); 952164426Ssam } 953164426Ssam npe_updatestats(sc); 954164426Ssam mii_tick(mii); 955164426Ssam 956166339Skevlo npewatchdog(sc); 957166339Skevlo 958164426Ssam /* schedule next poll */ 959164426Ssam callout_reset(&sc->tick_ch, sc->sc_tickinterval * hz, npe_tick, sc); 960164426Ssam#undef ACK 961164426Ssam} 962164426Ssam 963164426Ssamstatic void 964164426Ssamnpe_setmac(struct npe_softc *sc, u_char *eaddr) 965164426Ssam{ 966164426Ssam WR4(sc, NPE_MAC_UNI_ADDR_1, eaddr[0]); 967164426Ssam WR4(sc, NPE_MAC_UNI_ADDR_2, eaddr[1]); 968164426Ssam WR4(sc, NPE_MAC_UNI_ADDR_3, eaddr[2]); 969164426Ssam WR4(sc, NPE_MAC_UNI_ADDR_4, eaddr[3]); 970164426Ssam WR4(sc, NPE_MAC_UNI_ADDR_5, eaddr[4]); 971164426Ssam WR4(sc, NPE_MAC_UNI_ADDR_6, eaddr[5]); 972164426Ssam} 973164426Ssam 974164426Ssamstatic void 975164426Ssamnpe_getmac(struct npe_softc *sc, u_char *eaddr) 976164426Ssam{ 977164426Ssam /* NB: the unicast address appears to be loaded from EEPROM on reset */ 978164426Ssam eaddr[0] = RD4(sc, NPE_MAC_UNI_ADDR_1) & 0xff; 979164426Ssam eaddr[1] = RD4(sc, NPE_MAC_UNI_ADDR_2) & 0xff; 980164426Ssam eaddr[2] = RD4(sc, NPE_MAC_UNI_ADDR_3) & 0xff; 981164426Ssam eaddr[3] = RD4(sc, NPE_MAC_UNI_ADDR_4) & 0xff; 982164426Ssam eaddr[4] = RD4(sc, NPE_MAC_UNI_ADDR_5) & 0xff; 983164426Ssam eaddr[5] = RD4(sc, NPE_MAC_UNI_ADDR_6) & 0xff; 984164426Ssam} 985164426Ssam 986164426Ssamstruct txdone { 987164426Ssam struct npebuf *head; 988164426Ssam struct npebuf **tail; 989164426Ssam int count; 990164426Ssam}; 991164426Ssam 992164426Ssamstatic __inline void 993164426Ssamnpe_txdone_finish(struct npe_softc *sc, const struct txdone *td) 994164426Ssam{ 995164426Ssam struct ifnet *ifp = sc->sc_ifp; 996164426Ssam 997164426Ssam NPE_LOCK(sc); 998164426Ssam *td->tail = sc->tx_free; 999164426Ssam sc->tx_free = td->head; 1000164426Ssam /* 1001164426Ssam * We're no longer busy, so clear the busy flag and call the 1002164426Ssam * start routine to xmit more packets. 1003164426Ssam */ 1004164426Ssam ifp->if_opackets += td->count; 1005164426Ssam ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1006166339Skevlo sc->npe_watchdog_timer = 0; 1007164426Ssam npestart_locked(ifp); 1008164426Ssam NPE_UNLOCK(sc); 1009164426Ssam} 1010164426Ssam 1011164426Ssam/* 1012164426Ssam * Q manager callback on tx done queue. Reap mbufs 1013164426Ssam * and return tx buffers to the free list. Finally 1014164426Ssam * restart output. Note the microcode has only one 1015164426Ssam * txdone q wired into it so we must use the NPE ID 1016164426Ssam * returned with each npehwbuf to decide where to 1017164426Ssam * send buffers. 1018164426Ssam */ 1019164426Ssamstatic void 1020164426Ssamnpe_txdone(int qid, void *arg) 1021164426Ssam{ 1022164426Ssam#define P2V(a, dma) \ 1023164426Ssam &(dma)->buf[((a) - (dma)->buf_phys) / sizeof(struct npehwbuf)] 1024164426Ssam struct npe_softc *sc0 = arg; 1025164426Ssam struct npe_softc *sc; 1026164426Ssam struct npebuf *npe; 1027164426Ssam struct txdone *td, q[NPE_MAX]; 1028164426Ssam uint32_t entry; 1029164426Ssam 1030194321Ssam q[NPE_A].tail = &q[NPE_A].head; q[NPE_A].count = 0; 1031164426Ssam q[NPE_B].tail = &q[NPE_B].head; q[NPE_B].count = 0; 1032164426Ssam q[NPE_C].tail = &q[NPE_C].head; q[NPE_C].count = 0; 1033164426Ssam /* XXX max # at a time? */ 1034164426Ssam while (ixpqmgr_qread(qid, &entry) == 0) { 1035164426Ssam DPRINTF(sc0, "%s: entry 0x%x NPE %u port %u\n", 1036164426Ssam __func__, entry, NPE_QM_Q_NPE(entry), NPE_QM_Q_PORT(entry)); 1037164426Ssam 1038164426Ssam sc = npes[NPE_QM_Q_NPE(entry)]; 1039164426Ssam npe = P2V(NPE_QM_Q_ADDR(entry), &sc->txdma); 1040164426Ssam m_freem(npe->ix_m); 1041164426Ssam npe->ix_m = NULL; 1042164426Ssam 1043164426Ssam td = &q[NPE_QM_Q_NPE(entry)]; 1044164426Ssam *td->tail = npe; 1045164426Ssam td->tail = &npe->ix_next; 1046164426Ssam td->count++; 1047164426Ssam } 1048164426Ssam 1049194321Ssam if (q[NPE_A].count) 1050194321Ssam npe_txdone_finish(npes[NPE_A], &q[NPE_A]); 1051164426Ssam if (q[NPE_B].count) 1052164426Ssam npe_txdone_finish(npes[NPE_B], &q[NPE_B]); 1053164426Ssam if (q[NPE_C].count) 1054164426Ssam npe_txdone_finish(npes[NPE_C], &q[NPE_C]); 1055164426Ssam#undef P2V 1056164426Ssam} 1057164426Ssam 1058164426Ssamstatic int 1059164426Ssamnpe_rxbuf_init(struct npe_softc *sc, struct npebuf *npe, struct mbuf *m) 1060164426Ssam{ 1061164426Ssam bus_dma_segment_t segs[1]; 1062164426Ssam struct npedma *dma = &sc->rxdma; 1063164426Ssam struct npehwbuf *hw; 1064164426Ssam int error, nseg; 1065164426Ssam 1066164426Ssam if (m == NULL) { 1067243882Sglebius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1068164426Ssam if (m == NULL) 1069164426Ssam return ENOBUFS; 1070164426Ssam } 1071164426Ssam KASSERT(m->m_ext.ext_size >= 1536 + ETHER_ALIGN, 1072164426Ssam ("ext_size %d", m->m_ext.ext_size)); 1073164426Ssam m->m_pkthdr.len = m->m_len = 1536; 1074164426Ssam /* backload payload and align ip hdr */ 1075164426Ssam m->m_data = m->m_ext.ext_buf + (m->m_ext.ext_size - (1536+ETHER_ALIGN)); 1076266406Sian bus_dmamap_unload(dma->mtag, npe->ix_map); 1077164426Ssam error = bus_dmamap_load_mbuf_sg(dma->mtag, npe->ix_map, m, 1078164426Ssam segs, &nseg, 0); 1079164426Ssam if (error != 0) { 1080164426Ssam m_freem(m); 1081164426Ssam return error; 1082164426Ssam } 1083164426Ssam hw = npe->ix_hw; 1084164426Ssam hw->ix_ne[0].data = htobe32(segs[0].ds_addr); 1085164426Ssam /* NB: NPE requires length be a multiple of 64 */ 1086164426Ssam /* NB: buffer length is shifted in word */ 1087164426Ssam hw->ix_ne[0].len = htobe32(segs[0].ds_len << 16); 1088164426Ssam hw->ix_ne[0].next = 0; 1089266406Sian bus_dmamap_sync(dma->buf_tag, dma->buf_map, 1090266406Sian BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 1091164426Ssam npe->ix_m = m; 1092164426Ssam /* Flush the memory in the mbuf */ 1093164426Ssam bus_dmamap_sync(dma->mtag, npe->ix_map, BUS_DMASYNC_PREREAD); 1094164426Ssam return 0; 1095164426Ssam} 1096164426Ssam 1097164426Ssam/* 1098164426Ssam * RX q processing for a specific NPE. Claim entries 1099164426Ssam * from the hardware queue and pass the frames up the 1100164426Ssam * stack. Pass the rx buffers to the free list. 1101164426Ssam */ 1102193096Sattiliostatic int 1103164426Ssamnpe_rxdone(int qid, void *arg) 1104164426Ssam{ 1105164426Ssam#define P2V(a, dma) \ 1106164426Ssam &(dma)->buf[((a) - (dma)->buf_phys) / sizeof(struct npehwbuf)] 1107164426Ssam struct npe_softc *sc = arg; 1108164426Ssam struct npedma *dma = &sc->rxdma; 1109164426Ssam uint32_t entry; 1110193096Sattilio int rx_npkts = 0; 1111164426Ssam 1112164426Ssam while (ixpqmgr_qread(qid, &entry) == 0) { 1113164426Ssam struct npebuf *npe = P2V(NPE_QM_Q_ADDR(entry), dma); 1114164426Ssam struct mbuf *m; 1115164426Ssam 1116266406Sian bus_dmamap_sync(dma->buf_tag, dma->buf_map, 1117266406Sian BUS_DMASYNC_POSTREAD); 1118164426Ssam DPRINTF(sc, "%s: entry 0x%x neaddr 0x%x ne_len 0x%x\n", 1119164426Ssam __func__, entry, npe->ix_neaddr, npe->ix_hw->ix_ne[0].len); 1120164426Ssam /* 1121164426Ssam * Allocate a new mbuf to replenish the rx buffer. 1122164426Ssam * If doing so fails we drop the rx'd frame so we 1123164426Ssam * can reuse the previous mbuf. When we're able to 1124164426Ssam * allocate a new mbuf dispatch the mbuf w/ rx'd 1125164426Ssam * data up the stack and replace it with the newly 1126164426Ssam * allocated one. 1127164426Ssam */ 1128243882Sglebius m = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR); 1129164426Ssam if (m != NULL) { 1130164426Ssam struct mbuf *mrx = npe->ix_m; 1131164426Ssam struct npehwbuf *hw = npe->ix_hw; 1132164426Ssam struct ifnet *ifp = sc->sc_ifp; 1133164426Ssam 1134164426Ssam /* Flush mbuf memory for rx'd data */ 1135164426Ssam bus_dmamap_sync(dma->mtag, npe->ix_map, 1136164426Ssam BUS_DMASYNC_POSTREAD); 1137164426Ssam 1138164426Ssam /* set m_len etc. per rx frame size */ 1139164426Ssam mrx->m_len = be32toh(hw->ix_ne[0].len) & 0xffff; 1140164426Ssam mrx->m_pkthdr.len = mrx->m_len; 1141164426Ssam mrx->m_pkthdr.rcvif = ifp; 1142164426Ssam 1143164426Ssam ifp->if_ipackets++; 1144164426Ssam ifp->if_input(ifp, mrx); 1145193096Sattilio rx_npkts++; 1146164426Ssam } else { 1147164426Ssam /* discard frame and re-use mbuf */ 1148164426Ssam m = npe->ix_m; 1149164426Ssam } 1150164426Ssam if (npe_rxbuf_init(sc, npe, m) == 0) { 1151164426Ssam /* return npe buf to rx free list */ 1152164426Ssam ixpqmgr_qwrite(sc->rx_freeqid, npe->ix_neaddr); 1153164426Ssam } else { 1154164426Ssam /* XXX should not happen */ 1155164426Ssam } 1156164426Ssam } 1157193104Ssam return rx_npkts; 1158164426Ssam#undef P2V 1159164426Ssam} 1160164426Ssam 1161164426Ssam#ifdef DEVICE_POLLING 1162193096Sattiliostatic int 1163164426Ssamnpe_poll(struct ifnet *ifp, enum poll_cmd cmd, int count) 1164164426Ssam{ 1165164426Ssam struct npe_softc *sc = ifp->if_softc; 1166193096Sattilio int rx_npkts = 0; 1167164426Ssam 1168164426Ssam if (ifp->if_drv_flags & IFF_DRV_RUNNING) { 1169193096Sattilio rx_npkts = npe_rxdone(sc->rx_qid, sc); 1170164426Ssam npe_txdone(sc->tx_doneqid, sc); /* XXX polls both NPE's */ 1171164426Ssam } 1172193104Ssam return rx_npkts; 1173164426Ssam} 1174164426Ssam#endif /* DEVICE_POLLING */ 1175164426Ssam 1176164426Ssamstatic void 1177164426Ssamnpe_startxmit(struct npe_softc *sc) 1178164426Ssam{ 1179164426Ssam struct npedma *dma = &sc->txdma; 1180164426Ssam int i; 1181164426Ssam 1182164426Ssam NPE_ASSERT_LOCKED(sc); 1183164426Ssam sc->tx_free = NULL; 1184164426Ssam for (i = 0; i < dma->nbuf; i++) { 1185164426Ssam struct npebuf *npe = &dma->buf[i]; 1186164426Ssam if (npe->ix_m != NULL) { 1187164426Ssam /* NB: should not happen */ 1188164426Ssam device_printf(sc->sc_dev, 1189164426Ssam "%s: free mbuf at entry %u\n", __func__, i); 1190164426Ssam m_freem(npe->ix_m); 1191164426Ssam } 1192164426Ssam npe->ix_m = NULL; 1193164426Ssam npe->ix_next = sc->tx_free; 1194164426Ssam sc->tx_free = npe; 1195164426Ssam } 1196164426Ssam} 1197164426Ssam 1198164426Ssamstatic void 1199164426Ssamnpe_startrecv(struct npe_softc *sc) 1200164426Ssam{ 1201164426Ssam struct npedma *dma = &sc->rxdma; 1202164426Ssam struct npebuf *npe; 1203164426Ssam int i; 1204164426Ssam 1205164426Ssam NPE_ASSERT_LOCKED(sc); 1206164426Ssam for (i = 0; i < dma->nbuf; i++) { 1207164426Ssam npe = &dma->buf[i]; 1208164426Ssam npe_rxbuf_init(sc, npe, npe->ix_m); 1209164426Ssam /* set npe buf on rx free list */ 1210164426Ssam ixpqmgr_qwrite(sc->rx_freeqid, npe->ix_neaddr); 1211164426Ssam } 1212164426Ssam} 1213164426Ssam 1214164426Ssam/* 1215164426Ssam * Reset and initialize the chip 1216164426Ssam */ 1217164426Ssamstatic void 1218164426Ssamnpeinit_locked(void *xsc) 1219164426Ssam{ 1220164426Ssam struct npe_softc *sc = xsc; 1221164426Ssam struct ifnet *ifp = sc->sc_ifp; 1222164426Ssam 1223164426Ssam NPE_ASSERT_LOCKED(sc); 1224164426Ssamif (ifp->if_drv_flags & IFF_DRV_RUNNING) return;/*XXX*/ 1225164426Ssam 1226164426Ssam /* 1227164426Ssam * Reset MAC core. 1228164426Ssam */ 1229186352Ssam npe_mac_reset(sc); 1230164426Ssam 1231164426Ssam /* disable transmitter and reciver in the MAC */ 1232164426Ssam WR4(sc, NPE_MAC_RX_CNTRL1, 1233164426Ssam RD4(sc, NPE_MAC_RX_CNTRL1) &~ NPE_RX_CNTRL1_RX_EN); 1234164426Ssam WR4(sc, NPE_MAC_TX_CNTRL1, 1235164426Ssam RD4(sc, NPE_MAC_TX_CNTRL1) &~ NPE_TX_CNTRL1_TX_EN); 1236164426Ssam 1237164426Ssam /* 1238164426Ssam * Set the MAC core registers. 1239164426Ssam */ 1240164426Ssam WR4(sc, NPE_MAC_INT_CLK_THRESH, 0x1); /* clock ratio: for ipx4xx */ 1241164426Ssam WR4(sc, NPE_MAC_TX_CNTRL2, 0xf); /* max retries */ 1242164426Ssam WR4(sc, NPE_MAC_RANDOM_SEED, 0x8); /* LFSR back-off seed */ 1243164426Ssam /* thresholds determined by NPE firmware FS */ 1244164426Ssam WR4(sc, NPE_MAC_THRESH_P_EMPTY, 0x12); 1245164426Ssam WR4(sc, NPE_MAC_THRESH_P_FULL, 0x30); 1246164426Ssam WR4(sc, NPE_MAC_BUF_SIZE_TX, 0x8); /* tx fifo threshold (bytes) */ 1247164426Ssam WR4(sc, NPE_MAC_TX_DEFER, 0x15); /* for single deferral */ 1248164426Ssam WR4(sc, NPE_MAC_RX_DEFER, 0x16); /* deferral on inter-frame gap*/ 1249164426Ssam WR4(sc, NPE_MAC_TX_TWO_DEFER_1, 0x8); /* for 2-part deferral */ 1250164426Ssam WR4(sc, NPE_MAC_TX_TWO_DEFER_2, 0x7); /* for 2-part deferral */ 1251164426Ssam WR4(sc, NPE_MAC_SLOT_TIME, 0x80); /* assumes MII mode */ 1252164426Ssam 1253164426Ssam WR4(sc, NPE_MAC_TX_CNTRL1, 1254164426Ssam NPE_TX_CNTRL1_RETRY /* retry failed xmits */ 1255164426Ssam | NPE_TX_CNTRL1_FCS_EN /* append FCS */ 1256164426Ssam | NPE_TX_CNTRL1_2DEFER /* 2-part deferal */ 1257164426Ssam | NPE_TX_CNTRL1_PAD_EN); /* pad runt frames */ 1258164426Ssam /* XXX pad strip? */ 1259189642Ssam /* ena pause frame handling */ 1260189642Ssam WR4(sc, NPE_MAC_RX_CNTRL1, NPE_RX_CNTRL1_PAUSE_EN); 1261164426Ssam WR4(sc, NPE_MAC_RX_CNTRL2, 0); 1262164426Ssam 1263164426Ssam npe_setmac(sc, IF_LLADDR(ifp)); 1264194321Ssam npe_setportaddress(sc, IF_LLADDR(ifp)); 1265164426Ssam npe_setmcast(sc); 1266164426Ssam 1267164426Ssam npe_startxmit(sc); 1268164426Ssam npe_startrecv(sc); 1269164426Ssam 1270164426Ssam ifp->if_drv_flags |= IFF_DRV_RUNNING; 1271164426Ssam ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1272166339Skevlo sc->npe_watchdog_timer = 0; /* just in case */ 1273164426Ssam 1274164426Ssam /* enable transmitter and reciver in the MAC */ 1275164426Ssam WR4(sc, NPE_MAC_RX_CNTRL1, 1276164426Ssam RD4(sc, NPE_MAC_RX_CNTRL1) | NPE_RX_CNTRL1_RX_EN); 1277164426Ssam WR4(sc, NPE_MAC_TX_CNTRL1, 1278164426Ssam RD4(sc, NPE_MAC_TX_CNTRL1) | NPE_TX_CNTRL1_TX_EN); 1279164426Ssam 1280164426Ssam callout_reset(&sc->tick_ch, sc->sc_tickinterval * hz, npe_tick, sc); 1281164426Ssam} 1282164426Ssam 1283164426Ssamstatic void 1284164426Ssamnpeinit(void *xsc) 1285164426Ssam{ 1286164426Ssam struct npe_softc *sc = xsc; 1287164426Ssam NPE_LOCK(sc); 1288164426Ssam npeinit_locked(sc); 1289164426Ssam NPE_UNLOCK(sc); 1290164426Ssam} 1291164426Ssam 1292164426Ssam/* 1293164426Ssam * Dequeue packets and place on the h/w transmit queue. 1294164426Ssam */ 1295164426Ssamstatic void 1296164426Ssamnpestart_locked(struct ifnet *ifp) 1297164426Ssam{ 1298164426Ssam struct npe_softc *sc = ifp->if_softc; 1299164426Ssam struct npebuf *npe; 1300164426Ssam struct npehwbuf *hw; 1301164426Ssam struct mbuf *m, *n; 1302164426Ssam struct npedma *dma = &sc->txdma; 1303164426Ssam bus_dma_segment_t segs[NPE_MAXSEG]; 1304164426Ssam int nseg, len, error, i; 1305164426Ssam uint32_t next; 1306164426Ssam 1307164426Ssam NPE_ASSERT_LOCKED(sc); 1308164426Ssam /* XXX can this happen? */ 1309164426Ssam if (ifp->if_drv_flags & IFF_DRV_OACTIVE) 1310164426Ssam return; 1311164426Ssam 1312164426Ssam while (sc->tx_free != NULL) { 1313164426Ssam IFQ_DRV_DEQUEUE(&ifp->if_snd, m); 1314164426Ssam if (m == NULL) { 1315164426Ssam /* XXX? */ 1316164426Ssam ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 1317164426Ssam return; 1318164426Ssam } 1319164426Ssam npe = sc->tx_free; 1320266406Sian bus_dmamap_unload(dma->mtag, npe->ix_map); 1321164426Ssam error = bus_dmamap_load_mbuf_sg(dma->mtag, npe->ix_map, 1322164426Ssam m, segs, &nseg, 0); 1323164426Ssam if (error == EFBIG) { 1324243882Sglebius n = m_collapse(m, M_NOWAIT, NPE_MAXSEG); 1325164426Ssam if (n == NULL) { 1326164426Ssam if_printf(ifp, "%s: too many fragments %u\n", 1327164426Ssam __func__, nseg); 1328164426Ssam m_freem(m); 1329164426Ssam return; /* XXX? */ 1330164426Ssam } 1331164426Ssam m = n; 1332164426Ssam error = bus_dmamap_load_mbuf_sg(dma->mtag, npe->ix_map, 1333164426Ssam m, segs, &nseg, 0); 1334164426Ssam } 1335164426Ssam if (error != 0 || nseg == 0) { 1336164426Ssam if_printf(ifp, "%s: error %u nseg %u\n", 1337164426Ssam __func__, error, nseg); 1338164426Ssam m_freem(m); 1339164426Ssam return; /* XXX? */ 1340164426Ssam } 1341164426Ssam sc->tx_free = npe->ix_next; 1342164426Ssam 1343164426Ssam bus_dmamap_sync(dma->mtag, npe->ix_map, BUS_DMASYNC_PREWRITE); 1344164426Ssam 1345164426Ssam /* 1346164426Ssam * Tap off here if there is a bpf listener. 1347164426Ssam */ 1348164426Ssam BPF_MTAP(ifp, m); 1349164426Ssam 1350164426Ssam npe->ix_m = m; 1351164426Ssam hw = npe->ix_hw; 1352164426Ssam len = m->m_pkthdr.len; 1353164426Ssam next = npe->ix_neaddr + sizeof(hw->ix_ne[0]); 1354164426Ssam for (i = 0; i < nseg; i++) { 1355164426Ssam hw->ix_ne[i].data = htobe32(segs[i].ds_addr); 1356164426Ssam hw->ix_ne[i].len = htobe32((segs[i].ds_len<<16) | len); 1357164426Ssam hw->ix_ne[i].next = htobe32(next); 1358164426Ssam 1359164426Ssam len = 0; /* zero for segments > 1 */ 1360164426Ssam next += sizeof(hw->ix_ne[0]); 1361164426Ssam } 1362164426Ssam hw->ix_ne[i-1].next = 0; /* zero last in chain */ 1363266406Sian bus_dmamap_sync(dma->buf_tag, dma->buf_map, 1364266406Sian BUS_DMASYNC_PREREAD|BUS_DMASYNC_PREWRITE); 1365164426Ssam 1366164426Ssam DPRINTF(sc, "%s: qwrite(%u, 0x%x) ne_data %x ne_len 0x%x\n", 1367164426Ssam __func__, sc->tx_qid, npe->ix_neaddr, 1368164426Ssam hw->ix_ne[0].data, hw->ix_ne[0].len); 1369164426Ssam /* stick it on the tx q */ 1370164426Ssam /* XXX add vlan priority */ 1371164426Ssam ixpqmgr_qwrite(sc->tx_qid, npe->ix_neaddr); 1372164426Ssam 1373166339Skevlo sc->npe_watchdog_timer = 5; 1374164426Ssam } 1375164426Ssam if (sc->tx_free == NULL) 1376164426Ssam ifp->if_drv_flags |= IFF_DRV_OACTIVE; 1377164426Ssam} 1378164426Ssam 1379164426Ssamvoid 1380164426Ssamnpestart(struct ifnet *ifp) 1381164426Ssam{ 1382164426Ssam struct npe_softc *sc = ifp->if_softc; 1383164426Ssam NPE_LOCK(sc); 1384164426Ssam npestart_locked(ifp); 1385164426Ssam NPE_UNLOCK(sc); 1386164426Ssam} 1387164426Ssam 1388164426Ssamstatic void 1389164426Ssamnpe_stopxmit(struct npe_softc *sc) 1390164426Ssam{ 1391164426Ssam struct npedma *dma = &sc->txdma; 1392164426Ssam int i; 1393164426Ssam 1394164426Ssam NPE_ASSERT_LOCKED(sc); 1395164426Ssam 1396164426Ssam /* XXX qmgr */ 1397164426Ssam for (i = 0; i < dma->nbuf; i++) { 1398164426Ssam struct npebuf *npe = &dma->buf[i]; 1399164426Ssam 1400164426Ssam if (npe->ix_m != NULL) { 1401164426Ssam bus_dmamap_unload(dma->mtag, npe->ix_map); 1402164426Ssam m_freem(npe->ix_m); 1403164426Ssam npe->ix_m = NULL; 1404164426Ssam } 1405164426Ssam } 1406164426Ssam} 1407164426Ssam 1408164426Ssamstatic void 1409164426Ssamnpe_stoprecv(struct npe_softc *sc) 1410164426Ssam{ 1411164426Ssam struct npedma *dma = &sc->rxdma; 1412164426Ssam int i; 1413164426Ssam 1414164426Ssam NPE_ASSERT_LOCKED(sc); 1415164426Ssam 1416164426Ssam /* XXX qmgr */ 1417164426Ssam for (i = 0; i < dma->nbuf; i++) { 1418164426Ssam struct npebuf *npe = &dma->buf[i]; 1419164426Ssam 1420164426Ssam if (npe->ix_m != NULL) { 1421164426Ssam bus_dmamap_unload(dma->mtag, npe->ix_map); 1422164426Ssam m_freem(npe->ix_m); 1423164426Ssam npe->ix_m = NULL; 1424164426Ssam } 1425164426Ssam } 1426164426Ssam} 1427164426Ssam 1428164426Ssam/* 1429164426Ssam * Turn off interrupts, and stop the nic. 1430164426Ssam */ 1431164426Ssamvoid 1432164426Ssamnpestop(struct npe_softc *sc) 1433164426Ssam{ 1434164426Ssam struct ifnet *ifp = sc->sc_ifp; 1435164426Ssam 1436164426Ssam /* disable transmitter and reciver in the MAC */ 1437164426Ssam WR4(sc, NPE_MAC_RX_CNTRL1, 1438164426Ssam RD4(sc, NPE_MAC_RX_CNTRL1) &~ NPE_RX_CNTRL1_RX_EN); 1439164426Ssam WR4(sc, NPE_MAC_TX_CNTRL1, 1440164426Ssam RD4(sc, NPE_MAC_TX_CNTRL1) &~ NPE_TX_CNTRL1_TX_EN); 1441164426Ssam 1442166339Skevlo sc->npe_watchdog_timer = 0; 1443164426Ssam ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 1444164426Ssam 1445164426Ssam callout_stop(&sc->tick_ch); 1446164426Ssam 1447164426Ssam npe_stopxmit(sc); 1448164426Ssam npe_stoprecv(sc); 1449164426Ssam /* XXX go into loopback & drain q's? */ 1450164426Ssam /* XXX but beware of disabling tx above */ 1451164426Ssam 1452164426Ssam /* 1453164426Ssam * The MAC core rx/tx disable may leave the MAC hardware in an 1454236987Simp * unpredictable state. A hw reset is executed before resetting 1455164426Ssam * all the MAC parameters to a known value. 1456164426Ssam */ 1457164426Ssam WR4(sc, NPE_MAC_CORE_CNTRL, NPE_CORE_RESET); 1458164426Ssam DELAY(NPE_MAC_RESET_DELAY); 1459164426Ssam WR4(sc, NPE_MAC_INT_CLK_THRESH, NPE_MAC_INT_CLK_THRESH_DEFAULT); 1460164426Ssam WR4(sc, NPE_MAC_CORE_CNTRL, NPE_CORE_MDC_EN); 1461164426Ssam} 1462164426Ssam 1463164426Ssamvoid 1464166339Skevlonpewatchdog(struct npe_softc *sc) 1465164426Ssam{ 1466166339Skevlo NPE_ASSERT_LOCKED(sc); 1467164426Ssam 1468166339Skevlo if (sc->npe_watchdog_timer == 0 || --sc->npe_watchdog_timer != 0) 1469166339Skevlo return; 1470166339Skevlo 1471166339Skevlo device_printf(sc->sc_dev, "watchdog timeout\n"); 1472166339Skevlo sc->sc_ifp->if_oerrors++; 1473166339Skevlo 1474164426Ssam npeinit_locked(sc); 1475164426Ssam} 1476164426Ssam 1477164426Ssamstatic int 1478164426Ssamnpeioctl(struct ifnet *ifp, u_long cmd, caddr_t data) 1479164426Ssam{ 1480164426Ssam struct npe_softc *sc = ifp->if_softc; 1481164426Ssam struct mii_data *mii; 1482164426Ssam struct ifreq *ifr = (struct ifreq *)data; 1483164426Ssam int error = 0; 1484164426Ssam#ifdef DEVICE_POLLING 1485164426Ssam int mask; 1486164426Ssam#endif 1487164426Ssam 1488164426Ssam switch (cmd) { 1489164426Ssam case SIOCSIFFLAGS: 1490164426Ssam NPE_LOCK(sc); 1491164426Ssam if ((ifp->if_flags & IFF_UP) == 0 && 1492164426Ssam ifp->if_drv_flags & IFF_DRV_RUNNING) { 1493164426Ssam ifp->if_drv_flags &= ~IFF_DRV_RUNNING; 1494164426Ssam npestop(sc); 1495164426Ssam } else { 1496164426Ssam /* reinitialize card on any parameter change */ 1497164426Ssam npeinit_locked(sc); 1498164426Ssam } 1499164426Ssam NPE_UNLOCK(sc); 1500164426Ssam break; 1501164426Ssam 1502164426Ssam case SIOCADDMULTI: 1503164426Ssam case SIOCDELMULTI: 1504164426Ssam /* update multicast filter list. */ 1505164426Ssam NPE_LOCK(sc); 1506164426Ssam npe_setmcast(sc); 1507164426Ssam NPE_UNLOCK(sc); 1508164426Ssam error = 0; 1509164426Ssam break; 1510164426Ssam 1511164426Ssam case SIOCSIFMEDIA: 1512164426Ssam case SIOCGIFMEDIA: 1513164426Ssam mii = device_get_softc(sc->sc_mii); 1514164426Ssam error = ifmedia_ioctl(ifp, ifr, &mii->mii_media, cmd); 1515164426Ssam break; 1516164426Ssam 1517164426Ssam#ifdef DEVICE_POLLING 1518164426Ssam case SIOCSIFCAP: 1519164426Ssam mask = ifp->if_capenable ^ ifr->ifr_reqcap; 1520164426Ssam if (mask & IFCAP_POLLING) { 1521164426Ssam if (ifr->ifr_reqcap & IFCAP_POLLING) { 1522164426Ssam error = ether_poll_register(npe_poll, ifp); 1523164426Ssam if (error) 1524164426Ssam return error; 1525164426Ssam NPE_LOCK(sc); 1526164426Ssam /* disable callbacks XXX txdone is shared */ 1527164426Ssam ixpqmgr_notify_disable(sc->rx_qid); 1528164426Ssam ixpqmgr_notify_disable(sc->tx_doneqid); 1529164426Ssam ifp->if_capenable |= IFCAP_POLLING; 1530164426Ssam NPE_UNLOCK(sc); 1531164426Ssam } else { 1532164426Ssam error = ether_poll_deregister(ifp); 1533164426Ssam /* NB: always enable qmgr callbacks */ 1534164426Ssam NPE_LOCK(sc); 1535164426Ssam /* enable qmgr callbacks */ 1536164426Ssam ixpqmgr_notify_enable(sc->rx_qid, 1537164426Ssam IX_QMGR_Q_SOURCE_ID_NOT_E); 1538164426Ssam ixpqmgr_notify_enable(sc->tx_doneqid, 1539164426Ssam IX_QMGR_Q_SOURCE_ID_NOT_E); 1540164426Ssam ifp->if_capenable &= ~IFCAP_POLLING; 1541164426Ssam NPE_UNLOCK(sc); 1542164426Ssam } 1543164426Ssam } 1544164426Ssam break; 1545164426Ssam#endif 1546164426Ssam default: 1547164426Ssam error = ether_ioctl(ifp, cmd, data); 1548164426Ssam break; 1549164426Ssam } 1550164426Ssam return error; 1551164426Ssam} 1552164426Ssam 1553164426Ssam/* 1554164426Ssam * Setup a traffic class -> rx queue mapping. 1555164426Ssam */ 1556164426Ssamstatic int 1557164426Ssamnpe_setrxqosentry(struct npe_softc *sc, int classix, int trafclass, int qid) 1558164426Ssam{ 1559164426Ssam uint32_t msg[2]; 1560164426Ssam 1561186352Ssam msg[0] = (NPE_SETRXQOSENTRY << 24) | (sc->sc_npeid << 20) | classix; 1562164426Ssam msg[1] = (trafclass << 24) | (1 << 23) | (qid << 16) | (qid << 4); 1563186352Ssam return ixpnpe_sendandrecvmsg_sync(sc->sc_npe, msg, msg); 1564164426Ssam} 1565164426Ssam 1566186352Ssamstatic int 1567194321Ssamnpe_setportaddress(struct npe_softc *sc, const uint8_t mac[ETHER_ADDR_LEN]) 1568194321Ssam{ 1569194321Ssam uint32_t msg[2]; 1570194321Ssam 1571194321Ssam msg[0] = (NPE_SETPORTADDRESS << 24) 1572194321Ssam | (sc->sc_npeid << 20) 1573194321Ssam | (mac[0] << 8) 1574194321Ssam | (mac[1] << 0); 1575194321Ssam msg[1] = (mac[2] << 24) 1576194321Ssam | (mac[3] << 16) 1577194321Ssam | (mac[4] << 8) 1578194321Ssam | (mac[5] << 0); 1579194321Ssam return ixpnpe_sendandrecvmsg_sync(sc->sc_npe, msg, msg); 1580194321Ssam} 1581194321Ssam 1582194321Ssamstatic int 1583186352Ssamnpe_setfirewallmode(struct npe_softc *sc, int onoff) 1584186352Ssam{ 1585186352Ssam uint32_t msg[2]; 1586186352Ssam 1587186352Ssam /* XXX honor onoff */ 1588186352Ssam msg[0] = (NPE_SETFIREWALLMODE << 24) | (sc->sc_npeid << 20); 1589186352Ssam msg[1] = 0; 1590186352Ssam return ixpnpe_sendandrecvmsg_sync(sc->sc_npe, msg, msg); 1591186352Ssam} 1592186352Ssam 1593164426Ssam/* 1594164426Ssam * Update and reset the statistics in the NPE. 1595164426Ssam */ 1596164426Ssamstatic int 1597164426Ssamnpe_updatestats(struct npe_softc *sc) 1598164426Ssam{ 1599164426Ssam uint32_t msg[2]; 1600164426Ssam 1601164426Ssam msg[0] = NPE_RESETSTATS << NPE_MAC_MSGID_SHL; 1602164426Ssam msg[1] = sc->sc_stats_phys; /* physical address of stat block */ 1603186352Ssam return ixpnpe_sendmsg_async(sc->sc_npe, msg); 1604164426Ssam} 1605164426Ssam 1606164426Ssam#if 0 1607164426Ssam/* 1608164426Ssam * Get the current statistics block. 1609164426Ssam */ 1610164426Ssamstatic int 1611164426Ssamnpe_getstats(struct npe_softc *sc) 1612164426Ssam{ 1613164426Ssam uint32_t msg[2]; 1614164426Ssam 1615164426Ssam msg[0] = NPE_GETSTATS << NPE_MAC_MSGID_SHL; 1616164426Ssam msg[1] = sc->sc_stats_phys; /* physical address of stat block */ 1617164426Ssam return ixpnpe_sendandrecvmsg(sc->sc_npe, msg, msg); 1618164426Ssam} 1619164426Ssam 1620164426Ssam/* 1621164426Ssam * Query the image id of the loaded firmware. 1622164426Ssam */ 1623164426Ssamstatic uint32_t 1624164426Ssamnpe_getimageid(struct npe_softc *sc) 1625164426Ssam{ 1626164426Ssam uint32_t msg[2]; 1627164426Ssam 1628164426Ssam msg[0] = NPE_GETSTATUS << NPE_MAC_MSGID_SHL; 1629164426Ssam msg[1] = 0; 1630186352Ssam return ixpnpe_sendandrecvmsg_sync(sc->sc_npe, msg, msg) == 0 ? msg[1] : 0; 1631164426Ssam} 1632164426Ssam 1633164426Ssam/* 1634164426Ssam * Enable/disable loopback. 1635164426Ssam */ 1636164426Ssamstatic int 1637164426Ssamnpe_setloopback(struct npe_softc *sc, int ena) 1638164426Ssam{ 1639164426Ssam uint32_t msg[2]; 1640164426Ssam 1641164426Ssam msg[0] = (NPE_SETLOOPBACK << NPE_MAC_MSGID_SHL) | (ena != 0); 1642164426Ssam msg[1] = 0; 1643186352Ssam return ixpnpe_sendandrecvmsg_sync(sc->sc_npe, msg, msg); 1644164426Ssam} 1645164426Ssam#endif 1646164426Ssam 1647164426Ssamstatic void 1648164426Ssamnpe_child_detached(device_t dev, device_t child) 1649164426Ssam{ 1650164426Ssam struct npe_softc *sc; 1651164426Ssam 1652164426Ssam sc = device_get_softc(dev); 1653164426Ssam if (child == sc->sc_mii) 1654164426Ssam sc->sc_mii = NULL; 1655164426Ssam} 1656164426Ssam 1657164426Ssam/* 1658164426Ssam * MII bus support routines. 1659164426Ssam */ 1660186352Ssam#define MII_RD4(sc, reg) bus_space_read_4(sc->sc_iot, sc->sc_miih, reg) 1661186352Ssam#define MII_WR4(sc, reg, v) \ 1662186352Ssam bus_space_write_4(sc->sc_iot, sc->sc_miih, reg, v) 1663186352Ssam 1664164426Ssamstatic uint32_t 1665164426Ssamnpe_mii_mdio_read(struct npe_softc *sc, int reg) 1666164426Ssam{ 1667164426Ssam uint32_t v; 1668164426Ssam 1669164426Ssam /* NB: registers are known to be sequential */ 1670164426Ssam v = (MII_RD4(sc, reg+0) & 0xff) << 0; 1671164426Ssam v |= (MII_RD4(sc, reg+4) & 0xff) << 8; 1672164426Ssam v |= (MII_RD4(sc, reg+8) & 0xff) << 16; 1673164426Ssam v |= (MII_RD4(sc, reg+12) & 0xff) << 24; 1674164426Ssam return v; 1675164426Ssam} 1676164426Ssam 1677164426Ssamstatic void 1678164426Ssamnpe_mii_mdio_write(struct npe_softc *sc, int reg, uint32_t cmd) 1679164426Ssam{ 1680164426Ssam /* NB: registers are known to be sequential */ 1681164426Ssam MII_WR4(sc, reg+0, cmd & 0xff); 1682164426Ssam MII_WR4(sc, reg+4, (cmd >> 8) & 0xff); 1683164426Ssam MII_WR4(sc, reg+8, (cmd >> 16) & 0xff); 1684164426Ssam MII_WR4(sc, reg+12, (cmd >> 24) & 0xff); 1685164426Ssam} 1686164426Ssam 1687164426Ssamstatic int 1688164426Ssamnpe_mii_mdio_wait(struct npe_softc *sc) 1689164426Ssam{ 1690164426Ssam uint32_t v; 1691164426Ssam int i; 1692164426Ssam 1693186352Ssam /* NB: typically this takes 25-30 trips */ 1694186352Ssam for (i = 0; i < 1000; i++) { 1695164426Ssam v = npe_mii_mdio_read(sc, NPE_MAC_MDIO_CMD); 1696164426Ssam if ((v & NPE_MII_GO) == 0) 1697164426Ssam return 1; 1698186352Ssam DELAY(1); 1699164426Ssam } 1700186352Ssam device_printf(sc->sc_dev, "%s: timeout after ~1ms, cmd 0x%x\n", 1701186352Ssam __func__, v); 1702164426Ssam return 0; /* NB: timeout */ 1703164426Ssam} 1704164426Ssam 1705164426Ssamstatic int 1706164426Ssamnpe_miibus_readreg(device_t dev, int phy, int reg) 1707164426Ssam{ 1708164426Ssam struct npe_softc *sc = device_get_softc(dev); 1709164426Ssam uint32_t v; 1710164426Ssam 1711186352Ssam v = (phy << NPE_MII_ADDR_SHL) | (reg << NPE_MII_REG_SHL) | NPE_MII_GO; 1712164426Ssam npe_mii_mdio_write(sc, NPE_MAC_MDIO_CMD, v); 1713164426Ssam if (npe_mii_mdio_wait(sc)) 1714164426Ssam v = npe_mii_mdio_read(sc, NPE_MAC_MDIO_STS); 1715164426Ssam else 1716164426Ssam v = 0xffff | NPE_MII_READ_FAIL; 1717164426Ssam return (v & NPE_MII_READ_FAIL) ? 0xffff : (v & 0xffff); 1718164426Ssam} 1719164426Ssam 1720194015Savgstatic int 1721164426Ssamnpe_miibus_writereg(device_t dev, int phy, int reg, int data) 1722164426Ssam{ 1723164426Ssam struct npe_softc *sc = device_get_softc(dev); 1724164426Ssam uint32_t v; 1725164426Ssam 1726164426Ssam v = (phy << NPE_MII_ADDR_SHL) | (reg << NPE_MII_REG_SHL) 1727164426Ssam | data | NPE_MII_WRITE 1728164426Ssam | NPE_MII_GO; 1729164426Ssam npe_mii_mdio_write(sc, NPE_MAC_MDIO_CMD, v); 1730164426Ssam /* XXX complain about timeout */ 1731164426Ssam (void) npe_mii_mdio_wait(sc); 1732194015Savg return (0); 1733164426Ssam} 1734164426Ssam 1735164426Ssamstatic void 1736164426Ssamnpe_miibus_statchg(device_t dev) 1737164426Ssam{ 1738164426Ssam struct npe_softc *sc = device_get_softc(dev); 1739164426Ssam struct mii_data *mii = device_get_softc(sc->sc_mii); 1740164426Ssam uint32_t tx1, rx1; 1741164426Ssam 1742164426Ssam /* sync MAC duplex state */ 1743164426Ssam tx1 = RD4(sc, NPE_MAC_TX_CNTRL1); 1744164426Ssam rx1 = RD4(sc, NPE_MAC_RX_CNTRL1); 1745164426Ssam if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX) { 1746164426Ssam tx1 &= ~NPE_TX_CNTRL1_DUPLEX; 1747164426Ssam rx1 |= NPE_RX_CNTRL1_PAUSE_EN; 1748164426Ssam } else { 1749164426Ssam tx1 |= NPE_TX_CNTRL1_DUPLEX; 1750164426Ssam rx1 &= ~NPE_RX_CNTRL1_PAUSE_EN; 1751164426Ssam } 1752164426Ssam WR4(sc, NPE_MAC_RX_CNTRL1, rx1); 1753164426Ssam WR4(sc, NPE_MAC_TX_CNTRL1, tx1); 1754164426Ssam} 1755164426Ssam 1756164426Ssamstatic device_method_t npe_methods[] = { 1757164426Ssam /* Device interface */ 1758164426Ssam DEVMETHOD(device_probe, npe_probe), 1759164426Ssam DEVMETHOD(device_attach, npe_attach), 1760164426Ssam DEVMETHOD(device_detach, npe_detach), 1761164426Ssam 1762164426Ssam /* Bus interface */ 1763164426Ssam DEVMETHOD(bus_child_detached, npe_child_detached), 1764164426Ssam 1765164426Ssam /* MII interface */ 1766164426Ssam DEVMETHOD(miibus_readreg, npe_miibus_readreg), 1767164426Ssam DEVMETHOD(miibus_writereg, npe_miibus_writereg), 1768164426Ssam DEVMETHOD(miibus_statchg, npe_miibus_statchg), 1769164426Ssam 1770164426Ssam { 0, 0 } 1771164426Ssam}; 1772164426Ssam 1773164426Ssamstatic driver_t npe_driver = { 1774164426Ssam "npe", 1775164426Ssam npe_methods, 1776164426Ssam sizeof(struct npe_softc), 1777164426Ssam}; 1778164426Ssam 1779164426SsamDRIVER_MODULE(npe, ixp, npe_driver, npe_devclass, 0, 0); 1780164426SsamDRIVER_MODULE(miibus, npe, miibus_driver, miibus_devclass, 0, 0); 1781164426SsamMODULE_DEPEND(npe, ixpqmgr, 1, 1, 1); 1782164426SsamMODULE_DEPEND(npe, miibus, 1, 1, 1); 1783164426SsamMODULE_DEPEND(npe, ether, 1, 1, 1); 1784