if_ex.c revision 179775
1139749Simp/*- 221769Sjkh * Copyright (c) 1996, Javier Mart�n Rueda (jmrueda@diatel.upm.es) 321769Sjkh * All rights reserved. 421769Sjkh * 521769Sjkh * Redistribution and use in source and binary forms, with or without 621769Sjkh * modification, are permitted provided that the following conditions 721769Sjkh * are met: 821769Sjkh * 1. Redistributions of source code must retain the above copyright 921769Sjkh * notice unmodified, this list of conditions, and the following 1021769Sjkh * disclaimer. 1121769Sjkh * 2. Redistributions in binary form must reproduce the above copyright 1221769Sjkh * notice, this list of conditions and the following disclaimer in the 1321769Sjkh * documentation and/or other materials provided with the distribution. 1421769Sjkh * 1521769Sjkh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1621769Sjkh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1721769Sjkh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1821769Sjkh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1921769Sjkh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2021769Sjkh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2121769Sjkh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2221769Sjkh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2321769Sjkh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2421769Sjkh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2521769Sjkh * SUCH DAMAGE. 2629877Smsmith * 2752286Smdodd * 2852286Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 2952286Smdodd * <mdodd@FreeBSD.org> 3021769Sjkh */ 3121769Sjkh 32119418Sobrien#include <sys/cdefs.h> 33119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/ex/if_ex.c 179775 2008-06-13 12:14:22Z jhb $"); 34119418Sobrien 3521769Sjkh/* 3629877Smsmith * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 3721769Sjkh * 3821769Sjkh * Revision history: 3921769Sjkh * 40112731Smdodd * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver. 4121769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 4221769Sjkh */ 4321769Sjkh 4421769Sjkh#include <sys/param.h> 4521769Sjkh#include <sys/systm.h> 4652286Smdodd#include <sys/kernel.h> 4724204Sbde#include <sys/sockio.h> 4821769Sjkh#include <sys/mbuf.h> 4921769Sjkh#include <sys/socket.h> 5021769Sjkh 5152286Smdodd#include <sys/module.h> 5252286Smdodd#include <sys/bus.h> 5352286Smdodd 5452286Smdodd#include <machine/bus.h> 5552286Smdodd#include <machine/resource.h> 5652286Smdodd#include <sys/rman.h> 5752286Smdodd 5857987Smdodd#include <net/if.h> 5957987Smdodd#include <net/if_arp.h> 60112731Smdodd#include <net/if_dl.h> 6157987Smdodd#include <net/if_media.h> 62147256Sbrooks#include <net/if_types.h> 6350026Smdodd#include <net/ethernet.h> 6457987Smdodd#include <net/bpf.h> 6521769Sjkh 6650026Smdodd#include <netinet/in.h> 6750026Smdodd#include <netinet/if_ether.h> 6850026Smdodd 6921769Sjkh 7052286Smdodd#include <isa/isavar.h> 7152286Smdodd#include <isa/pnpvar.h> 7252286Smdodd 7355953Speter#include <dev/ex/if_exreg.h> 7459816Smdodd#include <dev/ex/if_exvar.h> 7521769Sjkh 7621769Sjkh#ifdef EXDEBUG 7752286Smdodd# define Start_End 1 7852286Smdodd# define Rcvd_Pkts 2 7952286Smdodd# define Sent_Pkts 4 8052286Smdodd# define Status 8 8121769Sjkhstatic int debug_mask = 0; 8252286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8321769Sjkh#else 8452286Smdodd# define DODEBUG(level, action) 8521769Sjkh#endif 8621769Sjkh 87112801Smdodddevclass_t ex_devclass; 88112801Smdodd 8959816Smdoddchar irq2eemap[] = 9052286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 9159816Smdoddu_char ee2irqmap[] = 9252286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 9359816Smdodd 9459816Smdoddchar plus_irq2eemap[] = 9552286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 9659816Smdoddu_char plus_ee2irqmap[] = 9752286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 9821769Sjkh 9952286Smdodd/* Network Interface Functions */ 100131192Simpstatic void ex_init(void *); 101179775Sjhbstatic void ex_init_locked(struct ex_softc *); 102131192Simpstatic void ex_start(struct ifnet *); 103179775Sjhbstatic void ex_start_locked(struct ifnet *); 104131192Simpstatic int ex_ioctl(struct ifnet *, u_long, caddr_t); 105179775Sjhbstatic void ex_watchdog(void *); 10621769Sjkh 10757987Smdodd/* ifmedia Functions */ 108131192Simpstatic int ex_ifmedia_upd(struct ifnet *); 109131192Simpstatic void ex_ifmedia_sts(struct ifnet *, struct ifmediareq *); 11057987Smdodd 111131192Simpstatic int ex_get_media(struct ex_softc *); 11259816Smdodd 113131192Simpstatic void ex_reset(struct ex_softc *); 114131192Simpstatic void ex_setmulti(struct ex_softc *); 11552286Smdodd 116131192Simpstatic void ex_tx_intr(struct ex_softc *); 117131192Simpstatic void ex_rx_intr(struct ex_softc *); 11852286Smdodd 11959816Smdoddvoid 120131192Simpex_get_address(struct ex_softc *sc, u_char *enaddr) 12121769Sjkh{ 122131192Simp uint16_t eaddr_tmp; 12321769Sjkh 124131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Lo); 12552286Smdodd enaddr[5] = eaddr_tmp & 0xff; 12652286Smdodd enaddr[4] = eaddr_tmp >> 8; 127131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Mid); 12852286Smdodd enaddr[3] = eaddr_tmp & 0xff; 12952286Smdodd enaddr[2] = eaddr_tmp >> 8; 130131192Simp eaddr_tmp = ex_eeprom_read(sc, EE_Eth_Addr_Hi); 13152286Smdodd enaddr[1] = eaddr_tmp & 0xff; 13252286Smdodd enaddr[0] = eaddr_tmp >> 8; 13352286Smdodd 13452286Smdodd return; 13552286Smdodd} 13621769Sjkh 13759816Smdoddint 138131192Simpex_card_type(u_char *enaddr) 13952286Smdodd{ 14052286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 14152286Smdodd return (CARD_TYPE_EX_10_PLUS); 14252286Smdodd 14352286Smdodd return (CARD_TYPE_EX_10); 14452286Smdodd} 14552286Smdodd 14655882Smdodd/* 14759816Smdodd * Caller is responsible for eventually calling 14859816Smdodd * ex_release_resources() on failure. 14955882Smdodd */ 15059816Smdoddint 151131192Simpex_alloc_resources(device_t dev) 15255882Smdodd{ 15359816Smdodd struct ex_softc * sc = device_get_softc(dev); 15459816Smdodd int error = 0; 15555882Smdodd 156127135Snjl sc->ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 157127135Snjl &sc->ioport_rid, RF_ACTIVE); 15859816Smdodd if (!sc->ioport) { 15959816Smdodd device_printf(dev, "No I/O space?!\n"); 16059816Smdodd error = ENOMEM; 16159816Smdodd goto bad; 16259816Smdodd } 16357989Smdodd 164127135Snjl sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irq_rid, 165127135Snjl RF_ACTIVE); 16655882Smdodd 16759816Smdodd if (!sc->irq) { 16859816Smdodd device_printf(dev, "No IRQ?!\n"); 16959816Smdodd error = ENOMEM; 17059816Smdodd goto bad; 17155882Smdodd } 17255882Smdodd 17359816Smdoddbad: 17459816Smdodd return (error); 17555882Smdodd} 17655882Smdodd 17759816Smdoddvoid 178131192Simpex_release_resources(device_t dev) 17952286Smdodd{ 18059816Smdodd struct ex_softc * sc = device_get_softc(dev); 18152286Smdodd 18259816Smdodd if (sc->ih) { 18359816Smdodd bus_teardown_intr(dev, sc->irq, sc->ih); 18459816Smdodd sc->ih = NULL; 18552286Smdodd } 18652286Smdodd 18759816Smdodd if (sc->ioport) { 18859816Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 18959816Smdodd sc->ioport_rid, sc->ioport); 19059816Smdodd sc->ioport = NULL; 19152286Smdodd } 19252286Smdodd 19359816Smdodd if (sc->irq) { 19459816Smdodd bus_release_resource(dev, SYS_RES_IRQ, 19559816Smdodd sc->irq_rid, sc->irq); 19659816Smdodd sc->irq = NULL; 19757989Smdodd } 19857989Smdodd 199150215Sru if (sc->ifp) 200150215Sru if_free(sc->ifp); 201150215Sru 20259816Smdodd return; 20352286Smdodd} 20452286Smdodd 20559816Smdoddint 20659816Smdoddex_attach(device_t dev) 20752286Smdodd{ 20852286Smdodd struct ex_softc * sc = device_get_softc(dev); 209147256Sbrooks struct ifnet * ifp; 21057987Smdodd struct ifmedia * ifm; 211179775Sjhb int error; 212131192Simp uint16_t temp; 21352286Smdodd 214147256Sbrooks ifp = sc->ifp = if_alloc(IFT_ETHER); 215147256Sbrooks if (ifp == NULL) { 216147256Sbrooks device_printf(dev, "can not if_alloc()\n"); 217147256Sbrooks return (ENOSPC); 218147256Sbrooks } 21929877Smsmith /* work out which set of irq <-> internal tables to use */ 220147256Sbrooks if (ex_card_type(sc->enaddr) == CARD_TYPE_EX_10_PLUS) { 22129877Smsmith sc->irq2ee = plus_irq2eemap; 22229877Smsmith sc->ee2irq = plus_ee2irqmap; 22352286Smdodd } else { 22429877Smsmith sc->irq2ee = irq2eemap; 22529877Smsmith sc->ee2irq = ee2irqmap; 22629877Smsmith } 22729877Smsmith 22821769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 22921769Sjkh 23021769Sjkh /* 23121769Sjkh * Initialize the ifnet structure. 23221769Sjkh */ 23321769Sjkh ifp->if_softc = sc; 234121816Sbrooks if_initname(ifp, device_get_name(dev), device_get_unit(dev)); 23555883Smdodd ifp->if_mtu = ETHERMTU; 236179775Sjhb ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST; 23721769Sjkh ifp->if_start = ex_start; 23821769Sjkh ifp->if_ioctl = ex_ioctl; 23955883Smdodd ifp->if_init = ex_init; 240179775Sjhb IFQ_SET_MAXLEN(&ifp->if_snd, IFQ_MAXLEN); 24121769Sjkh 24257987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 243179775Sjhb mtx_init(&sc->lock, device_get_nameunit(dev), MTX_NETWORK_LOCK, 244179775Sjhb MTX_DEF); 245179775Sjhb callout_init_mtx(&sc->timer, &sc->lock, 0); 24657987Smdodd 247131192Simp temp = ex_eeprom_read(sc, EE_W5); 24857987Smdodd if (temp & EE_W5_PORT_TPE) 24957987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 25057987Smdodd if (temp & EE_W5_PORT_BNC) 25157987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 25257987Smdodd if (temp & EE_W5_PORT_AUI) 25357987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 25457987Smdodd 255112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_AUTO, 0, NULL); 256112764Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); 257131192Simp ifmedia_set(&sc->ifmedia, ex_get_media(sc)); 25857987Smdodd 25957987Smdodd ifm = &sc->ifmedia; 260179775Sjhb ifm->ifm_media = ifm->ifm_cur->ifm_media; 26157987Smdodd ex_ifmedia_upd(ifp); 26257987Smdodd 26321769Sjkh /* 26421769Sjkh * Attach the interface. 26521769Sjkh */ 266147256Sbrooks ether_ifattach(ifp, sc->enaddr); 26721769Sjkh 268179775Sjhb error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET | INTR_MPSAFE, 269179775Sjhb NULL, ex_intr, (void *)sc, &sc->ih); 270179775Sjhb if (error) { 271179775Sjhb device_printf(dev, "bus_setup_intr() failed!\n"); 272179775Sjhb ether_ifdetach(ifp); 273179775Sjhb mtx_destroy(&sc->lock); 274179775Sjhb return (error); 275179775Sjhb } 276179775Sjhb 27752286Smdodd return(0); 27821769Sjkh} 27921769Sjkh 280112800Smdoddint 281131192Simpex_detach(device_t dev) 282112800Smdodd{ 283112800Smdodd struct ex_softc *sc; 284112800Smdodd struct ifnet *ifp; 285112800Smdodd 286112800Smdodd sc = device_get_softc(dev); 287147256Sbrooks ifp = sc->ifp; 288112800Smdodd 289179775Sjhb EX_LOCK(sc); 290112800Smdodd ex_stop(sc); 291179775Sjhb EX_UNLOCK(sc); 292112800Smdodd 293112800Smdodd ether_ifdetach(ifp); 294179775Sjhb callout_drain(&sc->timer); 295112800Smdodd 296112800Smdodd ex_release_resources(dev); 297179775Sjhb mtx_destroy(&sc->lock); 298112800Smdodd 299112800Smdodd return (0); 300112800Smdodd} 301112800Smdodd 30255883Smdoddstatic void 30355883Smdoddex_init(void *xsc) 30421769Sjkh{ 30552286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 306179775Sjhb 307179775Sjhb EX_LOCK(sc); 308179775Sjhb ex_init_locked(sc); 309179775Sjhb EX_UNLOCK(sc); 310179775Sjhb} 311179775Sjhb 312179775Sjhbstatic void 313179775Sjhbex_init_locked(struct ex_softc *sc) 314179775Sjhb{ 315147256Sbrooks struct ifnet * ifp = sc->ifp; 31652286Smdodd int i; 31752286Smdodd unsigned short temp_reg; 31821769Sjkh 319121816Sbrooks DODEBUG(Start_End, printf("%s: ex_init: start\n", ifp->if_xname);); 32021769Sjkh 321179775Sjhb sc->tx_timeout = 0; 32221769Sjkh 32321769Sjkh /* 32421769Sjkh * Load the ethernet address into the card. 32521769Sjkh */ 326131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 327131192Simp temp_reg = CSR_READ_1(sc, EEPROM_REG); 32852286Smdodd if (temp_reg & Trnoff_Enable) { 329131192Simp CSR_WRITE_1(sc, EEPROM_REG, temp_reg & ~Trnoff_Enable); 33052286Smdodd } 33152286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 332152315Sru CSR_WRITE_1(sc, I_ADDR_REG0 + i, IF_LLADDR(sc->ifp)[i]); 33352286Smdodd } 33421769Sjkh /* 33521769Sjkh * - Setup transmit chaining and discard bad received frames. 33621769Sjkh * - Match broadcast. 33721769Sjkh * - Clear test mode. 33821769Sjkh * - Set receiving mode. 33921769Sjkh * - Set IRQ number. 34021769Sjkh */ 341131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 342131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | No_SA_Ins | RX_CRC_InMem); 343131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3) & 0x3f /* XXX constants. */ ); 344131192Simp CSR_WRITE_1(sc, CMD_REG, Bank1_Sel); 345131192Simp CSR_WRITE_1(sc, INT_NO_REG, (CSR_READ_1(sc, INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 34621769Sjkh 34721769Sjkh /* 34821769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 34921769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 35021769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 35121769Sjkh */ 35221769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 35321769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 35421769Sjkh sc->rx_lower_limit = 0x0000; 35521769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 35621769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 35721769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 358131192Simp CSR_WRITE_1(sc, RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 359131192Simp CSR_WRITE_1(sc, RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 360131192Simp CSR_WRITE_1(sc, XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 361131192Simp CSR_WRITE_1(sc, XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 36221769Sjkh 36321769Sjkh /* 36421769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 36521769Sjkh */ 366131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) | TriST_INT); 367131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 368131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 369131192Simp CSR_WRITE_1(sc, STATUS_REG, All_Int); 37021769Sjkh 37121769Sjkh /* 37221769Sjkh * Initialize receive and transmit ring buffers. 37321769Sjkh */ 374131192Simp CSR_WRITE_2(sc, RCV_BAR, sc->rx_lower_limit); 37521769Sjkh sc->rx_head = sc->rx_lower_limit; 376131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 377131192Simp CSR_WRITE_2(sc, XMT_BAR, sc->tx_lower_limit); 37821769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 37921769Sjkh 380148887Srwatson ifp->if_drv_flags |= IFF_DRV_RUNNING; 381148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 38221769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 383179775Sjhb callout_reset(&sc->timer, hz, ex_watchdog, sc); 38421769Sjkh 385112731Smdodd ex_setmulti(sc); 386112731Smdodd 38721769Sjkh /* 38821769Sjkh * Final reset of the board, and enable operation. 38921769Sjkh */ 390131192Simp CSR_WRITE_1(sc, CMD_REG, Sel_Reset_CMD); 39121769Sjkh DELAY(2); 392131192Simp CSR_WRITE_1(sc, CMD_REG, Rcv_Enable_CMD); 39321769Sjkh 394179775Sjhb ex_start_locked(ifp); 39521769Sjkh 396121816Sbrooks DODEBUG(Start_End, printf("%s: ex_init: finish\n", ifp->if_xname);); 39721769Sjkh} 39821769Sjkh 39955883Smdoddstatic void 40052286Smdoddex_start(struct ifnet *ifp) 40121769Sjkh{ 40252286Smdodd struct ex_softc * sc = ifp->if_softc; 403179775Sjhb 404179775Sjhb EX_LOCK(sc); 405179775Sjhb ex_start_locked(ifp); 406179775Sjhb EX_UNLOCK(sc); 407179775Sjhb} 408179775Sjhb 409179775Sjhbstatic void 410179775Sjhbex_start_locked(struct ifnet *ifp) 411179775Sjhb{ 412179775Sjhb struct ex_softc * sc = ifp->if_softc; 413179775Sjhb int i, len, data_len, avail, dest, next; 41452286Smdodd unsigned char tmp16[2]; 41552286Smdodd struct mbuf * opkt; 41652286Smdodd struct mbuf * m; 41721769Sjkh 41852286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 41921769Sjkh 42052286Smdodd /* 42152286Smdodd * Main loop: send outgoing packets to network card until there are no 42252286Smdodd * more packets left, or the card cannot accept any more yet. 42352286Smdodd */ 42452286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 425148887Srwatson !(ifp->if_drv_flags & IFF_DRV_OACTIVE)) { 42621769Sjkh 42752286Smdodd /* 42852286Smdodd * Ensure there is enough free transmit buffer space for 42952286Smdodd * this packet, including its header. Note: the header 43052286Smdodd * cannot wrap around the end of the transmit buffer and 43152286Smdodd * must be kept together, so we allow space for twice the 43252286Smdodd * length of the header, just in case. 43352286Smdodd */ 43421769Sjkh 43552286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 43652286Smdodd len += m->m_len; 43752286Smdodd } 43852286Smdodd 43952286Smdodd data_len = len; 44052286Smdodd 44152286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 44252286Smdodd 44352286Smdodd if (len & 1) { 44452286Smdodd len += XMT_HEADER_LEN + 1; 44552286Smdodd } else { 44652286Smdodd len += XMT_HEADER_LEN; 44752286Smdodd } 44852286Smdodd 44952286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 45052286Smdodd avail = sc->tx_mem_size - i; 45152286Smdodd } else { 45252286Smdodd avail = -i; 45352286Smdodd } 45452286Smdodd 45552286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 45652286Smdodd 45752286Smdodd if (avail >= len + XMT_HEADER_LEN) { 45852286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 45952286Smdodd 46021769Sjkh#ifdef EX_PSA_INTR 46152286Smdodd /* 46252286Smdodd * Disable rx and tx interrupts, to avoid corruption 46352286Smdodd * of the host address register by interrupt service 46452286Smdodd * routines. 46552286Smdodd * XXX Is this necessary with splimp() enabled? 46652286Smdodd */ 467131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int); 46821769Sjkh#endif 46921769Sjkh 47052286Smdodd /* 47152286Smdodd * Compute the start and end addresses of this 47252286Smdodd * frame in the tx buffer. 47352286Smdodd */ 47452286Smdodd dest = sc->tx_tail; 47552286Smdodd next = dest + len; 47621769Sjkh 47752286Smdodd if (next > sc->tx_upper_limit) { 47852286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 47952286Smdodd XMT_HEADER_LEN) { 48052286Smdodd dest = sc->tx_lower_limit; 48152286Smdodd next = dest + len; 48255881Smdodd } else { 48355881Smdodd next = sc->tx_lower_limit + 48455881Smdodd next - sc->tx_upper_limit - 2; 48552286Smdodd } 48652286Smdodd } 48721769Sjkh 48852286Smdodd /* 48952286Smdodd * Build the packet frame in the card's ring buffer. 49052286Smdodd */ 49152286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 49221769Sjkh 493131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, dest); 494131192Simp CSR_WRITE_2(sc, IO_PORT_REG, Transmit_CMD); 495131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 496131192Simp CSR_WRITE_2(sc, IO_PORT_REG, next); 497131192Simp CSR_WRITE_2(sc, IO_PORT_REG, data_len); 49821769Sjkh 49952286Smdodd /* 50052286Smdodd * Output the packet data to the card. Ensure all 50152286Smdodd * transfers are 16-bit wide, even if individual 50252286Smdodd * mbufs have odd length. 50352286Smdodd */ 50452286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 50552286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 50652286Smdodd if (i) { 50752286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 508131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 509131192Simp (uint16_t *) tmp16, 1); 51052286Smdodd } 511131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 512131192Simp (uint16_t *) (mtod(m, caddr_t) + i), 513131192Simp (m->m_len - i) / 2); 51452286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 51552286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 51652286Smdodd m->m_len - 1); 51752286Smdodd } 51852286Smdodd } 519131192Simp if (i) 520131192Simp CSR_WRITE_MULTI_2(sc, IO_PORT_REG, 521131192Simp (uint16_t *) tmp16, 1); 52255881Smdodd /* 52355881Smdodd * If there were other frames chained, update the 52455881Smdodd * chain in the last one. 52555881Smdodd */ 52655881Smdodd if (sc->tx_head != sc->tx_tail) { 52755881Smdodd if (sc->tx_tail != dest) { 528131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 52955881Smdodd sc->tx_last + XMT_Chain_Point); 530131192Simp CSR_WRITE_2(sc, IO_PORT_REG, dest); 53152286Smdodd } 532131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 53355881Smdodd sc->tx_last + XMT_Byte_Count); 534131192Simp i = CSR_READ_2(sc, IO_PORT_REG); 535131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, 53655881Smdodd sc->tx_last + XMT_Byte_Count); 537131192Simp CSR_WRITE_2(sc, IO_PORT_REG, i | Ch_bit); 53855881Smdodd } 53955881Smdodd 54055881Smdodd /* 54155881Smdodd * Resume normal operation of the card: 54255881Smdodd * - Make a dummy read to flush the DRAM write 54355881Smdodd * pipeline. 54455881Smdodd * - Enable receive and transmit interrupts. 54555881Smdodd * - Send Transmit or Resume_XMT command, as 54655881Smdodd * appropriate. 54755881Smdodd */ 548131192Simp CSR_READ_2(sc, IO_PORT_REG); 54921769Sjkh#ifdef EX_PSA_INTR 550131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 55121769Sjkh#endif 55255881Smdodd if (sc->tx_head == sc->tx_tail) { 553131192Simp CSR_WRITE_2(sc, XMT_BAR, dest); 554131192Simp CSR_WRITE_1(sc, CMD_REG, Transmit_CMD); 55555881Smdodd sc->tx_head = dest; 55655881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 55752286Smdodd } else { 558131192Simp CSR_WRITE_1(sc, CMD_REG, Resume_XMT_List_CMD); 55955881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 56052286Smdodd } 56155881Smdodd 56255881Smdodd sc->tx_last = dest; 56355881Smdodd sc->tx_tail = next; 56455881Smdodd 565106937Ssam BPF_MTAP(ifp, opkt); 56655881Smdodd 567179775Sjhb sc->tx_timeout = 2; 56855881Smdodd ifp->if_opackets++; 56955881Smdodd m_freem(opkt); 57055881Smdodd } else { 571148887Srwatson ifp->if_drv_flags |= IFF_DRV_OACTIVE; 57255881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 57352286Smdodd } 57421769Sjkh } 57521769Sjkh 57652286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 57721769Sjkh} 57821769Sjkh 57966440Simpvoid 58052286Smdoddex_stop(struct ex_softc *sc) 58121769Sjkh{ 582131192Simp 58352286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 58421769Sjkh 585179775Sjhb EX_ASSERT_LOCKED(sc); 58652286Smdodd /* 58752286Smdodd * Disable card operation: 58852286Smdodd * - Disable the interrupt line. 58952286Smdodd * - Flush transmission and disable reception. 59052286Smdodd * - Mask and clear all interrupts. 59152286Smdodd * - Reset the 82595. 59252286Smdodd */ 593131192Simp CSR_WRITE_1(sc, CMD_REG, Bank1_Sel); 594131192Simp CSR_WRITE_1(sc, REG1, CSR_READ_1(sc, REG1) & ~TriST_INT); 595131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 596131192Simp CSR_WRITE_1(sc, CMD_REG, Rcv_Stop); 59752286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 59852286Smdodd sc->tx_last = 0; /* XXX I think these two lines are not necessary, because ex_init will always be called again to reinit the interface. */ 599131192Simp CSR_WRITE_1(sc, MASK_REG, All_Int); 600131192Simp CSR_WRITE_1(sc, STATUS_REG, All_Int); 601131192Simp CSR_WRITE_1(sc, CMD_REG, Reset_CMD); 60252286Smdodd DELAY(200); 603179775Sjhb sc->ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); 604179775Sjhb sc->tx_timeout = 0; 605179775Sjhb callout_stop(&sc->timer); 60621769Sjkh 60752286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 60852286Smdodd 60952286Smdodd return; 61021769Sjkh} 61121769Sjkh 61259816Smdoddvoid 61355883Smdoddex_intr(void *arg) 61421769Sjkh{ 615131192Simp struct ex_softc *sc = (struct ex_softc *)arg; 616147256Sbrooks struct ifnet *ifp = sc->ifp; 617131192Simp int int_status, send_pkts; 618131192Simp int loops = 100; 61921769Sjkh 62055883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 62121769Sjkh 622179775Sjhb EX_LOCK(sc); 62352286Smdodd send_pkts = 0; 624131192Simp while (loops-- > 0 && 625131192Simp (int_status = CSR_READ_1(sc, STATUS_REG)) & (Tx_Int | Rx_Int)) { 626131192Simp /* don't loop forever */ 627131192Simp if (int_status == 0xff) 628131192Simp break; 62952286Smdodd if (int_status & Rx_Int) { 630131192Simp CSR_WRITE_1(sc, STATUS_REG, Rx_Int); 63152286Smdodd ex_rx_intr(sc); 63252286Smdodd } else if (int_status & Tx_Int) { 633131192Simp CSR_WRITE_1(sc, STATUS_REG, Tx_Int); 63452286Smdodd ex_tx_intr(sc); 63552286Smdodd send_pkts = 1; 63652286Smdodd } 63752286Smdodd } 638131192Simp if (loops == 0) 639131192Simp printf("100 loops are not enough\n"); 64021769Sjkh 64152286Smdodd /* 64252286Smdodd * If any packet has been transmitted, and there are queued packets to 64352286Smdodd * be sent, attempt to send more packets to the network card. 64452286Smdodd */ 645131192Simp if (send_pkts && (ifp->if_snd.ifq_head != NULL)) 646179775Sjhb ex_start_locked(ifp); 647179775Sjhb EX_UNLOCK(sc); 64852286Smdodd 64955883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 65052286Smdodd 65152286Smdodd return; 65221769Sjkh} 65321769Sjkh 65452286Smdoddstatic void 65552286Smdoddex_tx_intr(struct ex_softc *sc) 65621769Sjkh{ 657147256Sbrooks struct ifnet * ifp = sc->ifp; 65852286Smdodd int tx_status; 65921769Sjkh 66052286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 66121769Sjkh 66252286Smdodd /* 66352286Smdodd * - Cancel the watchdog. 66452286Smdodd * For all packets transmitted since last transmit interrupt: 66552286Smdodd * - Advance chain pointer to next queued packet. 66652286Smdodd * - Update statistics. 66752286Smdodd */ 66821769Sjkh 669179775Sjhb sc->tx_timeout = 0; 67021769Sjkh 67152286Smdodd while (sc->tx_head != sc->tx_tail) { 672131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->tx_head); 67321769Sjkh 674131192Simp if (! CSR_READ_2(sc, IO_PORT_REG) & Done_bit) 67552286Smdodd break; 67621769Sjkh 677131192Simp tx_status = CSR_READ_2(sc, IO_PORT_REG); 678131192Simp sc->tx_head = CSR_READ_2(sc, IO_PORT_REG); 67952286Smdodd 68052286Smdodd if (tx_status & TX_OK_bit) { 68152286Smdodd ifp->if_opackets++; 68252286Smdodd } else { 68352286Smdodd ifp->if_oerrors++; 68452286Smdodd } 68552286Smdodd 68652286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 68752286Smdodd } 68852286Smdodd 68952286Smdodd /* 69052286Smdodd * The card should be ready to accept more packets now. 69152286Smdodd */ 69252286Smdodd 693148887Srwatson ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 69452286Smdodd 69552286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 69652286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 69752286Smdodd 69852286Smdodd return; 69921769Sjkh} 70021769Sjkh 70152286Smdoddstatic void 70252286Smdoddex_rx_intr(struct ex_softc *sc) 70321769Sjkh{ 704147256Sbrooks struct ifnet * ifp = sc->ifp; 70552286Smdodd int rx_status; 70652286Smdodd int pkt_len; 70752286Smdodd int QQQ; 70852286Smdodd struct mbuf * m; 70952286Smdodd struct mbuf * ipkt; 71052286Smdodd struct ether_header * eh; 71121769Sjkh 71252286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 71321769Sjkh 71452286Smdodd /* 71552286Smdodd * For all packets received since last receive interrupt: 71652286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 71752286Smdodd * updating statistics. 71852286Smdodd * - If packet bad, just discard it, and update statistics. 71952286Smdodd * Finally, advance receive stop limit in card's memory to new location. 72052286Smdodd */ 72121769Sjkh 722131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->rx_head); 72321769Sjkh 724131192Simp while (CSR_READ_2(sc, IO_PORT_REG) == RCV_Done) { 72552286Smdodd 726131192Simp rx_status = CSR_READ_2(sc, IO_PORT_REG); 727131192Simp sc->rx_head = CSR_READ_2(sc, IO_PORT_REG); 728131192Simp QQQ = pkt_len = CSR_READ_2(sc, IO_PORT_REG); 72952286Smdodd 73052286Smdodd if (rx_status & RCV_OK_bit) { 731111119Simp MGETHDR(m, M_DONTWAIT, MT_DATA); 73252286Smdodd ipkt = m; 73352286Smdodd if (ipkt == NULL) { 73452286Smdodd ifp->if_iqdrops++; 73552286Smdodd } else { 73652286Smdodd ipkt->m_pkthdr.rcvif = ifp; 73752286Smdodd ipkt->m_pkthdr.len = pkt_len; 73852286Smdodd ipkt->m_len = MHLEN; 73952286Smdodd 74052286Smdodd while (pkt_len > 0) { 741136625Sglebius if (pkt_len >= MINCLSIZE) { 742111119Simp MCLGET(m, M_DONTWAIT); 74352286Smdodd if (m->m_flags & M_EXT) { 74452286Smdodd m->m_len = MCLBYTES; 74552286Smdodd } else { 74652286Smdodd m_freem(ipkt); 74752286Smdodd ifp->if_iqdrops++; 74852286Smdodd goto rx_another; 74952286Smdodd } 75052286Smdodd } 75152286Smdodd m->m_len = min(m->m_len, pkt_len); 75252286Smdodd 75321769Sjkh /* 75421769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 75521769Sjkh * except for the last one in an odd-length packet. 75621769Sjkh */ 75752286Smdodd 758131192Simp CSR_READ_MULTI_2(sc, IO_PORT_REG, 759131192Simp mtod(m, uint16_t *), m->m_len / 2); 76052286Smdodd 76152286Smdodd if (m->m_len & 1) { 762131192Simp *(mtod(m, caddr_t) + m->m_len - 1) = CSR_READ_1(sc, IO_PORT_REG); 76352286Smdodd } 76452286Smdodd pkt_len -= m->m_len; 76552286Smdodd 76652286Smdodd if (pkt_len > 0) { 767111119Simp MGET(m->m_next, M_DONTWAIT, MT_DATA); 76852286Smdodd if (m->m_next == NULL) { 76952286Smdodd m_freem(ipkt); 77052286Smdodd ifp->if_iqdrops++; 77152286Smdodd goto rx_another; 77252286Smdodd } 77352286Smdodd m = m->m_next; 77452286Smdodd m->m_len = MLEN; 77552286Smdodd } 77652286Smdodd } 77752286Smdodd eh = mtod(ipkt, struct ether_header *); 77852286Smdodd#ifdef EXDEBUG 77952286Smdodd if (debug_mask & Rcvd_Pkts) { 78052286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 78152286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 78252286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 78352286Smdodd } /* QQQ */ 78421769Sjkh } 78521769Sjkh#endif 786179775Sjhb EX_UNLOCK(sc); 787106937Ssam (*ifp->if_input)(ifp, ipkt); 788179775Sjhb EX_LOCK(sc); 78952286Smdodd ifp->if_ipackets++; 79052286Smdodd } 79152286Smdodd } else { 79252286Smdodd ifp->if_ierrors++; 79321769Sjkh } 794131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->rx_head); 79552286Smdoddrx_another: ; 79621769Sjkh } 79721769Sjkh 79852286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 799131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_upper_limit); 80052286Smdodd else 801131192Simp CSR_WRITE_2(sc, RCV_STOP_REG, sc->rx_head - 2); 80252286Smdodd 80352286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 80452286Smdodd 80552286Smdodd return; 80621769Sjkh} 80721769Sjkh 80821769Sjkh 80955883Smdoddstatic int 81055883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 81121769Sjkh{ 81252286Smdodd struct ex_softc * sc = ifp->if_softc; 81357987Smdodd struct ifreq * ifr = (struct ifreq *)data; 81452286Smdodd int error = 0; 81521769Sjkh 816121816Sbrooks DODEBUG(Start_End, printf("%s: ex_ioctl: start ", ifp->if_xname);); 81721769Sjkh 81852286Smdodd switch(cmd) { 81952286Smdodd case SIOCSIFADDR: 82052286Smdodd case SIOCGIFADDR: 82152286Smdodd case SIOCSIFMTU: 82252286Smdodd error = ether_ioctl(ifp, cmd, data); 82352286Smdodd break; 82421769Sjkh 82552286Smdodd case SIOCSIFFLAGS: 82652286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 827179775Sjhb EX_LOCK(sc); 82852286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 829148887Srwatson (ifp->if_drv_flags & IFF_DRV_RUNNING)) { 83052286Smdodd ex_stop(sc); 83152286Smdodd } else { 832179775Sjhb ex_init_locked(sc); 83352286Smdodd } 834179775Sjhb EX_UNLOCK(sc); 83552286Smdodd break; 83652286Smdodd case SIOCADDMULTI: 83752286Smdodd case SIOCDELMULTI: 838112731Smdodd ex_init(sc); 839112731Smdodd error = 0; 84052286Smdodd break; 84157987Smdodd case SIOCSIFMEDIA: 84257987Smdodd case SIOCGIFMEDIA: 84357987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 84457987Smdodd break; 84552286Smdodd default: 84652286Smdodd DODEBUG(Start_End, printf("unknown");); 84752286Smdodd error = EINVAL; 84852286Smdodd } 84921769Sjkh 850121816Sbrooks DODEBUG(Start_End, printf("\n%s: ex_ioctl: finish\n", ifp->if_xname);); 85152286Smdodd 85252286Smdodd return(error); 85321769Sjkh} 85421769Sjkh 855112731Smdoddstatic void 856112731Smdoddex_setmulti(struct ex_softc *sc) 857112731Smdodd{ 858112731Smdodd struct ifnet *ifp; 859112731Smdodd struct ifmultiaddr *maddr; 860131192Simp uint16_t *addr; 861112731Smdodd int count; 862112731Smdodd int timeout, status; 863112731Smdodd 864147256Sbrooks ifp = sc->ifp; 86521769Sjkh 866112731Smdodd count = 0; 867148654Srwatson IF_ADDR_LOCK(ifp); 868112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 869112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 870112731Smdodd continue; 871112731Smdodd count++; 872112731Smdodd } 873148654Srwatson IF_ADDR_UNLOCK(ifp); 874112731Smdodd 875112731Smdodd if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI) 876112731Smdodd || count > 63) { 877112731Smdodd /* Interface is in promiscuous mode or there are too many 878112731Smdodd * multicast addresses for the card to handle */ 879131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 880131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | Promisc_Mode); 881131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 882131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 883112731Smdodd } 884112731Smdodd else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) { 885112731Smdodd /* Program multicast addresses plus our MAC address 886112731Smdodd * into the filter */ 887131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 888131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) | Multi_IA); 889131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 890131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 891112731Smdodd 892112731Smdodd /* Borrow space from TX buffer; this should be safe 893112731Smdodd * as this is only called from ex_init */ 894112731Smdodd 895131192Simp CSR_WRITE_2(sc, HOST_ADDR_REG, sc->tx_lower_limit); 896131192Simp CSR_WRITE_2(sc, IO_PORT_REG, MC_Setup_CMD); 897131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 898131192Simp CSR_WRITE_2(sc, IO_PORT_REG, 0); 899131192Simp CSR_WRITE_2(sc, IO_PORT_REG, (count + 1) * 6); 900148654Srwatson 901148654Srwatson IF_ADDR_LOCK(ifp); 902112731Smdodd TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) { 903112731Smdodd if (maddr->ifma_addr->sa_family != AF_LINK) 904112731Smdodd continue; 905112731Smdodd 906131192Simp addr = (uint16_t*)LLADDR((struct sockaddr_dl *) 907112731Smdodd maddr->ifma_addr); 908131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 909131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 910131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 911112731Smdodd } 912148654Srwatson IF_ADDR_UNLOCK(ifp); 913112731Smdodd 914112731Smdodd /* Program our MAC address as well */ 915112731Smdodd /* XXX: Is this necessary? The Linux driver does this 916112731Smdodd * but the NetBSD driver does not */ 917152315Sru addr = (uint16_t*)IF_LLADDR(sc->ifp); 918131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 919131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 920131192Simp CSR_WRITE_2(sc, IO_PORT_REG, *addr++); 921112731Smdodd 922131192Simp CSR_READ_2(sc, IO_PORT_REG); 923131192Simp CSR_WRITE_2(sc, XMT_BAR, sc->tx_lower_limit); 924131192Simp CSR_WRITE_1(sc, CMD_REG, MC_Setup_CMD); 925112731Smdodd 926112731Smdodd sc->tx_head = sc->tx_lower_limit; 927112731Smdodd sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6; 928112731Smdodd 929112731Smdodd for (timeout=0; timeout<100; timeout++) { 930112731Smdodd DELAY(2); 931131192Simp if ((CSR_READ_1(sc, STATUS_REG) & Exec_Int) == 0) 932112731Smdodd continue; 933112731Smdodd 934131192Simp status = CSR_READ_1(sc, CMD_REG); 935131192Simp CSR_WRITE_1(sc, STATUS_REG, Exec_Int); 936112731Smdodd break; 937112731Smdodd } 938112731Smdodd 939112731Smdodd sc->tx_head = sc->tx_tail; 940112731Smdodd } 941112731Smdodd else 942112731Smdodd { 943112731Smdodd /* No multicast or promiscuous mode */ 944131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 945131192Simp CSR_WRITE_1(sc, REG2, CSR_READ_1(sc, REG2) & 0xDE); 946112731Smdodd /* ~(Multi_IA | Promisc_Mode) */ 947131192Simp CSR_WRITE_1(sc, REG3, CSR_READ_1(sc, REG3)); 948131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 949112731Smdodd } 950112731Smdodd} 951112731Smdodd 95252286Smdoddstatic void 95352286Smdoddex_reset(struct ex_softc *sc) 95421769Sjkh{ 95521769Sjkh 95652286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 95721769Sjkh 958179775Sjhb EX_ASSERT_LOCKED(sc); 95952286Smdodd ex_stop(sc); 960179775Sjhb ex_init_locked(sc); 96121769Sjkh 96252286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 96352286Smdodd 96452286Smdodd return; 96521769Sjkh} 96621769Sjkh 96752286Smdoddstatic void 968179775Sjhbex_watchdog(void *arg) 96921769Sjkh{ 970179775Sjhb struct ex_softc * sc = arg; 971179775Sjhb struct ifnet *ifp = sc->ifp; 97221769Sjkh 973179775Sjhb if (sc->tx_timeout && --sc->tx_timeout == 0) { 974179775Sjhb DODEBUG(Start_End, if_printf(ifp, "ex_watchdog: start\n");); 97521769Sjkh 976179775Sjhb ifp->if_drv_flags &= ~IFF_DRV_OACTIVE; 97721769Sjkh 978179775Sjhb DODEBUG(Status, printf("OIDLE watchdog\n");); 97952286Smdodd 980179775Sjhb ifp->if_oerrors++; 981179775Sjhb ex_reset(sc); 982179775Sjhb ex_start_locked(ifp); 98352286Smdodd 984179775Sjhb DODEBUG(Start_End, if_printf(ifp, "ex_watchdog: finish\n");); 985179775Sjhb } 98652286Smdodd 987179775Sjhb callout_reset(&sc->timer, hz, ex_watchdog, sc); 98821769Sjkh} 98921769Sjkh 99057987Smdoddstatic int 991131192Simpex_get_media(struct ex_softc *sc) 99259816Smdodd{ 993112764Smdodd int current; 994112764Smdodd int media; 99559816Smdodd 996131192Simp media = ex_eeprom_read(sc, EE_W5); 997112764Smdodd 998131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 999131192Simp current = CSR_READ_1(sc, REG3); 1000131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 100159816Smdodd 1002112764Smdodd if ((current & TPE_bit) && (media & EE_W5_PORT_TPE)) 100359816Smdodd return(IFM_ETHER|IFM_10_T); 1004112764Smdodd if ((current & BNC_bit) && (media & EE_W5_PORT_BNC)) 100559816Smdodd return(IFM_ETHER|IFM_10_2); 100659816Smdodd 1007112764Smdodd if (media & EE_W5_PORT_AUI) 1008112764Smdodd return (IFM_ETHER|IFM_10_5); 1009112764Smdodd 1010112764Smdodd return (IFM_ETHER|IFM_AUTO); 101159816Smdodd} 101259816Smdodd 101359816Smdoddstatic int 1014131192Simpex_ifmedia_upd(ifp) 101557987Smdodd struct ifnet * ifp; 101657987Smdodd{ 1017112764Smdodd struct ex_softc * sc = ifp->if_softc; 101821769Sjkh 1019112764Smdodd if (IFM_TYPE(sc->ifmedia.ifm_media) != IFM_ETHER) 1020112764Smdodd return EINVAL; 1021112764Smdodd 102257987Smdodd return (0); 102357987Smdodd} 102457987Smdodd 102557987Smdoddstatic void 102657987Smdoddex_ifmedia_sts(ifp, ifmr) 102757987Smdodd struct ifnet * ifp; 102857987Smdodd struct ifmediareq * ifmr; 102957987Smdodd{ 103057987Smdodd struct ex_softc * sc = ifp->if_softc; 103157987Smdodd 1032179775Sjhb EX_LOCK(sc); 1033131192Simp ifmr->ifm_active = ex_get_media(sc); 1034112764Smdodd ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; 1035179775Sjhb EX_UNLOCK(sc); 103657987Smdodd 103757987Smdodd return; 103857987Smdodd} 103957987Smdodd 104059816Smdoddu_short 1041131192Simpex_eeprom_read(struct ex_softc *sc, int location) 104221769Sjkh{ 104321769Sjkh int i; 104421769Sjkh u_short data = 0; 104521769Sjkh int read_cmd = location | EE_READ_CMD; 104621769Sjkh short ctrl_val = EECS; 104721769Sjkh 1048131192Simp CSR_WRITE_1(sc, CMD_REG, Bank2_Sel); 1049131192Simp CSR_WRITE_1(sc, EEPROM_REG, EECS); 105021769Sjkh for (i = 8; i >= 0; i--) { 105121769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 1052131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval); 1053131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval | EESK); 105421769Sjkh DELAY(3); 1055131192Simp CSR_WRITE_1(sc, EEPROM_REG, outval); 105621769Sjkh DELAY(2); 105721769Sjkh } 1058131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 105921769Sjkh 106021769Sjkh for (i = 16; i > 0; i--) { 1061131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val | EESK); 106221769Sjkh DELAY(3); 1063131192Simp data = (data << 1) | 1064131192Simp ((CSR_READ_1(sc, EEPROM_REG) & EEDO) ? 1 : 0); 1065131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 106621769Sjkh DELAY(2); 106721769Sjkh } 106821769Sjkh 106921769Sjkh ctrl_val &= ~EECS; 1070131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val | EESK); 107121769Sjkh DELAY(3); 1072131192Simp CSR_WRITE_1(sc, EEPROM_REG, ctrl_val); 107321769Sjkh DELAY(2); 1074131192Simp CSR_WRITE_1(sc, CMD_REG, Bank0_Sel); 107521769Sjkh return(data); 107621769Sjkh} 1077