if_vx.c revision 139749
1139749Simp/*- 219410Sguido * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> 319410Sguido * All rights reserved. 419410Sguido * 519410Sguido * Redistribution and use in source and binary forms, with or without 619410Sguido * modification, are permitted provided that the following conditions 719410Sguido * are met: 819410Sguido * 1. Redistributions of source code must retain the above copyright 919410Sguido * notice, this list of conditions and the following disclaimer. 1019410Sguido * 2. Redistributions in binary form must reproduce the above copyright 1119410Sguido * notice, this list of conditions and the following disclaimer in the 1219410Sguido * documentation and/or other materials provided with the distribution. 1319410Sguido * 3. All advertising materials mentioning features or use of this software 1419410Sguido * must display the following acknowledgement: 1519410Sguido * This product includes software developed by Herb Peyerl. 1619410Sguido * 4. The name of Herb Peyerl may not be used to endorse or promote products 1719410Sguido * derived from this software without specific prior written permission. 1819410Sguido * 1919410Sguido * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 2019410Sguido * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 2119410Sguido * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 2219410Sguido * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2319410Sguido * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 2419410Sguido * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2519410Sguido * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2619410Sguido * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 2719410Sguido * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 2819410Sguido * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2919410Sguido * 3033707Sgpalmer * 3119410Sguido */ 3219410Sguido 33119418Sobrien#include <sys/cdefs.h> 34119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/vx/if_vx.c 139749 2005-01-06 01:43:34Z imp $"); 35119418Sobrien 3619410Sguido/* 3719410Sguido * Created from if_ep.c driver by Fred Gray (fgray@rice.edu) to support 3819410Sguido * the 3c590 family. 3919410Sguido */ 4019410Sguido 4119410Sguido/* 4219410Sguido * Modified from the FreeBSD 1.1.5.1 version by: 4319410Sguido * Andres Vega Garcia 4419410Sguido * INRIA - Sophia Antipolis, France 4519410Sguido * avega@sophia.inria.fr 4619410Sguido */ 4719410Sguido 4819410Sguido/* 4919410Sguido * Promiscuous mode added and interrupt logic slightly changed 5019410Sguido * to reduce the number of adapter failures. Transceiver select 5119410Sguido * logic changed to use value from EEPROM. Autoconfiguration 5219410Sguido * features added. 5319410Sguido * Done by: 5419410Sguido * Serge Babkin 5519410Sguido * Chelindbank (Chelyabinsk, Russia) 5619410Sguido * babkin@hq.icb.chel.su 5719410Sguido */ 5819410Sguido 5919410Sguido 6019410Sguido#include <sys/param.h> 6119410Sguido#include <sys/systm.h> 6224204Sbde#include <sys/sockio.h> 6326640Sbde#include <sys/malloc.h> 6419410Sguido#include <sys/mbuf.h> 6519410Sguido#include <sys/socket.h> 6619410Sguido 6719410Sguido#include <net/if.h> 6819410Sguido 6932350Seivind#include <net/ethernet.h> 7032350Seivind#include <net/if_arp.h> 7119410Sguido 7268417Swpaul#include <machine/bus_pio.h> 7368417Swpaul#include <machine/bus.h> 7468417Swpaul 75121816Sbrooks#include <sys/bus.h> 76121816Sbrooks 7719410Sguido#include <net/bpf.h> 7819410Sguido 7919410Sguido#include <dev/vx/if_vxreg.h> 80121491Simp#include <dev/vx/if_vxvar.h> 8119410Sguido 8219410Sguido#define ETHER_MAX_LEN 1518 8319410Sguido#define ETHER_ADDR_LEN 6 8469732Swpaul#define ETHER_ALIGN 2 8519410Sguido 8619410Sguidostatic struct connector_entry { 87133980Sgibbs int bit; 88133980Sgibbs char *name; 8920096Sguido} conn_tab[VX_CONNECTORS] = { 90133980Sgibbs 9119410Sguido#define CONNECTOR_UTP 0 92133980Sgibbs { 93133980Sgibbs 0x08, "utp" 94133980Sgibbs }, 9519410Sguido#define CONNECTOR_AUI 1 96133980Sgibbs { 97133980Sgibbs 0x20, "aui" 98133980Sgibbs }, 9919410Sguido/* dummy */ 100133980Sgibbs { 101133980Sgibbs 0, "???" 102133980Sgibbs }, 10319410Sguido#define CONNECTOR_BNC 3 104133980Sgibbs { 105133980Sgibbs 0x10, "bnc" 106133980Sgibbs }, 10719410Sguido#define CONNECTOR_TX 4 108133980Sgibbs { 109133980Sgibbs 0x02, "tx" 110133980Sgibbs }, 11119410Sguido#define CONNECTOR_FX 5 112133980Sgibbs { 113133980Sgibbs 0x04, "fx" 114133980Sgibbs }, 11519410Sguido#define CONNECTOR_MII 6 116133980Sgibbs { 117133980Sgibbs 0x40, "mii" 118133980Sgibbs }, 119133980Sgibbs { 120133980Sgibbs 0, "???" 121133980Sgibbs } 12219410Sguido}; 12319410Sguido 12492739Salfred/* int vxattach(struct vx_softc *); */ 12592739Salfredstatic void vxtxstat(struct vx_softc *); 12692739Salfredstatic int vxstatus(struct vx_softc *); 12792739Salfredstatic void vxinit(void *); 128133980Sgibbsstatic int vxioctl(struct ifnet *, u_long, caddr_t); 129133980Sgibbsstatic void vxstart(struct ifnet *); 13092739Salfredstatic void vxwatchdog(struct ifnet *); 13192739Salfredstatic void vxreset(struct vx_softc *); 13292739Salfredstatic void vxread(struct vx_softc *); 13392739Salfredstatic struct mbuf *vxget(struct vx_softc *, u_int); 13492739Salfredstatic void vxmbuffill(void *); 13592739Salfredstatic void vxmbufempty(struct vx_softc *); 13692739Salfredstatic void vxsetfilter(struct vx_softc *); 13792739Salfredstatic void vxgetlink(struct vx_softc *); 13892739Salfredstatic void vxsetlink(struct vx_softc *); 13919410Sguido 14019410Sguidoint 141133980Sgibbsvxattach(device_t dev) 14219410Sguido{ 143133980Sgibbs struct vx_softc *sc = device_get_softc(dev); 144133980Sgibbs struct ifnet *ifp = &sc->arpcom.ac_if; 145133980Sgibbs int i; 14619410Sguido 147133980Sgibbs callout_handle_init(&sc->ch); 148133980Sgibbs GO_WINDOW(0); 149133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, GLOBAL_RESET); 150133980Sgibbs VX_BUSY_WAIT; 15119410Sguido 152133980Sgibbs vxgetlink(sc); 15319410Sguido 154133980Sgibbs /* 155133980Sgibbs * Read the station address from the eeprom 156133980Sgibbs */ 157133980Sgibbs GO_WINDOW(0); 158133980Sgibbs for (i = 0; i < 3; i++) { 159133980Sgibbs int x; 16019410Sguido 161133980Sgibbs if (vxbusyeeprom(sc)) 162133980Sgibbs return 0; 163133980Sgibbs CSR_WRITE_2(sc, VX_W0_EEPROM_COMMAND, EEPROM_CMD_RD 164133980Sgibbs | (EEPROM_OEM_ADDR0 + i)); 165133980Sgibbs if (vxbusyeeprom(sc)) 166133980Sgibbs return 0; 167133980Sgibbs x = CSR_READ_2(sc, VX_W0_EEPROM_DATA); 168133980Sgibbs sc->arpcom.ac_enaddr[(i << 1)] = x >> 8; 169133980Sgibbs sc->arpcom.ac_enaddr[(i << 1) + 1] = x; 170133980Sgibbs } 17119410Sguido 172133980Sgibbs if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 173133980Sgibbs ifp->if_mtu = ETHERMTU; 174133980Sgibbs ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 175133980Sgibbs ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST | 176133980Sgibbs IFF_NEEDSGIANT; 177133980Sgibbs ifp->if_start = vxstart; 178133980Sgibbs ifp->if_ioctl = vxioctl; 179133980Sgibbs ifp->if_init = vxinit; 180133980Sgibbs ifp->if_watchdog = vxwatchdog; 181133980Sgibbs ifp->if_softc = sc; 18219410Sguido 183133980Sgibbs ether_ifattach(ifp, sc->arpcom.ac_enaddr); 18419410Sguido 185133980Sgibbs sc->tx_start_thresh = 20; /* probably a good starting point. */ 18619410Sguido 187133980Sgibbs vxstop(sc); 188133980Sgibbs 189133980Sgibbs return 1; 19019410Sguido} 19119410Sguido 19219410Sguido/* 19319410Sguido * The order in here seems important. Otherwise we may not receive 19419410Sguido * interrupts. ?! 19519410Sguido */ 19619410Sguidostatic void 197133980Sgibbsvxinit(void *xsc) 19819410Sguido{ 199133980Sgibbs struct vx_softc *sc = (struct vx_softc *)xsc; 200133980Sgibbs struct ifnet *ifp = &sc->arpcom.ac_if; 201133980Sgibbs int i; 20219410Sguido 203133980Sgibbs VX_BUSY_WAIT; 20419410Sguido 205133980Sgibbs GO_WINDOW(2); 20619410Sguido 207133980Sgibbs for (i = 0; i < 6; i++) /* Reload the ether_addr. */ 208133980Sgibbs CSR_WRITE_1(sc, VX_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); 20919410Sguido 210133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_RESET); 211133980Sgibbs VX_BUSY_WAIT; 212133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_RESET); 213133980Sgibbs VX_BUSY_WAIT; 21419410Sguido 215133980Sgibbs GO_WINDOW(1); /* Window 1 is operating window */ 216133980Sgibbs for (i = 0; i < 31; i++) 217133980Sgibbs CSR_READ_1(sc, VX_W1_TX_STATUS); 21819410Sguido 219133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RD_0_MASK | S_CARD_FAILURE | 220133980Sgibbs S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 221133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_INTR_MASK | S_CARD_FAILURE | 222133980Sgibbs S_RX_COMPLETE | S_TX_COMPLETE | S_TX_AVAIL); 22319410Sguido 224133980Sgibbs /* 225133980Sgibbs * Attempt to get rid of any stray interrupts that occured during 226133980Sgibbs * configuration. On the i386 this isn't possible because one may 227133980Sgibbs * already be queued. However, a single stray interrupt is 228133980Sgibbs * unimportant. 229133980Sgibbs */ 230133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, ACK_INTR | 0xff); 23119410Sguido 232133980Sgibbs vxsetfilter(sc); 233133980Sgibbs vxsetlink(sc); 23419410Sguido 235133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_ENABLE); 236133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_ENABLE); 23719410Sguido 238133980Sgibbs vxmbuffill((caddr_t) sc); 23919410Sguido 240133980Sgibbs /* Interface is now `running', with no output active. */ 241133980Sgibbs ifp->if_flags |= IFF_RUNNING; 242133980Sgibbs ifp->if_flags &= ~IFF_OACTIVE; 24319410Sguido 244133980Sgibbs /* Attempt to start output, if any. */ 245133980Sgibbs vxstart(ifp); 24619410Sguido} 24719410Sguido 24819410Sguidostatic void 249133980Sgibbsvxsetfilter(struct vx_softc *sc) 25019410Sguido{ 251133980Sgibbs register struct ifnet *ifp = &sc->arpcom.ac_if; 25219410Sguido 253133980Sgibbs GO_WINDOW(1); /* Window 1 is operating window */ 254133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RX_FILTER | 255133980Sgibbs FIL_INDIVIDUAL | FIL_BRDCST | FIL_MULTICAST | 256133980Sgibbs ((ifp->if_flags & IFF_PROMISC) ? FIL_PROMISC : 0)); 257133980Sgibbs} 258133980Sgibbs 259133980Sgibbsstatic void 260133980Sgibbsvxgetlink(struct vx_softc *sc) 26119410Sguido{ 262133980Sgibbs int n, k; 26319410Sguido 264133980Sgibbs GO_WINDOW(3); 265133980Sgibbs sc->vx_connectors = CSR_READ_2(sc, VX_W3_RESET_OPT) & 0x7f; 266133980Sgibbs for (n = 0, k = 0; k < VX_CONNECTORS; k++) { 267133980Sgibbs if (sc->vx_connectors & conn_tab[k].bit) { 268133980Sgibbs if (n > 0) 269133980Sgibbs printf("/"); 270133980Sgibbs printf("%s", conn_tab[k].name); 271133980Sgibbs n++; 272133980Sgibbs } 27319410Sguido } 274133980Sgibbs if (sc->vx_connectors == 0) { 275133980Sgibbs printf("no connectors!"); 276133980Sgibbs return; 277133980Sgibbs } 278133980Sgibbs GO_WINDOW(3); 279133980Sgibbs sc->vx_connector = 280133980Sgibbs (CSR_READ_4(sc, VX_W3_INTERNAL_CFG) & INTERNAL_CONNECTOR_MASK) 281133980Sgibbs >> INTERNAL_CONNECTOR_BITS; 282133980Sgibbs if (sc->vx_connector & 0x10) { 283133980Sgibbs sc->vx_connector &= 0x0f; 284133980Sgibbs printf("[*%s*]", conn_tab[(int)sc->vx_connector].name); 285133980Sgibbs printf(": disable 'auto select' with DOS util!"); 286133980Sgibbs } else { 287133980Sgibbs printf("[*%s*]", conn_tab[(int)sc->vx_connector].name); 288133980Sgibbs } 28919410Sguido} 29019410Sguido 291133980Sgibbsstatic void 292133980Sgibbsvxsetlink(struct vx_softc *sc) 293133980Sgibbs{ 294133980Sgibbs register struct ifnet *ifp = &sc->arpcom.ac_if; 295133980Sgibbs int i, j, k; 296133980Sgibbs char *reason, *warning; 297133980Sgibbs static int prev_flags; 298133980Sgibbs static char prev_conn = -1; 29919410Sguido 300133980Sgibbs if (prev_conn == -1) 301133980Sgibbs prev_conn = sc->vx_connector; 30220096Sguido 303133980Sgibbs /* 304133980Sgibbs * S.B. 305133980Sgibbs * 306133980Sgibbs * Now behavior was slightly changed: 307133980Sgibbs * 308133980Sgibbs * if any of flags link[0-2] is used and its connector is 309133980Sgibbs * physically present the following connectors are used: 310133980Sgibbs * 311133980Sgibbs * link0 - AUI * highest precedence 312133980Sgibbs * link1 - BNC 313133980Sgibbs * link2 - UTP * lowest precedence 314133980Sgibbs * 315133980Sgibbs * If none of them is specified then 316133980Sgibbs * connector specified in the EEPROM is used 317133980Sgibbs * (if present on card or UTP if not). 318133980Sgibbs */ 319133980Sgibbs i = sc->vx_connector; /* default in EEPROM */ 320133980Sgibbs reason = "default"; 321133980Sgibbs warning = 0; 32220096Sguido 323133980Sgibbs if (ifp->if_flags & IFF_LINK0) { 324133980Sgibbs if (sc->vx_connectors & conn_tab[CONNECTOR_AUI].bit) { 325133980Sgibbs i = CONNECTOR_AUI; 326133980Sgibbs reason = "link0"; 327133980Sgibbs } else { 328133980Sgibbs warning = "aui not present! (link0)"; 329133980Sgibbs } 330133980Sgibbs } else if (ifp->if_flags & IFF_LINK1) { 331133980Sgibbs if (sc->vx_connectors & conn_tab[CONNECTOR_BNC].bit) { 332133980Sgibbs i = CONNECTOR_BNC; 333133980Sgibbs reason = "link1"; 334133980Sgibbs } else { 335133980Sgibbs warning = "bnc not present! (link1)"; 336133980Sgibbs } 337133980Sgibbs } else if (ifp->if_flags & IFF_LINK2) { 338133980Sgibbs if (sc->vx_connectors & conn_tab[CONNECTOR_UTP].bit) { 339133980Sgibbs i = CONNECTOR_UTP; 340133980Sgibbs reason = "link2"; 341133980Sgibbs } else { 342133980Sgibbs warning = "utp not present! (link2)"; 343133980Sgibbs } 344133980Sgibbs } else if ((sc->vx_connectors & conn_tab[(int)sc->vx_connector].bit) == 0) { 345133980Sgibbs warning = "strange connector type in EEPROM."; 346133980Sgibbs reason = "forced"; 347133980Sgibbs i = CONNECTOR_UTP; 34820096Sguido } 349133980Sgibbs /* Avoid unnecessary message. */ 350133980Sgibbs k = (prev_flags ^ ifp->if_flags) & (IFF_LINK0 | IFF_LINK1 | IFF_LINK2); 351133980Sgibbs if ((k != 0) || (prev_conn != i)) { 352133980Sgibbs if (warning != 0) { 353133980Sgibbs printf("vx%d: warning: %s\n", sc->unit, warning); 354133980Sgibbs } 355133980Sgibbs printf("vx%d: selected %s. (%s)\n", 356133980Sgibbs sc->unit, conn_tab[i].name, reason); 35720096Sguido } 358133980Sgibbs /* Set the selected connector. */ 359133980Sgibbs GO_WINDOW(3); 360133980Sgibbs j = CSR_READ_4(sc, VX_W3_INTERNAL_CFG) & ~INTERNAL_CONNECTOR_MASK; 361133980Sgibbs CSR_WRITE_4(sc, VX_W3_INTERNAL_CFG, j | (i << INTERNAL_CONNECTOR_BITS)); 36220096Sguido 363133980Sgibbs /* First, disable all. */ 364133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, STOP_TRANSCEIVER); 36520096Sguido DELAY(800); 36620096Sguido GO_WINDOW(4); 367133980Sgibbs CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, 0); 36820096Sguido 369133980Sgibbs /* Second, enable the selected one. */ 370133980Sgibbs switch (i) { 371133980Sgibbs case CONNECTOR_UTP: 372133980Sgibbs GO_WINDOW(4); 373133980Sgibbs CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, ENABLE_UTP); 374133980Sgibbs break; 375133980Sgibbs case CONNECTOR_BNC: 376133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, START_TRANSCEIVER); 377133980Sgibbs DELAY(800); 378133980Sgibbs break; 379133980Sgibbs case CONNECTOR_TX: 380133980Sgibbs case CONNECTOR_FX: 381133980Sgibbs GO_WINDOW(4); 382133980Sgibbs CSR_WRITE_2(sc, VX_W4_MEDIA_TYPE, LINKBEAT_ENABLE); 383133980Sgibbs break; 384133980Sgibbs default: /* AUI and MII fall here */ 385133980Sgibbs break; 386133980Sgibbs } 387133980Sgibbs GO_WINDOW(1); 388133980Sgibbs 389133980Sgibbs prev_flags = ifp->if_flags; 390133980Sgibbs prev_conn = i; 39119410Sguido} 39219410Sguido 39319410Sguidostatic void 394133980Sgibbsvxstart(struct ifnet *ifp) 39519410Sguido{ 396133980Sgibbs register struct vx_softc *sc = ifp->if_softc; 397133980Sgibbs register struct mbuf *m; 398133980Sgibbs int sh, len, pad; 39919410Sguido 400133980Sgibbs /* Don't transmit if interface is busy or not running */ 401133980Sgibbs if ((sc->arpcom.ac_if.if_flags & 402133980Sgibbs (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) 403133980Sgibbs return; 40419410Sguido 40519410Sguidostartagain: 406133980Sgibbs /* Sneak a peek at the next packet */ 407133980Sgibbs m = ifp->if_snd.ifq_head; 408133980Sgibbs if (m == NULL) { 409133980Sgibbs return; 410133980Sgibbs } 411133980Sgibbs /* We need to use m->m_pkthdr.len, so require the header */ 412133980Sgibbs M_ASSERTPKTHDR(m); 413133980Sgibbs len = m->m_pkthdr.len; 41419410Sguido 415133980Sgibbs pad = (4 - len) & 3; 41619410Sguido 417133980Sgibbs /* 418133980Sgibbs * The 3c509 automatically pads short packets to minimum ethernet 419133980Sgibbs * length, but we drop packets that are too large. Perhaps we should 420133980Sgibbs * truncate them instead? 421133980Sgibbs */ 422133980Sgibbs if (len + pad > ETHER_MAX_LEN) { 423133980Sgibbs /* packet is obviously too large: toss it */ 424133980Sgibbs ++ifp->if_oerrors; 425133980Sgibbs IF_DEQUEUE(&ifp->if_snd, m); 426133980Sgibbs m_freem(m); 427133980Sgibbs goto readcheck; 428133980Sgibbs } 429133980Sgibbs VX_BUSY_WAIT; 430133980Sgibbs if (CSR_READ_2(sc, VX_W1_FREE_TX) < len + pad + 4) { 431133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, 432133980Sgibbs SET_TX_AVAIL_THRESH | ((len + pad + 4) >> 2)); 433133980Sgibbs /* not enough room in FIFO - make sure */ 434133980Sgibbs if (CSR_READ_2(sc, VX_W1_FREE_TX) < len + pad + 4) { 435133980Sgibbs ifp->if_flags |= IFF_OACTIVE; 436133980Sgibbs ifp->if_timer = 1; 437133980Sgibbs return; 438133980Sgibbs } 439133980Sgibbs } 440133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_TX_AVAIL_THRESH | (8188 >> 2)); 44190227Sdillon IF_DEQUEUE(&ifp->if_snd, m); 442133980Sgibbs if (m == NULL) /* not really needed */ 443133980Sgibbs return; 44419410Sguido 445133980Sgibbs VX_BUSY_WAIT; 446133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_TX_START_THRESH | 447133980Sgibbs ((len / 4 + sc->tx_start_thresh) >> 2)); 44819410Sguido 449133980Sgibbs BPF_MTAP(&sc->arpcom.ac_if, m); 45019410Sguido 451133980Sgibbs /* 452133980Sgibbs * Do the output at splhigh() so that an interrupt from another device 453133980Sgibbs * won't cause a FIFO underrun. 454133980Sgibbs */ 455133980Sgibbs sh = splhigh(); 45619410Sguido 457133980Sgibbs CSR_WRITE_4(sc, VX_W1_TX_PIO_WR_1, len | TX_INDICATE); 45819410Sguido 459133980Sgibbs while (m) { 460133980Sgibbs if (m->m_len > 3) 461133980Sgibbs bus_space_write_multi_4(sc->bst, sc->bsh, 462133980Sgibbs VX_W1_TX_PIO_WR_1, (u_int32_t *)mtod(m, caddr_t), 463133980Sgibbs m->m_len / 4); 464133980Sgibbs if (m->m_len & 3) 465133980Sgibbs bus_space_write_multi_1(sc->bst, sc->bsh, 466133980Sgibbs VX_W1_TX_PIO_WR_1, 467133980Sgibbs mtod(m, caddr_t) + (m->m_len & ~3), m->m_len & 3); 468133980Sgibbs m = m_free(m); 469133980Sgibbs } 470133980Sgibbs while (pad--) 471133980Sgibbs CSR_WRITE_1(sc, VX_W1_TX_PIO_WR_1, 0); /* Padding */ 47219410Sguido 473133980Sgibbs splx(sh); 47419410Sguido 475133980Sgibbs ++ifp->if_opackets; 476133980Sgibbs ifp->if_timer = 1; 47719410Sguido 47819410Sguidoreadcheck: 479133980Sgibbs if ((CSR_READ_2(sc, VX_W1_RX_STATUS) & ERR_INCOMPLETE) == 0) { 480133980Sgibbs /* We received a complete packet. */ 481133980Sgibbs 482133980Sgibbs if ((CSR_READ_2(sc, VX_STATUS) & S_INTR_LATCH) == 0) { 483133980Sgibbs /* 484133980Sgibbs * No interrupt, read the packet and continue 485133980Sgibbs * Is this supposed to happen? Is my motherboard 486133980Sgibbs * completely busted? 487133980Sgibbs */ 488133980Sgibbs vxread(sc); 489133980Sgibbs } else 490133980Sgibbs /* 491133980Sgibbs * Got an interrupt, return so that it gets 492133980Sgibbs * serviced. 493133980Sgibbs */ 494133980Sgibbs return; 495133980Sgibbs } else { 496133980Sgibbs /* Check if we are stuck and reset [see XXX comment] */ 497133980Sgibbs if (vxstatus(sc)) { 498133980Sgibbs if (ifp->if_flags & IFF_DEBUG) 499133980Sgibbs if_printf(ifp, "adapter reset\n"); 500133980Sgibbs vxreset(sc); 501133980Sgibbs } 50219410Sguido } 50319410Sguido 504133980Sgibbs goto startagain; 50519410Sguido} 50619410Sguido 50719410Sguido/* 50819410Sguido * XXX: The 3c509 card can get in a mode where both the fifo status bit 50919410Sguido * FIFOS_RX_OVERRUN and the status bit ERR_INCOMPLETE are set 51019410Sguido * We detect this situation and we reset the adapter. 51119410Sguido * It happens at times when there is a lot of broadcast traffic 51219410Sguido * on the cable (once in a blue moon). 51319410Sguido */ 51419410Sguidostatic int 515133980Sgibbsvxstatus(struct vx_softc *sc) 51619410Sguido{ 517133980Sgibbs int fifost; 51819410Sguido 519133980Sgibbs /* 520133980Sgibbs * Check the FIFO status and act accordingly 521133980Sgibbs */ 522133980Sgibbs GO_WINDOW(4); 523133980Sgibbs fifost = CSR_READ_2(sc, VX_W4_FIFO_DIAG); 524133980Sgibbs GO_WINDOW(1); 52519410Sguido 526133980Sgibbs if (fifost & FIFOS_RX_UNDERRUN) { 527133980Sgibbs if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 528133980Sgibbs printf("vx%d: RX underrun\n", sc->unit); 529133980Sgibbs vxreset(sc); 530133980Sgibbs return 0; 531133980Sgibbs } 532133980Sgibbs if (fifost & FIFOS_RX_STATUS_OVERRUN) { 533133980Sgibbs if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 534133980Sgibbs printf("vx%d: RX Status overrun\n", sc->unit); 535133980Sgibbs return 1; 536133980Sgibbs } 537133980Sgibbs if (fifost & FIFOS_RX_OVERRUN) { 538133980Sgibbs if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 539133980Sgibbs printf("vx%d: RX overrun\n", sc->unit); 540133980Sgibbs return 1; 541133980Sgibbs } 542133980Sgibbs if (fifost & FIFOS_TX_OVERRUN) { 543133980Sgibbs if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 544133980Sgibbs printf("vx%d: TX overrun\n", sc->unit); 545133980Sgibbs vxreset(sc); 546133980Sgibbs return 0; 547133980Sgibbs } 54819410Sguido return 0; 54919410Sguido} 55019410Sguido 551133980Sgibbsstatic void 552133980Sgibbsvxtxstat(struct vx_softc *sc) 55319410Sguido{ 554133980Sgibbs int i; 55519410Sguido 556133980Sgibbs /* 557133980Sgibbs * We need to read+write TX_STATUS until we get a 0 status 558133980Sgibbs * in order to turn off the interrupt flag. 559133980Sgibbs */ 560133980Sgibbs while ((i = CSR_READ_1(sc, VX_W1_TX_STATUS)) & TXS_COMPLETE) { 561133980Sgibbs CSR_WRITE_1(sc, VX_W1_TX_STATUS, 0x0); 56219410Sguido 563133980Sgibbs if (i & TXS_JABBER) { 564133980Sgibbs ++sc->arpcom.ac_if.if_oerrors; 565133980Sgibbs if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 566133980Sgibbs printf("vx%d: jabber (%x)\n", sc->unit, i); 567133980Sgibbs vxreset(sc); 568133980Sgibbs } else if (i & TXS_UNDERRUN) { 569133980Sgibbs ++sc->arpcom.ac_if.if_oerrors; 570133980Sgibbs if (sc->arpcom.ac_if.if_flags & IFF_DEBUG) 571133980Sgibbs printf("vx%d: fifo underrun (%x) @%d\n", 572133980Sgibbs sc->unit, i, sc->tx_start_thresh); 573133980Sgibbs if (sc->tx_succ_ok < 100) 574133980Sgibbs sc->tx_start_thresh = 575133980Sgibbs min(ETHER_MAX_LEN,sc->tx_start_thresh + 20); 576133980Sgibbs sc->tx_succ_ok = 0; 577133980Sgibbs vxreset(sc); 578133980Sgibbs } else if (i & TXS_MAX_COLLISION) { 579133980Sgibbs ++sc->arpcom.ac_if.if_collisions; 580133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_ENABLE); 581133980Sgibbs sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 582133980Sgibbs } else 583133980Sgibbs sc->tx_succ_ok = (sc->tx_succ_ok + 1) & 127; 584133980Sgibbs } 58519410Sguido} 58619410Sguido 58719410Sguidovoid 588133980Sgibbsvxintr(void *voidsc) 58919410Sguido{ 590133980Sgibbs register short status; 591133980Sgibbs struct vx_softc *sc = voidsc; 592133980Sgibbs struct ifnet *ifp = &sc->arpcom.ac_if; 59319410Sguido 594133980Sgibbs for (;;) { 595133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, C_INTR_LATCH); 59619410Sguido 597133980Sgibbs status = CSR_READ_2(sc, VX_STATUS); 59819410Sguido 599133980Sgibbs if ((status & (S_TX_COMPLETE | S_TX_AVAIL | 600133980Sgibbs S_RX_COMPLETE | S_CARD_FAILURE)) == 0) 601133980Sgibbs break; 60219410Sguido 603133980Sgibbs /* 604133980Sgibbs * Acknowledge any interrupts. It's important that we do this 605133980Sgibbs * first, since there would otherwise be a race condition. 606133980Sgibbs * Due to the i386 interrupt queueing, we may get spurious 607133980Sgibbs * interrupts occasionally. 608133980Sgibbs */ 609133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, ACK_INTR | status); 61019410Sguido 611133980Sgibbs if (status & S_RX_COMPLETE) 612133980Sgibbs vxread(sc); 613133980Sgibbs if (status & S_TX_AVAIL) { 614133980Sgibbs ifp->if_timer = 0; 615133980Sgibbs sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; 616133980Sgibbs vxstart(&sc->arpcom.ac_if); 617133980Sgibbs } 618133980Sgibbs if (status & S_CARD_FAILURE) { 619133980Sgibbs printf("vx%d: adapter failure (%x)\n", 620133980Sgibbs sc->unit, status); 621133980Sgibbs ifp->if_timer = 0; 622133980Sgibbs vxreset(sc); 623133980Sgibbs return; 624133980Sgibbs } 625133980Sgibbs if (status & S_TX_COMPLETE) { 626133980Sgibbs ifp->if_timer = 0; 627133980Sgibbs vxtxstat(sc); 628133980Sgibbs vxstart(ifp); 629133980Sgibbs } 63019410Sguido } 63119410Sguido 632133980Sgibbs /* no more interrupts */ 633133980Sgibbs return; 63419410Sguido} 63519410Sguido 63619410Sguidostatic void 637133980Sgibbsvxread(struct vx_softc *sc) 63819410Sguido{ 639133980Sgibbs struct ifnet *ifp = &sc->arpcom.ac_if; 640133980Sgibbs struct mbuf *m; 641133980Sgibbs struct ether_header *eh; 642133980Sgibbs u_int len; 64319410Sguido 644133980Sgibbs len = CSR_READ_2(sc, VX_W1_RX_STATUS); 64519410Sguido 64619410Sguidoagain: 64719410Sguido 648133980Sgibbs if (ifp->if_flags & IFF_DEBUG) { 649133980Sgibbs int err = len & ERR_MASK; 650133980Sgibbs char *s = NULL; 65119410Sguido 652133980Sgibbs if (len & ERR_INCOMPLETE) 653133980Sgibbs s = "incomplete packet"; 654133980Sgibbs else if (err == ERR_OVERRUN) 655133980Sgibbs s = "packet overrun"; 656133980Sgibbs else if (err == ERR_RUNT) 657133980Sgibbs s = "runt packet"; 658133980Sgibbs else if (err == ERR_ALIGNMENT) 659133980Sgibbs s = "bad alignment"; 660133980Sgibbs else if (err == ERR_CRC) 661133980Sgibbs s = "bad crc"; 662133980Sgibbs else if (err == ERR_OVERSIZE) 663133980Sgibbs s = "oversized packet"; 664133980Sgibbs else if (err == ERR_DRIBBLE) 665133980Sgibbs s = "dribble bits"; 666133980Sgibbs 667133980Sgibbs if (s) 668133980Sgibbs printf("vx%d: %s\n", sc->unit, s); 669133980Sgibbs } 67019410Sguido if (len & ERR_INCOMPLETE) 671133980Sgibbs return; 67219410Sguido 673133980Sgibbs if (len & ERR_RX) { 674133980Sgibbs ++ifp->if_ierrors; 675133980Sgibbs goto abort; 676133980Sgibbs } 677133980Sgibbs len &= RX_BYTES_MASK; /* Lower 11 bits = RX bytes. */ 67819410Sguido 679133980Sgibbs /* Pull packet off interface. */ 680133980Sgibbs m = vxget(sc, len); 681133980Sgibbs if (m == 0) { 68269732Swpaul ifp->if_ierrors++; 68369732Swpaul goto abort; 68469732Swpaul } 685133980Sgibbs ++ifp->if_ipackets; 68669732Swpaul 687133980Sgibbs { 688133980Sgibbs struct mbuf *m0; 68969732Swpaul 690133980Sgibbs m0 = m_devget(mtod(m, char *), m->m_pkthdr.len, ETHER_ALIGN, ifp, NULL); 691133980Sgibbs if (m0 == NULL) { 692133980Sgibbs ifp->if_ierrors++; 693133980Sgibbs goto abort; 694133980Sgibbs } 695133980Sgibbs m_freem(m); 696133980Sgibbs m = m0; 697133980Sgibbs } 69819410Sguido 699133980Sgibbs /* We assume the header fit entirely in one mbuf. */ 700133980Sgibbs eh = mtod(m, struct ether_header *); 70122062Sphk 702133980Sgibbs /* 703133980Sgibbs * XXX: Some cards seem to be in promiscous mode all the time. 704133980Sgibbs * we need to make sure we only get our own stuff always. 705133980Sgibbs * bleah! 706133980Sgibbs */ 70760536Sarchie 708133980Sgibbs if ((eh->ether_dhost[0] & 1) == 0 /* !mcast and !bcast */ 709133980Sgibbs && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN)!=0) { 710133980Sgibbs m_freem(m); 711133980Sgibbs return; 712133980Sgibbs } 713133980Sgibbs (*ifp->if_input) (ifp, m); 71419410Sguido 715133980Sgibbs /* 716133980Sgibbs * In periods of high traffic we can actually receive enough 717133980Sgibbs * packets so that the fifo overrun bit will be set at this point, 718133980Sgibbs * even though we just read a packet. In this case we 719133980Sgibbs * are not going to receive any more interrupts. We check for 720133980Sgibbs * this condition and read again until the fifo is not full. 721133980Sgibbs * We could simplify this test by not using vxstatus(), but 722133980Sgibbs * rechecking the RX_STATUS register directly. This test could 723133980Sgibbs * result in unnecessary looping in cases where there is a new 724133980Sgibbs * packet but the fifo is not full, but it will not fix the 725133980Sgibbs * stuck behavior. 726133980Sgibbs * 727133980Sgibbs * Even with this improvement, we still get packet overrun errors 728133980Sgibbs * which are hurting performance. Maybe when I get some more time 729133980Sgibbs * I'll modify vxread() so that it can handle RX_EARLY interrupts. 730133980Sgibbs */ 731133980Sgibbs if (vxstatus(sc)) { 732133980Sgibbs len = CSR_READ_2(sc, VX_W1_RX_STATUS); 733133980Sgibbs /* Check if we are stuck and reset [see XXX comment] */ 734133980Sgibbs if (len & ERR_INCOMPLETE) { 735133980Sgibbs if (ifp->if_flags & IFF_DEBUG) 736133980Sgibbs printf("vx%d: adapter reset\n", sc->unit); 737133980Sgibbs vxreset(sc); 738133980Sgibbs return; 739133980Sgibbs } 740133980Sgibbs goto again; 74119410Sguido } 742133980Sgibbs return; 74319410Sguido 74419410Sguidoabort: 745133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK); 74619410Sguido} 74719410Sguido 74819410Sguidostatic struct mbuf * 749133980Sgibbsvxget(struct vx_softc *sc, u_int totlen) 75019410Sguido{ 751133980Sgibbs struct ifnet *ifp = &sc->arpcom.ac_if; 752133980Sgibbs struct mbuf *top, **mp, *m; 753133980Sgibbs int len; 754133980Sgibbs int sh; 75519410Sguido 756133980Sgibbs m = sc->mb[sc->next_mb]; 757133980Sgibbs sc->mb[sc->next_mb] = 0; 758133980Sgibbs if (m == 0) { 759133980Sgibbs MGETHDR(m, M_DONTWAIT, MT_DATA); 760133980Sgibbs if (m == 0) 761133980Sgibbs return 0; 762133980Sgibbs } else { 763133980Sgibbs /* If the queue is no longer full, refill. */ 764133980Sgibbs if (sc->last_mb == sc->next_mb && sc->buffill_pending == 0) { 765133980Sgibbs sc->ch = timeout(vxmbuffill, sc, 1); 766133980Sgibbs sc->buffill_pending = 1; 767133980Sgibbs } 768133980Sgibbs /* Convert one of our saved mbuf's. */ 769133980Sgibbs sc->next_mb = (sc->next_mb + 1) % MAX_MBS; 770133980Sgibbs m->m_data = m->m_pktdat; 771133980Sgibbs m->m_flags = M_PKTHDR; 772133980Sgibbs bzero(&m->m_pkthdr, sizeof(m->m_pkthdr)); 77329671Sgibbs } 774133980Sgibbs m->m_pkthdr.rcvif = ifp; 775133980Sgibbs m->m_pkthdr.len = totlen; 776133980Sgibbs len = MHLEN; 777133980Sgibbs top = 0; 778133980Sgibbs mp = ⊤ 77919410Sguido 780133980Sgibbs /* 781133980Sgibbs * We read the packet at splhigh() so that an interrupt from another 782133980Sgibbs * device doesn't cause the card's buffer to overflow while we're 783133980Sgibbs * reading it. We may still lose packets at other times. 784133980Sgibbs */ 785133980Sgibbs sh = splhigh(); 78619410Sguido 787133980Sgibbs /* 788133980Sgibbs * Since we don't set allowLargePackets bit in MacControl register, 789133980Sgibbs * we can assume that totlen <= 1500bytes. 790133980Sgibbs * The while loop will be performed iff we have a packet with 791133980Sgibbs * MLEN < m_len < MINCLSIZE. 792133980Sgibbs */ 793133980Sgibbs while (totlen > 0) { 794133980Sgibbs if (top) { 795133980Sgibbs m = sc->mb[sc->next_mb]; 796133980Sgibbs sc->mb[sc->next_mb] = 0; 797133980Sgibbs if (m == 0) { 798133980Sgibbs MGET(m, M_DONTWAIT, MT_DATA); 799133980Sgibbs if (m == 0) { 800133980Sgibbs splx(sh); 801133980Sgibbs m_freem(top); 802133980Sgibbs return 0; 803133980Sgibbs } 804133980Sgibbs } else { 805133980Sgibbs sc->next_mb = (sc->next_mb + 1) % MAX_MBS; 806133980Sgibbs } 807133980Sgibbs len = MLEN; 808133980Sgibbs } 809133980Sgibbs if (totlen >= MINCLSIZE) { 810133980Sgibbs MCLGET(m, M_DONTWAIT); 811133980Sgibbs if (m->m_flags & M_EXT) 812133980Sgibbs len = MCLBYTES; 813133980Sgibbs } 814133980Sgibbs len = min(totlen, len); 815133980Sgibbs if (len > 3) 816133980Sgibbs bus_space_read_multi_4(sc->bst, sc->bsh, 817133980Sgibbs VX_W1_RX_PIO_RD_1, mtod(m, u_int32_t *), len / 4); 818133980Sgibbs if (len & 3) { 819133980Sgibbs bus_space_read_multi_1(sc->bst, sc->bsh, 820133980Sgibbs VX_W1_RX_PIO_RD_1, mtod(m, u_int8_t *) + (len & ~3), 821133980Sgibbs len & 3); 822133980Sgibbs } 823133980Sgibbs m->m_len = len; 824133980Sgibbs totlen -= len; 825133980Sgibbs *mp = m; 826133980Sgibbs mp = &m->m_next; 82730022Sitojun } 82819410Sguido 829133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK); 83019410Sguido 831133980Sgibbs splx(sh); 83219410Sguido 833133980Sgibbs return top; 83419410Sguido} 83519410Sguido 83619410Sguido 83719410Sguidostatic int 838133980Sgibbsvxioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 83919410Sguido{ 840133980Sgibbs struct vx_softc *sc = ifp->if_softc; 841133980Sgibbs struct ifreq *ifr = (struct ifreq *) data; 842133980Sgibbs int s, error = 0; 84319410Sguido 844133980Sgibbs s = splimp(); 84519410Sguido 846133980Sgibbs switch (cmd) { 847133980Sgibbs case SIOCSIFFLAGS: 848133980Sgibbs if ((ifp->if_flags & IFF_UP) == 0 && 849133980Sgibbs (ifp->if_flags & IFF_RUNNING) != 0) { 850133980Sgibbs /* 851133980Sgibbs * If interface is marked up and it is stopped, then 852133980Sgibbs * start it. 853133980Sgibbs */ 854133980Sgibbs vxstop(sc); 855133980Sgibbs ifp->if_flags &= ~IFF_RUNNING; 856133980Sgibbs } else if ((ifp->if_flags & IFF_UP) != 0 && 857133980Sgibbs (ifp->if_flags & IFF_RUNNING) == 0) { 858133980Sgibbs /* 859133980Sgibbs * If interface is marked up and it is stopped, then 860133980Sgibbs * start it. 861133980Sgibbs */ 862133980Sgibbs vxinit(sc); 863133980Sgibbs } else { 864133980Sgibbs /* 865133980Sgibbs * deal with flags changes: 866133980Sgibbs * IFF_MULTICAST, IFF_PROMISC, 867133980Sgibbs * IFF_LINK0, IFF_LINK1, 868133980Sgibbs */ 869133980Sgibbs vxsetfilter(sc); 870133980Sgibbs vxsetlink(sc); 871133980Sgibbs } 872133980Sgibbs break; 87319410Sguido 874133980Sgibbs case SIOCSIFMTU: 875133980Sgibbs /* 876133980Sgibbs * Set the interface MTU. 877133980Sgibbs */ 878133980Sgibbs if (ifr->ifr_mtu > ETHERMTU) { 879133980Sgibbs error = EINVAL; 880133980Sgibbs } else { 881133980Sgibbs ifp->if_mtu = ifr->ifr_mtu; 882133980Sgibbs } 883133980Sgibbs break; 88419410Sguido 885133980Sgibbs case SIOCADDMULTI: 886133980Sgibbs case SIOCDELMULTI: 887133980Sgibbs /* 888133980Sgibbs * Multicast list has changed; set the hardware filter 889133980Sgibbs * accordingly. 890133980Sgibbs */ 891133980Sgibbs vxreset(sc); 892133980Sgibbs error = 0; 893133980Sgibbs break; 89419410Sguido 89519410Sguido 896133980Sgibbs default: 897133980Sgibbs error = ether_ioctl(ifp, cmd, data); 898133980Sgibbs break; 899133980Sgibbs } 90019410Sguido 901133980Sgibbs splx(s); 90219410Sguido 903133980Sgibbs return (error); 90419410Sguido} 90519410Sguido 90619410Sguidostatic void 907133980Sgibbsvxreset(struct vx_softc *sc) 90819410Sguido{ 909133980Sgibbs int s; 91019410Sguido 911133980Sgibbs s = splimp(); 912133980Sgibbs 913133980Sgibbs vxstop(sc); 914133980Sgibbs vxinit(sc); 915133980Sgibbs splx(s); 91619410Sguido} 91719410Sguido 91819410Sguidostatic void 919133980Sgibbsvxwatchdog(struct ifnet *ifp) 92019410Sguido{ 921133980Sgibbs struct vx_softc *sc = ifp->if_softc; 92219410Sguido 923133980Sgibbs if (ifp->if_flags & IFF_DEBUG) 924133980Sgibbs if_printf(ifp, "device timeout\n"); 925133980Sgibbs ifp->if_flags &= ~IFF_OACTIVE; 926133980Sgibbs vxstart(ifp); 927133980Sgibbs vxintr(sc); 92819410Sguido} 92919410Sguido 93019410Sguidovoid 931133980Sgibbsvxstop(struct vx_softc *sc) 93219410Sguido{ 933133980Sgibbs struct ifnet *ifp = &sc->arpcom.ac_if; 93419410Sguido 935133980Sgibbs ifp->if_timer = 0; 93619410Sguido 937133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISABLE); 938133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_DISCARD_TOP_PACK); 939133980Sgibbs VX_BUSY_WAIT; 940133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_DISABLE); 941133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, STOP_TRANSCEIVER); 942133980Sgibbs DELAY(800); 943133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, RX_RESET); 944133980Sgibbs VX_BUSY_WAIT; 945133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, TX_RESET); 946133980Sgibbs VX_BUSY_WAIT; 947133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, C_INTR_LATCH); 948133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RD_0_MASK); 949133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_INTR_MASK); 950133980Sgibbs CSR_WRITE_2(sc, VX_COMMAND, SET_RX_FILTER); 95119410Sguido 952133980Sgibbs vxmbufempty(sc); 95319410Sguido} 95419410Sguido 95519410Sguidoint 956133980Sgibbsvxbusyeeprom(struct vx_softc *sc) 95719410Sguido{ 958133980Sgibbs int j, i = 100; 95919410Sguido 960133980Sgibbs while (i--) { 961133980Sgibbs j = CSR_READ_2(sc, VX_W0_EEPROM_COMMAND); 962133980Sgibbs if (j & EEPROM_BUSY) 963133980Sgibbs DELAY(100); 964133980Sgibbs else 965133980Sgibbs break; 966133980Sgibbs } 967133980Sgibbs if (!i) { 968133980Sgibbs printf("vx%d: eeprom failed to come ready\n", sc->unit); 969133980Sgibbs return (1); 970133980Sgibbs } 971133980Sgibbs return (0); 97219410Sguido} 97319410Sguido 97419410Sguidostatic void 975133980Sgibbsvxmbuffill(void *sp) 97619410Sguido{ 977133980Sgibbs struct vx_softc *sc = (struct vx_softc *)sp; 978133980Sgibbs int s, i; 97919410Sguido 980133980Sgibbs s = splimp(); 981133980Sgibbs i = sc->last_mb; 982133980Sgibbs do { 983133980Sgibbs if (sc->mb[i] == NULL) 984133980Sgibbs MGET(sc->mb[i], M_DONTWAIT, MT_DATA); 985133980Sgibbs if (sc->mb[i] == NULL) 986133980Sgibbs break; 987133980Sgibbs i = (i + 1) % MAX_MBS; 988133980Sgibbs } while (i != sc->next_mb); 989133980Sgibbs sc->last_mb = i; 990133980Sgibbs /* If the queue was not filled, try again. */ 991133980Sgibbs if (sc->last_mb != sc->next_mb) { 992133980Sgibbs sc->ch = timeout(vxmbuffill, sc, 1); 993133980Sgibbs sc->buffill_pending = 1; 994133980Sgibbs } else { 995133980Sgibbs sc->buffill_pending = 0; 996133980Sgibbs } 997133980Sgibbs splx(s); 99819410Sguido} 99919410Sguido 100019410Sguidostatic void 1001133980Sgibbsvxmbufempty(struct vx_softc *sc) 100219410Sguido{ 1003133980Sgibbs int s, i; 100419410Sguido 1005133980Sgibbs s = splimp(); 1006133980Sgibbs for (i = 0; i < MAX_MBS; i++) { 1007133980Sgibbs if (sc->mb[i]) { 1008133980Sgibbs m_freem(sc->mb[i]); 1009133980Sgibbs sc->mb[i] = NULL; 1010133980Sgibbs } 101119410Sguido } 1012133980Sgibbs sc->last_mb = sc->next_mb = 0; 1013133980Sgibbs if (sc->buffill_pending != 0) 1014133980Sgibbs untimeout(vxmbuffill, sc, sc->ch); 1015133980Sgibbs splx(s); 101619410Sguido} 1017