if_ex.c revision 59816
121769Sjkh/* 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 * 2750477Speter * $FreeBSD: head/sys/dev/ex/if_ex.c 59816 2000-05-01 09:05:19Z mdodd $ 2852286Smdodd * 2952286Smdodd * MAINTAINER: Matthew N. Dodd <winter@jurai.net> 3052286Smdodd * <mdodd@FreeBSD.org> 3121769Sjkh */ 3221769Sjkh 3321769Sjkh/* 3429877Smsmith * Intel EtherExpress Pro/10, Pro/10+ Ethernet driver 3521769Sjkh * 3621769Sjkh * Revision history: 3721769Sjkh * 3821769Sjkh * 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast. 3921769Sjkh */ 4021769Sjkh 4121769Sjkh#include "ex.h" 4221769Sjkh 4321769Sjkh#include <sys/param.h> 4421769Sjkh#include <sys/systm.h> 4552286Smdodd#include <sys/kernel.h> 4624204Sbde#include <sys/sockio.h> 4721769Sjkh#include <sys/mbuf.h> 4821769Sjkh#include <sys/socket.h> 4921769Sjkh 5052286Smdodd#include <sys/module.h> 5152286Smdodd#include <sys/bus.h> 5252286Smdodd 5352286Smdodd#include <machine/bus.h> 5452286Smdodd#include <machine/resource.h> 5552286Smdodd#include <sys/rman.h> 5652286Smdodd 5757987Smdodd#include <net/if.h> 5857987Smdodd#include <net/if_arp.h> 5957987Smdodd#include <net/if_media.h> 6050026Smdodd#include <net/ethernet.h> 6157987Smdodd#include <net/bpf.h> 6221769Sjkh 6350026Smdodd#include <netinet/in.h> 6450026Smdodd#include <netinet/if_ether.h> 6550026Smdodd 6621769Sjkh#include <machine/clock.h> 6721769Sjkh 6852286Smdodd#include <isa/isavar.h> 6952286Smdodd#include <isa/pnpvar.h> 7052286Smdodd 7155953Speter#include <dev/ex/if_exreg.h> 7259816Smdodd#include <dev/ex/if_exvar.h> 7321769Sjkh 7421769Sjkh#ifdef EXDEBUG 7552286Smdodd# define Start_End 1 7652286Smdodd# define Rcvd_Pkts 2 7752286Smdodd# define Sent_Pkts 4 7852286Smdodd# define Status 8 7921769Sjkhstatic int debug_mask = 0; 8021769Sjkhstatic int exintr_count = 0; 8152286Smdodd# define DODEBUG(level, action) if (level & debug_mask) action 8221769Sjkh#else 8352286Smdodd# define DODEBUG(level, action) 8421769Sjkh#endif 8521769Sjkh 8659816Smdoddchar irq2eemap[] = 8752286Smdodd { -1, -1, 0, 1, -1, 2, -1, -1, -1, 0, 3, 4, -1, -1, -1, -1 }; 8859816Smdoddu_char ee2irqmap[] = 8952286Smdodd { 9, 3, 5, 10, 11, 0, 0, 0 }; 9059816Smdodd 9159816Smdoddchar plus_irq2eemap[] = 9252286Smdodd { -1, -1, -1, 0, 1, 2, -1, 3, -1, 4, 5, 6, 7, -1, -1, -1 }; 9359816Smdoddu_char plus_ee2irqmap[] = 9452286Smdodd { 3, 4, 5, 7, 9, 10, 11, 12 }; 9521769Sjkh 9652286Smdodd/* Network Interface Functions */ 9752286Smdoddstatic void ex_init __P((void *)); 9852286Smdoddstatic void ex_start __P((struct ifnet *)); 9952286Smdoddstatic int ex_ioctl __P((struct ifnet *, u_long, caddr_t)); 10052286Smdoddstatic void ex_watchdog __P((struct ifnet *)); 10121769Sjkh 10257987Smdodd/* ifmedia Functions */ 10357987Smdoddstatic int ex_ifmedia_upd __P((struct ifnet *)); 10457987Smdoddstatic void ex_ifmedia_sts __P((struct ifnet *, struct ifmediareq *)); 10557987Smdodd 10659816Smdoddstatic int ex_get_media __P((u_int32_t iobase)); 10759816Smdodd 10852286Smdoddstatic void ex_stop __P((struct ex_softc *)); 10952286Smdoddstatic void ex_reset __P((struct ex_softc *)); 11052286Smdodd 11152286Smdoddstatic void ex_tx_intr __P((struct ex_softc *)); 11252286Smdoddstatic void ex_rx_intr __P((struct ex_softc *)); 11352286Smdodd 11459816Smdoddint 11559816Smdoddlook_for_card (u_int32_t iobase) 11621769Sjkh{ 11721769Sjkh int count1, count2; 11821769Sjkh 11921769Sjkh /* 12021769Sjkh * Check for the i82595 signature, and check that the round robin 12121769Sjkh * counter actually advances. 12221769Sjkh */ 12321769Sjkh if (((count1 = inb(iobase + ID_REG)) & Id_Mask) != Id_Sig) 12421769Sjkh return(0); 12521769Sjkh count2 = inb(iobase + ID_REG); 12621769Sjkh count2 = inb(iobase + ID_REG); 12721769Sjkh count2 = inb(iobase + ID_REG); 12852286Smdodd 12921769Sjkh return((count2 & Counter_bits) == ((count1 + 0xc0) & Counter_bits)); 13021769Sjkh} 13121769Sjkh 13259816Smdoddvoid 13352286Smdoddex_get_address (u_int32_t iobase, u_char *enaddr) 13421769Sjkh{ 13552286Smdodd u_int16_t eaddr_tmp; 13621769Sjkh 13752286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Lo); 13852286Smdodd enaddr[5] = eaddr_tmp & 0xff; 13952286Smdodd enaddr[4] = eaddr_tmp >> 8; 14052286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Mid); 14152286Smdodd enaddr[3] = eaddr_tmp & 0xff; 14252286Smdodd enaddr[2] = eaddr_tmp >> 8; 14352286Smdodd eaddr_tmp = eeprom_read(iobase, EE_Eth_Addr_Hi); 14452286Smdodd enaddr[1] = eaddr_tmp & 0xff; 14552286Smdodd enaddr[0] = eaddr_tmp >> 8; 14652286Smdodd 14752286Smdodd return; 14852286Smdodd} 14921769Sjkh 15059816Smdoddint 15152286Smdoddex_card_type (u_char *enaddr) 15252286Smdodd{ 15352286Smdodd if ((enaddr[0] == 0x00) && (enaddr[1] == 0xA0) && (enaddr[2] == 0xC9)) 15452286Smdodd return (CARD_TYPE_EX_10_PLUS); 15552286Smdodd 15652286Smdodd return (CARD_TYPE_EX_10); 15752286Smdodd} 15852286Smdodd 15955882Smdodd/* 16059816Smdodd * Caller is responsible for eventually calling 16159816Smdodd * ex_release_resources() on failure. 16255882Smdodd */ 16359816Smdoddint 16459816Smdoddex_alloc_resources (device_t dev) 16555882Smdodd{ 16659816Smdodd struct ex_softc * sc = device_get_softc(dev); 16759816Smdodd int error = 0; 16855882Smdodd 16959816Smdodd sc->ioport = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->ioport_rid, 17059816Smdodd 0, ~0, 1, RF_ACTIVE); 17159816Smdodd if (!sc->ioport) { 17259816Smdodd device_printf(dev, "No I/O space?!\n"); 17359816Smdodd error = ENOMEM; 17459816Smdodd goto bad; 17559816Smdodd } 17657989Smdodd 17759816Smdodd sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->irq_rid, 17859816Smdodd 0, ~0, 1, RF_ACTIVE); 17955882Smdodd 18059816Smdodd if (!sc->irq) { 18159816Smdodd device_printf(dev, "No IRQ?!\n"); 18259816Smdodd error = ENOMEM; 18359816Smdodd goto bad; 18455882Smdodd } 18555882Smdodd 18659816Smdoddbad: 18759816Smdodd return (error); 18855882Smdodd} 18955882Smdodd 19059816Smdoddvoid 19159816Smdoddex_release_resources (device_t dev) 19252286Smdodd{ 19359816Smdodd struct ex_softc * sc = device_get_softc(dev); 19452286Smdodd 19559816Smdodd if (sc->ih) { 19659816Smdodd bus_teardown_intr(dev, sc->irq, sc->ih); 19759816Smdodd sc->ih = NULL; 19852286Smdodd } 19952286Smdodd 20059816Smdodd if (sc->ioport) { 20159816Smdodd bus_release_resource(dev, SYS_RES_IOPORT, 20259816Smdodd sc->ioport_rid, sc->ioport); 20359816Smdodd sc->ioport = NULL; 20452286Smdodd } 20552286Smdodd 20659816Smdodd if (sc->irq) { 20759816Smdodd bus_release_resource(dev, SYS_RES_IRQ, 20859816Smdodd sc->irq_rid, sc->irq); 20959816Smdodd sc->irq = NULL; 21057989Smdodd } 21157989Smdodd 21259816Smdodd return; 21352286Smdodd} 21452286Smdodd 21559816Smdoddint 21659816Smdoddex_attach(device_t dev) 21752286Smdodd{ 21852286Smdodd struct ex_softc * sc = device_get_softc(dev); 21952286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 22057987Smdodd struct ifmedia * ifm; 22152286Smdodd int unit = device_get_unit(dev); 22257987Smdodd u_int16_t temp; 22352286Smdodd 22429877Smsmith /* work out which set of irq <-> internal tables to use */ 22552286Smdodd if (ex_card_type(sc->arpcom.ac_enaddr) == CARD_TYPE_EX_10_PLUS) { 22629877Smsmith sc->irq2ee = plus_irq2eemap; 22729877Smsmith sc->ee2irq = plus_ee2irqmap; 22852286Smdodd } else { 22929877Smsmith sc->irq2ee = irq2eemap; 23029877Smsmith sc->ee2irq = ee2irqmap; 23129877Smsmith } 23229877Smsmith 23321769Sjkh sc->mem_size = CARD_RAM_SIZE; /* XXX This should be read from the card itself. */ 23421769Sjkh 23521769Sjkh /* 23621769Sjkh * Initialize the ifnet structure. 23721769Sjkh */ 23821769Sjkh ifp->if_softc = sc; 23921769Sjkh ifp->if_unit = unit; 24021769Sjkh ifp->if_name = "ex"; 24155883Smdodd ifp->if_mtu = ETHERMTU; 24255883Smdodd ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST /* XXX not done yet. | IFF_MULTICAST */; 24321769Sjkh ifp->if_output = ether_output; 24421769Sjkh ifp->if_start = ex_start; 24521769Sjkh ifp->if_ioctl = ex_ioctl; 24621769Sjkh ifp->if_watchdog = ex_watchdog; 24755883Smdodd ifp->if_init = ex_init; 24855883Smdodd ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; 24921769Sjkh 25057987Smdodd ifmedia_init(&sc->ifmedia, 0, ex_ifmedia_upd, ex_ifmedia_sts); 25157987Smdodd 25257987Smdodd temp = eeprom_read(sc->iobase, EE_W5); 25357987Smdodd if (temp & EE_W5_PORT_TPE) 25457987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); 25557987Smdodd if (temp & EE_W5_PORT_BNC) 25657987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); 25757987Smdodd if (temp & EE_W5_PORT_AUI) 25857987Smdodd ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); 25957987Smdodd 26057987Smdodd ifmedia_set(&sc->ifmedia, ex_get_media(sc->iobase)); 26157987Smdodd 26257987Smdodd ifm = &sc->ifmedia; 26357987Smdodd ifm->ifm_media = ifm->ifm_cur->ifm_media; 26457987Smdodd ex_ifmedia_upd(ifp); 26557987Smdodd 26621769Sjkh /* 26721769Sjkh * Attach the interface. 26821769Sjkh */ 26921769Sjkh if_attach(ifp); 27021769Sjkh ether_ifattach(ifp); 27121769Sjkh 27255883Smdodd device_printf(sc->dev, "Ethernet address %6D\n", 27355883Smdodd sc->arpcom.ac_enaddr, ":"); 27421769Sjkh /* 27521769Sjkh * If BPF is in the kernel, call the attach for it 27621769Sjkh */ 27721769Sjkh bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); 27852286Smdodd 27952286Smdodd return(0); 28021769Sjkh} 28121769Sjkh 28255883Smdoddstatic void 28355883Smdoddex_init(void *xsc) 28421769Sjkh{ 28552286Smdodd struct ex_softc * sc = (struct ex_softc *) xsc; 28652286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 28752286Smdodd int s; 28852286Smdodd int i; 28952286Smdodd register int iobase = sc->iobase; 29052286Smdodd unsigned short temp_reg; 29121769Sjkh 29221769Sjkh DODEBUG(Start_End, printf("ex_init%d: start\n", ifp->if_unit);); 29321769Sjkh 29452286Smdodd if (ifp->if_addrhead.tqh_first == NULL) { 29552286Smdodd return; 29652286Smdodd } 29721769Sjkh s = splimp(); 29859816Smdodd ifp->if_timer = 0; 29921769Sjkh 30021769Sjkh /* 30121769Sjkh * Load the ethernet address into the card. 30221769Sjkh */ 30321769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 30421769Sjkh temp_reg = inb(iobase + EEPROM_REG); 30552286Smdodd if (temp_reg & Trnoff_Enable) { 30652286Smdodd outb(iobase + EEPROM_REG, temp_reg & ~Trnoff_Enable); 30752286Smdodd } 30852286Smdodd for (i = 0; i < ETHER_ADDR_LEN; i++) { 30952286Smdodd outb(iobase + I_ADDR_REG0 + i, sc->arpcom.ac_enaddr[i]); 31052286Smdodd } 31121769Sjkh /* 31221769Sjkh * - Setup transmit chaining and discard bad received frames. 31321769Sjkh * - Match broadcast. 31421769Sjkh * - Clear test mode. 31521769Sjkh * - Set receiving mode. 31621769Sjkh * - Set IRQ number. 31721769Sjkh */ 31821769Sjkh outb(iobase + REG1, inb(iobase + REG1) | Tx_Chn_Int_Md | Tx_Chn_ErStp | Disc_Bad_Fr); 31921769Sjkh outb(iobase + REG2, inb(iobase + REG2) | No_SA_Ins | RX_CRC_InMem); 32026545Sgibbs outb(iobase + REG3, inb(iobase + REG3) & 0x3f /* XXX constants. */ ); 32121769Sjkh outb(iobase + CMD_REG, Bank1_Sel); 32229877Smsmith outb(iobase + INT_NO_REG, (inb(iobase + INT_NO_REG) & 0xf8) | sc->irq2ee[sc->irq_no]); 32321769Sjkh 32421769Sjkh /* 32521769Sjkh * Divide the available memory in the card into rcv and xmt buffers. 32621769Sjkh * By default, I use the first 3/4 of the memory for the rcv buffer, 32721769Sjkh * and the remaining 1/4 of the memory for the xmt buffer. 32821769Sjkh */ 32921769Sjkh sc->rx_mem_size = sc->mem_size * 3 / 4; 33021769Sjkh sc->tx_mem_size = sc->mem_size - sc->rx_mem_size; 33121769Sjkh sc->rx_lower_limit = 0x0000; 33221769Sjkh sc->rx_upper_limit = sc->rx_mem_size - 2; 33321769Sjkh sc->tx_lower_limit = sc->rx_mem_size; 33421769Sjkh sc->tx_upper_limit = sc->mem_size - 2; 33521769Sjkh outb(iobase + RCV_LOWER_LIMIT_REG, sc->rx_lower_limit >> 8); 33621769Sjkh outb(iobase + RCV_UPPER_LIMIT_REG, sc->rx_upper_limit >> 8); 33721769Sjkh outb(iobase + XMT_LOWER_LIMIT_REG, sc->tx_lower_limit >> 8); 33821769Sjkh outb(iobase + XMT_UPPER_LIMIT_REG, sc->tx_upper_limit >> 8); 33921769Sjkh 34021769Sjkh /* 34121769Sjkh * Enable receive and transmit interrupts, and clear any pending int. 34221769Sjkh */ 34321769Sjkh outb(iobase + REG1, inb(iobase + REG1) | TriST_INT); 34421769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 34521769Sjkh outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 34621769Sjkh outb(iobase + STATUS_REG, All_Int); 34721769Sjkh 34821769Sjkh /* 34921769Sjkh * Initialize receive and transmit ring buffers. 35021769Sjkh */ 35121769Sjkh outw(iobase + RCV_BAR, sc->rx_lower_limit); 35221769Sjkh sc->rx_head = sc->rx_lower_limit; 35326545Sgibbs outw(iobase + RCV_STOP_REG, sc->rx_upper_limit | 0xfe); 35421769Sjkh outw(iobase + XMT_BAR, sc->tx_lower_limit); 35521769Sjkh sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 35621769Sjkh 35721769Sjkh ifp->if_flags |= IFF_RUNNING; 35821769Sjkh ifp->if_flags &= ~IFF_OACTIVE; 35921769Sjkh DODEBUG(Status, printf("OIDLE init\n");); 36021769Sjkh 36121769Sjkh /* 36221769Sjkh * Final reset of the board, and enable operation. 36321769Sjkh */ 36421769Sjkh outb(iobase + CMD_REG, Sel_Reset_CMD); 36521769Sjkh DELAY(2); 36621769Sjkh outb(iobase + CMD_REG, Rcv_Enable_CMD); 36721769Sjkh 36821769Sjkh ex_start(ifp); 36921769Sjkh splx(s); 37021769Sjkh 37121769Sjkh DODEBUG(Start_End, printf("ex_init%d: finish\n", ifp->if_unit);); 37221769Sjkh} 37321769Sjkh 37421769Sjkh 37555883Smdoddstatic void 37652286Smdoddex_start(struct ifnet *ifp) 37721769Sjkh{ 37852286Smdodd struct ex_softc * sc = ifp->if_softc; 37952286Smdodd int iobase = sc->iobase; 38052286Smdodd int i, s, len, data_len, avail, dest, next; 38152286Smdodd unsigned char tmp16[2]; 38252286Smdodd struct mbuf * opkt; 38352286Smdodd struct mbuf * m; 38421769Sjkh 38552286Smdodd DODEBUG(Start_End, printf("ex_start%d: start\n", unit);); 38621769Sjkh 38752286Smdodd s = splimp(); 38821769Sjkh 38952286Smdodd /* 39052286Smdodd * Main loop: send outgoing packets to network card until there are no 39152286Smdodd * more packets left, or the card cannot accept any more yet. 39252286Smdodd */ 39352286Smdodd while (((opkt = ifp->if_snd.ifq_head) != NULL) && 39452286Smdodd !(ifp->if_flags & IFF_OACTIVE)) { 39521769Sjkh 39652286Smdodd /* 39752286Smdodd * Ensure there is enough free transmit buffer space for 39852286Smdodd * this packet, including its header. Note: the header 39952286Smdodd * cannot wrap around the end of the transmit buffer and 40052286Smdodd * must be kept together, so we allow space for twice the 40152286Smdodd * length of the header, just in case. 40252286Smdodd */ 40321769Sjkh 40452286Smdodd for (len = 0, m = opkt; m != NULL; m = m->m_next) { 40552286Smdodd len += m->m_len; 40652286Smdodd } 40752286Smdodd 40852286Smdodd data_len = len; 40952286Smdodd 41052286Smdodd DODEBUG(Sent_Pkts, printf("1. Sending packet with %d data bytes. ", data_len);); 41152286Smdodd 41252286Smdodd if (len & 1) { 41352286Smdodd len += XMT_HEADER_LEN + 1; 41452286Smdodd } else { 41552286Smdodd len += XMT_HEADER_LEN; 41652286Smdodd } 41752286Smdodd 41852286Smdodd if ((i = sc->tx_tail - sc->tx_head) >= 0) { 41952286Smdodd avail = sc->tx_mem_size - i; 42052286Smdodd } else { 42152286Smdodd avail = -i; 42252286Smdodd } 42352286Smdodd 42452286Smdodd DODEBUG(Sent_Pkts, printf("i=%d, avail=%d\n", i, avail);); 42552286Smdodd 42652286Smdodd if (avail >= len + XMT_HEADER_LEN) { 42752286Smdodd IF_DEQUEUE(&ifp->if_snd, opkt); 42852286Smdodd 42921769Sjkh#ifdef EX_PSA_INTR 43052286Smdodd /* 43152286Smdodd * Disable rx and tx interrupts, to avoid corruption 43252286Smdodd * of the host address register by interrupt service 43352286Smdodd * routines. 43452286Smdodd * XXX Is this necessary with splimp() enabled? 43552286Smdodd */ 43652286Smdodd outb(iobase + MASK_REG, All_Int); 43721769Sjkh#endif 43821769Sjkh 43952286Smdodd /* 44052286Smdodd * Compute the start and end addresses of this 44152286Smdodd * frame in the tx buffer. 44252286Smdodd */ 44352286Smdodd dest = sc->tx_tail; 44452286Smdodd next = dest + len; 44521769Sjkh 44652286Smdodd if (next > sc->tx_upper_limit) { 44752286Smdodd if ((sc->tx_upper_limit + 2 - sc->tx_tail) <= 44852286Smdodd XMT_HEADER_LEN) { 44952286Smdodd dest = sc->tx_lower_limit; 45052286Smdodd next = dest + len; 45155881Smdodd } else { 45255881Smdodd next = sc->tx_lower_limit + 45355881Smdodd next - sc->tx_upper_limit - 2; 45452286Smdodd } 45552286Smdodd } 45621769Sjkh 45752286Smdodd /* 45852286Smdodd * Build the packet frame in the card's ring buffer. 45952286Smdodd */ 46052286Smdodd DODEBUG(Sent_Pkts, printf("2. dest=%d, next=%d. ", dest, next);); 46121769Sjkh 46252286Smdodd outw(iobase + HOST_ADDR_REG, dest); 46352286Smdodd outw(iobase + IO_PORT_REG, Transmit_CMD); 46452286Smdodd outw(iobase + IO_PORT_REG, 0); 46552286Smdodd outw(iobase + IO_PORT_REG, next); 46652286Smdodd outw(iobase + IO_PORT_REG, data_len); 46721769Sjkh 46852286Smdodd /* 46952286Smdodd * Output the packet data to the card. Ensure all 47052286Smdodd * transfers are 16-bit wide, even if individual 47152286Smdodd * mbufs have odd length. 47252286Smdodd */ 47321769Sjkh 47452286Smdodd for (m = opkt, i = 0; m != NULL; m = m->m_next) { 47552286Smdodd DODEBUG(Sent_Pkts, printf("[%d]", m->m_len);); 47652286Smdodd if (i) { 47752286Smdodd tmp16[1] = *(mtod(m, caddr_t)); 47852286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 47952286Smdodd } 48052286Smdodd outsw(iobase + IO_PORT_REG, 48152286Smdodd mtod(m, caddr_t) + i, (m->m_len - i) / 2); 48252286Smdodd 48352286Smdodd if ((i = (m->m_len - i) & 1) != 0) { 48452286Smdodd tmp16[0] = *(mtod(m, caddr_t) + 48552286Smdodd m->m_len - 1); 48652286Smdodd } 48752286Smdodd } 48852286Smdodd if (i) { 48952286Smdodd outsw(iobase + IO_PORT_REG, tmp16, 1); 49055881Smdodd } 49152286Smdodd 49255881Smdodd /* 49355881Smdodd * If there were other frames chained, update the 49455881Smdodd * chain in the last one. 49555881Smdodd */ 49655881Smdodd if (sc->tx_head != sc->tx_tail) { 49755881Smdodd if (sc->tx_tail != dest) { 49852286Smdodd outw(iobase + HOST_ADDR_REG, 49955881Smdodd sc->tx_last + XMT_Chain_Point); 50055881Smdodd outw(iobase + IO_PORT_REG, dest); 50152286Smdodd } 50255881Smdodd outw(iobase + HOST_ADDR_REG, 50355881Smdodd sc->tx_last + XMT_Byte_Count); 50455881Smdodd i = inw(iobase + IO_PORT_REG); 50555881Smdodd outw(iobase + HOST_ADDR_REG, 50655881Smdodd sc->tx_last + XMT_Byte_Count); 50755881Smdodd outw(iobase + IO_PORT_REG, i | Ch_bit); 50855881Smdodd } 50955881Smdodd 51055881Smdodd /* 51155881Smdodd * Resume normal operation of the card: 51255881Smdodd * - Make a dummy read to flush the DRAM write 51355881Smdodd * pipeline. 51455881Smdodd * - Enable receive and transmit interrupts. 51555881Smdodd * - Send Transmit or Resume_XMT command, as 51655881Smdodd * appropriate. 51755881Smdodd */ 51855881Smdodd inw(iobase + IO_PORT_REG); 51921769Sjkh#ifdef EX_PSA_INTR 52055881Smdodd outb(iobase + MASK_REG, All_Int & ~(Rx_Int | Tx_Int)); 52121769Sjkh#endif 52255881Smdodd if (sc->tx_head == sc->tx_tail) { 52355881Smdodd outw(iobase + XMT_BAR, dest); 52455881Smdodd outb(iobase + CMD_REG, Transmit_CMD); 52555881Smdodd sc->tx_head = dest; 52655881Smdodd DODEBUG(Sent_Pkts, printf("Transmit\n");); 52752286Smdodd } else { 52855881Smdodd outb(iobase + CMD_REG, Resume_XMT_List_CMD); 52955881Smdodd DODEBUG(Sent_Pkts, printf("Resume\n");); 53052286Smdodd } 53155881Smdodd 53255881Smdodd sc->tx_last = dest; 53355881Smdodd sc->tx_tail = next; 53455881Smdodd 53555881Smdodd if (ifp->if_bpf != NULL) { 53655881Smdodd bpf_mtap(ifp, opkt); 53755881Smdodd } 53855881Smdodd 53955881Smdodd ifp->if_timer = 2; 54055881Smdodd ifp->if_opackets++; 54155881Smdodd m_freem(opkt); 54255881Smdodd } else { 54355881Smdodd ifp->if_flags |= IFF_OACTIVE; 54455881Smdodd DODEBUG(Status, printf("OACTIVE start\n");); 54552286Smdodd } 54621769Sjkh } 54721769Sjkh 54852286Smdodd splx(s); 54921769Sjkh 55052286Smdodd DODEBUG(Start_End, printf("ex_start%d: finish\n", unit);); 55121769Sjkh} 55221769Sjkh 55352286Smdoddstatic void 55452286Smdoddex_stop(struct ex_softc *sc) 55521769Sjkh{ 55652286Smdodd int iobase = sc->iobase; 55721769Sjkh 55852286Smdodd DODEBUG(Start_End, printf("ex_stop%d: start\n", unit);); 55921769Sjkh 56052286Smdodd /* 56152286Smdodd * Disable card operation: 56252286Smdodd * - Disable the interrupt line. 56352286Smdodd * - Flush transmission and disable reception. 56452286Smdodd * - Mask and clear all interrupts. 56552286Smdodd * - Reset the 82595. 56652286Smdodd */ 56752286Smdodd outb(iobase + CMD_REG, Bank1_Sel); 56852286Smdodd outb(iobase + REG1, inb(iobase + REG1) & ~TriST_INT); 56952286Smdodd outb(iobase + CMD_REG, Bank0_Sel); 57052286Smdodd outb(iobase + CMD_REG, Rcv_Stop); 57152286Smdodd sc->tx_head = sc->tx_tail = sc->tx_lower_limit; 57252286Smdodd 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. */ 57352286Smdodd outb(iobase + MASK_REG, All_Int); 57452286Smdodd outb(iobase + STATUS_REG, All_Int); 57552286Smdodd outb(iobase + CMD_REG, Reset_CMD); 57652286Smdodd DELAY(200); 57721769Sjkh 57852286Smdodd DODEBUG(Start_End, printf("ex_stop%d: finish\n", unit);); 57952286Smdodd 58052286Smdodd return; 58121769Sjkh} 58221769Sjkh 58359816Smdoddvoid 58455883Smdoddex_intr(void *arg) 58521769Sjkh{ 58652286Smdodd struct ex_softc * sc = (struct ex_softc *)arg; 58752286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 58852286Smdodd int iobase = sc->iobase; 58952286Smdodd int int_status, send_pkts; 59021769Sjkh 59155883Smdodd DODEBUG(Start_End, printf("ex_intr%d: start\n", unit);); 59221769Sjkh 59321769Sjkh#ifdef EXDEBUG 59421769Sjkh if (++exintr_count != 1) 59521769Sjkh printf("WARNING: nested interrupt (%d). Mail the author.\n", exintr_count); 59621769Sjkh#endif 59721769Sjkh 59852286Smdodd send_pkts = 0; 59952286Smdodd while ((int_status = inb(iobase + STATUS_REG)) & (Tx_Int | Rx_Int)) { 60052286Smdodd if (int_status & Rx_Int) { 60152286Smdodd outb(iobase + STATUS_REG, Rx_Int); 60221769Sjkh 60352286Smdodd ex_rx_intr(sc); 60452286Smdodd } else if (int_status & Tx_Int) { 60552286Smdodd outb(iobase + STATUS_REG, Tx_Int); 60621769Sjkh 60752286Smdodd ex_tx_intr(sc); 60852286Smdodd send_pkts = 1; 60952286Smdodd } 61052286Smdodd } 61121769Sjkh 61252286Smdodd /* 61352286Smdodd * If any packet has been transmitted, and there are queued packets to 61452286Smdodd * be sent, attempt to send more packets to the network card. 61552286Smdodd */ 61652286Smdodd 61752286Smdodd if (send_pkts && (ifp->if_snd.ifq_head != NULL)) { 61852286Smdodd ex_start(ifp); 61952286Smdodd } 62052286Smdodd 62121769Sjkh#ifdef EXDEBUG 62221769Sjkh exintr_count--; 62321769Sjkh#endif 62421769Sjkh 62555883Smdodd DODEBUG(Start_End, printf("ex_intr%d: finish\n", unit);); 62652286Smdodd 62752286Smdodd return; 62821769Sjkh} 62921769Sjkh 63052286Smdoddstatic void 63152286Smdoddex_tx_intr(struct ex_softc *sc) 63221769Sjkh{ 63352286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 63452286Smdodd int iobase = sc->iobase; 63552286Smdodd int tx_status; 63621769Sjkh 63752286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: start\n", unit);); 63821769Sjkh 63952286Smdodd /* 64052286Smdodd * - Cancel the watchdog. 64152286Smdodd * For all packets transmitted since last transmit interrupt: 64252286Smdodd * - Advance chain pointer to next queued packet. 64352286Smdodd * - Update statistics. 64452286Smdodd */ 64521769Sjkh 64652286Smdodd ifp->if_timer = 0; 64721769Sjkh 64852286Smdodd while (sc->tx_head != sc->tx_tail) { 64952286Smdodd outw(iobase + HOST_ADDR_REG, sc->tx_head); 65021769Sjkh 65152286Smdodd if (! inw(iobase + IO_PORT_REG) & Done_bit) 65252286Smdodd break; 65321769Sjkh 65452286Smdodd tx_status = inw(iobase + IO_PORT_REG); 65552286Smdodd sc->tx_head = inw(iobase + IO_PORT_REG); 65652286Smdodd 65752286Smdodd if (tx_status & TX_OK_bit) { 65852286Smdodd ifp->if_opackets++; 65952286Smdodd } else { 66052286Smdodd ifp->if_oerrors++; 66152286Smdodd } 66252286Smdodd 66352286Smdodd ifp->if_collisions += tx_status & No_Collisions_bits; 66452286Smdodd } 66552286Smdodd 66652286Smdodd /* 66752286Smdodd * The card should be ready to accept more packets now. 66852286Smdodd */ 66952286Smdodd 67052286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 67152286Smdodd 67252286Smdodd DODEBUG(Status, printf("OIDLE tx_intr\n");); 67352286Smdodd DODEBUG(Start_End, printf("ex_tx_intr%d: finish\n", unit);); 67452286Smdodd 67552286Smdodd return; 67621769Sjkh} 67721769Sjkh 67852286Smdoddstatic void 67952286Smdoddex_rx_intr(struct ex_softc *sc) 68021769Sjkh{ 68152286Smdodd struct ifnet * ifp = &sc->arpcom.ac_if; 68252286Smdodd int iobase = sc->iobase; 68352286Smdodd int rx_status; 68452286Smdodd int pkt_len; 68552286Smdodd int QQQ; 68652286Smdodd struct mbuf * m; 68752286Smdodd struct mbuf * ipkt; 68852286Smdodd struct ether_header * eh; 68921769Sjkh 69052286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: start\n", unit);); 69121769Sjkh 69252286Smdodd /* 69352286Smdodd * For all packets received since last receive interrupt: 69452286Smdodd * - If packet ok, read it into a new mbuf and queue it to interface, 69552286Smdodd * updating statistics. 69652286Smdodd * - If packet bad, just discard it, and update statistics. 69752286Smdodd * Finally, advance receive stop limit in card's memory to new location. 69852286Smdodd */ 69921769Sjkh 70052286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 70121769Sjkh 70252286Smdodd while (inw(iobase + IO_PORT_REG) == RCV_Done) { 70352286Smdodd 70452286Smdodd rx_status = inw(iobase + IO_PORT_REG); 70552286Smdodd sc->rx_head = inw(iobase + IO_PORT_REG); 70652286Smdodd QQQ = pkt_len = inw(iobase + IO_PORT_REG); 70752286Smdodd 70852286Smdodd if (rx_status & RCV_OK_bit) { 70952286Smdodd MGETHDR(m, M_DONTWAIT, MT_DATA); 71052286Smdodd ipkt = m; 71152286Smdodd if (ipkt == NULL) { 71252286Smdodd ifp->if_iqdrops++; 71352286Smdodd } else { 71452286Smdodd ipkt->m_pkthdr.rcvif = ifp; 71552286Smdodd ipkt->m_pkthdr.len = pkt_len; 71652286Smdodd ipkt->m_len = MHLEN; 71752286Smdodd 71852286Smdodd while (pkt_len > 0) { 71952286Smdodd if (pkt_len > MINCLSIZE) { 72052286Smdodd MCLGET(m, M_DONTWAIT); 72152286Smdodd if (m->m_flags & M_EXT) { 72252286Smdodd m->m_len = MCLBYTES; 72352286Smdodd } else { 72452286Smdodd m_freem(ipkt); 72552286Smdodd ifp->if_iqdrops++; 72652286Smdodd goto rx_another; 72752286Smdodd } 72852286Smdodd } 72952286Smdodd m->m_len = min(m->m_len, pkt_len); 73052286Smdodd 73121769Sjkh /* 73221769Sjkh * NOTE: I'm assuming that all mbufs allocated are of even length, 73321769Sjkh * except for the last one in an odd-length packet. 73421769Sjkh */ 73552286Smdodd 73652286Smdodd insw(iobase + IO_PORT_REG, 73752286Smdodd mtod(m, caddr_t), m->m_len / 2); 73852286Smdodd 73952286Smdodd if (m->m_len & 1) { 74052286Smdodd *(mtod(m, caddr_t) + m->m_len - 1) = inb(iobase + IO_PORT_REG); 74152286Smdodd } 74252286Smdodd pkt_len -= m->m_len; 74352286Smdodd 74452286Smdodd if (pkt_len > 0) { 74552286Smdodd MGET(m->m_next, M_DONTWAIT, MT_DATA); 74652286Smdodd if (m->m_next == NULL) { 74752286Smdodd m_freem(ipkt); 74852286Smdodd ifp->if_iqdrops++; 74952286Smdodd goto rx_another; 75052286Smdodd } 75152286Smdodd m = m->m_next; 75252286Smdodd m->m_len = MLEN; 75352286Smdodd } 75452286Smdodd } 75552286Smdodd eh = mtod(ipkt, struct ether_header *); 75652286Smdodd#ifdef EXDEBUG 75752286Smdodd if (debug_mask & Rcvd_Pkts) { 75852286Smdodd if ((eh->ether_dhost[5] != 0xff) || (eh->ether_dhost[0] != 0xff)) { 75952286Smdodd printf("Receive packet with %d data bytes: %6D -> ", QQQ, eh->ether_shost, ":"); 76052286Smdodd printf("%6D\n", eh->ether_dhost, ":"); 76152286Smdodd } /* QQQ */ 76221769Sjkh } 76321769Sjkh#endif 76452286Smdodd if (ifp->if_bpf != NULL) { 76552286Smdodd bpf_mtap(ifp, ipkt); 76621769Sjkh 76721769Sjkh /* 76852286Smdodd * Note that the interface cannot be in promiscuous mode 76952286Smdodd * if there are no BPF listeners. And if we are in 77052286Smdodd * promiscuous mode, we have to check if this packet is 77152286Smdodd * really ours. 77221769Sjkh */ 77352286Smdodd if ((ifp->if_flags & IFF_PROMISC) && 77452286Smdodd (eh->ether_dhost[0] & 1) == 0 && 77552286Smdodd bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, sizeof(eh->ether_dhost)) != 0 && 77652286Smdodd bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { 77752286Smdodd m_freem(ipkt); 77852286Smdodd goto rx_another; 77952286Smdodd } 78052286Smdodd } 78152286Smdodd m_adj(ipkt, sizeof(struct ether_header)); 78252286Smdodd ether_input(ifp, eh, ipkt); 78352286Smdodd ifp->if_ipackets++; 78452286Smdodd } 78552286Smdodd } else { 78652286Smdodd ifp->if_ierrors++; 78721769Sjkh } 78852286Smdodd outw(iobase + HOST_ADDR_REG, sc->rx_head); 78952286Smdoddrx_another: ; 79021769Sjkh } 79121769Sjkh 79252286Smdodd if (sc->rx_head < sc->rx_lower_limit + 2) 79352286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_upper_limit); 79452286Smdodd else 79552286Smdodd outw(iobase + RCV_STOP_REG, sc->rx_head - 2); 79652286Smdodd 79752286Smdodd DODEBUG(Start_End, printf("ex_rx_intr%d: finish\n", unit);); 79852286Smdodd 79952286Smdodd return; 80021769Sjkh} 80121769Sjkh 80221769Sjkh 80355883Smdoddstatic int 80455883Smdoddex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data) 80521769Sjkh{ 80652286Smdodd struct ex_softc * sc = ifp->if_softc; 80757987Smdodd struct ifreq * ifr = (struct ifreq *)data; 80852286Smdodd int s; 80952286Smdodd int error = 0; 81021769Sjkh 81152286Smdodd DODEBUG(Start_End, printf("ex_ioctl%d: start ", ifp->if_unit);); 81221769Sjkh 81352286Smdodd s = splimp(); 81421769Sjkh 81552286Smdodd switch(cmd) { 81652286Smdodd case SIOCSIFADDR: 81752286Smdodd case SIOCGIFADDR: 81852286Smdodd case SIOCSIFMTU: 81952286Smdodd error = ether_ioctl(ifp, cmd, data); 82052286Smdodd break; 82121769Sjkh 82252286Smdodd case SIOCSIFFLAGS: 82352286Smdodd DODEBUG(Start_End, printf("SIOCSIFFLAGS");); 82452286Smdodd if ((ifp->if_flags & IFF_UP) == 0 && 82552286Smdodd (ifp->if_flags & IFF_RUNNING)) { 82652286Smdodd 82752286Smdodd ifp->if_flags &= ~IFF_RUNNING; 82852286Smdodd ex_stop(sc); 82952286Smdodd } else { 83052286Smdodd ex_init(sc); 83152286Smdodd } 83252286Smdodd break; 83321769Sjkh#ifdef NODEF 83452286Smdodd case SIOCGHWADDR: 83552286Smdodd DODEBUG(Start_End, printf("SIOCGHWADDR");); 83652286Smdodd bcopy((caddr_t)sc->sc_addr, (caddr_t)&ifr->ifr_data, 83752286Smdodd sizeof(sc->sc_addr)); 83852286Smdodd break; 83921769Sjkh#endif 84052286Smdodd case SIOCADDMULTI: 84152286Smdodd DODEBUG(Start_End, printf("SIOCADDMULTI");); 84252286Smdodd case SIOCDELMULTI: 84352286Smdodd DODEBUG(Start_End, printf("SIOCDELMULTI");); 84452286Smdodd /* XXX Support not done yet. */ 84552286Smdodd error = EINVAL; 84652286Smdodd break; 84757987Smdodd case SIOCSIFMEDIA: 84857987Smdodd case SIOCGIFMEDIA: 84957987Smdodd error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); 85057987Smdodd break; 85152286Smdodd default: 85252286Smdodd DODEBUG(Start_End, printf("unknown");); 85352286Smdodd error = EINVAL; 85452286Smdodd } 85521769Sjkh 85652286Smdodd splx(s); 85721769Sjkh 85852286Smdodd DODEBUG(Start_End, printf("\nex_ioctl%d: finish\n", ifp->if_unit);); 85952286Smdodd 86052286Smdodd return(error); 86121769Sjkh} 86221769Sjkh 86321769Sjkh 86452286Smdoddstatic void 86552286Smdoddex_reset(struct ex_softc *sc) 86621769Sjkh{ 86752286Smdodd int s; 86821769Sjkh 86952286Smdodd DODEBUG(Start_End, printf("ex_reset%d: start\n", unit);); 87021769Sjkh 87152286Smdodd s = splimp(); 87221769Sjkh 87352286Smdodd ex_stop(sc); 87452286Smdodd ex_init(sc); 87521769Sjkh 87652286Smdodd splx(s); 87721769Sjkh 87852286Smdodd DODEBUG(Start_End, printf("ex_reset%d: finish\n", unit);); 87952286Smdodd 88052286Smdodd return; 88121769Sjkh} 88221769Sjkh 88352286Smdoddstatic void 88452286Smdoddex_watchdog(struct ifnet *ifp) 88521769Sjkh{ 88652286Smdodd struct ex_softc * sc = ifp->if_softc; 88721769Sjkh 88852286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: start\n", ifp->if_unit);); 88921769Sjkh 89052286Smdodd ifp->if_flags &= ~IFF_OACTIVE; 89121769Sjkh 89252286Smdodd DODEBUG(Status, printf("OIDLE watchdog\n");); 89352286Smdodd 89452286Smdodd ifp->if_oerrors++; 89552286Smdodd ex_reset(sc); 89652286Smdodd ex_start(ifp); 89752286Smdodd 89852286Smdodd DODEBUG(Start_End, printf("ex_watchdog%d: finish\n", ifp->if_unit);); 89952286Smdodd 90052286Smdodd return; 90121769Sjkh} 90221769Sjkh 90357987Smdoddstatic int 90459816Smdoddex_get_media (u_int32_t iobase) 90559816Smdodd{ 90659816Smdodd int tmp; 90759816Smdodd 90859816Smdodd outb(iobase + CMD_REG, Bank2_Sel); 90959816Smdodd tmp = inb(iobase + REG3); 91059816Smdodd outb(iobase + CMD_REG, Bank0_Sel); 91159816Smdodd 91259816Smdodd if (tmp & TPE_bit) 91359816Smdodd return(IFM_ETHER|IFM_10_T); 91459816Smdodd if (tmp & BNC_bit) 91559816Smdodd return(IFM_ETHER|IFM_10_2); 91659816Smdodd 91759816Smdodd return (IFM_ETHER|IFM_10_5); 91859816Smdodd} 91959816Smdodd 92059816Smdoddstatic int 92157987Smdoddex_ifmedia_upd (ifp) 92257987Smdodd struct ifnet * ifp; 92357987Smdodd{ 92457987Smdodd struct ex_softc * sc = ifp->if_softc; 92521769Sjkh 92657987Smdodd return (0); 92757987Smdodd} 92857987Smdodd 92957987Smdoddstatic void 93057987Smdoddex_ifmedia_sts(ifp, ifmr) 93157987Smdodd struct ifnet * ifp; 93257987Smdodd struct ifmediareq * ifmr; 93357987Smdodd{ 93457987Smdodd struct ex_softc * sc = ifp->if_softc; 93557987Smdodd 93657987Smdodd ifmr->ifm_active = ex_get_media(sc->iobase); 93757987Smdodd 93857987Smdodd return; 93957987Smdodd} 94057987Smdodd 94159816Smdoddu_short 94259816Smdoddeeprom_read(u_int32_t iobase, int location) 94321769Sjkh{ 94421769Sjkh int i; 94521769Sjkh u_short data = 0; 94621769Sjkh int ee_addr; 94721769Sjkh int read_cmd = location | EE_READ_CMD; 94821769Sjkh short ctrl_val = EECS; 94921769Sjkh 95021769Sjkh ee_addr = iobase + EEPROM_REG; 95121769Sjkh outb(iobase + CMD_REG, Bank2_Sel); 95221769Sjkh outb(ee_addr, EECS); 95321769Sjkh for (i = 8; i >= 0; i--) { 95421769Sjkh short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI : ctrl_val; 95521769Sjkh outb(ee_addr, outval); 95621769Sjkh outb(ee_addr, outval | EESK); 95721769Sjkh DELAY(3); 95821769Sjkh outb(ee_addr, outval); 95921769Sjkh DELAY(2); 96021769Sjkh } 96121769Sjkh outb(ee_addr, ctrl_val); 96221769Sjkh 96321769Sjkh for (i = 16; i > 0; i--) { 96421769Sjkh outb(ee_addr, ctrl_val | EESK); 96521769Sjkh DELAY(3); 96621769Sjkh data = (data << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0); 96721769Sjkh outb(ee_addr, ctrl_val); 96821769Sjkh DELAY(2); 96921769Sjkh } 97021769Sjkh 97121769Sjkh ctrl_val &= ~EECS; 97221769Sjkh outb(ee_addr, ctrl_val | EESK); 97321769Sjkh DELAY(3); 97421769Sjkh outb(ee_addr, ctrl_val); 97521769Sjkh DELAY(2); 97621769Sjkh outb(iobase + CMD_REG, Bank0_Sel); 97721769Sjkh return(data); 97821769Sjkh} 979