diff options
author | Niklas Hallqvist <niklas@cvs.openbsd.org> | 2001-07-13 17:26:45 +0000 |
---|---|---|
committer | Niklas Hallqvist <niklas@cvs.openbsd.org> | 2001-07-13 17:26:45 +0000 |
commit | d003de9876c8509ab915bb56ba3788080d8bba5a (patch) | |
tree | 7015c8fb01c9f9717d0f940cd715099681352bbd /sys/dev | |
parent | 725ca777df50676e72aa1fe1a1273c55761680dc (diff) |
On my 10th wedding anniversary I am celebrating by
committing this driver for technology from back when I got married :-)
It is for DEC EtherWorks cards, and comes from NetBSD. I have done severe
KNF, and fixed the driver to work with PIO-only cards. Enjoy!
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/ic/lemac.c | 1111 | ||||
-rw-r--r-- | sys/dev/ic/lemacreg.h | 187 | ||||
-rw-r--r-- | sys/dev/ic/lemacvar.h | 160 | ||||
-rw-r--r-- | sys/dev/isa/files.isa | 6 | ||||
-rw-r--r-- | sys/dev/isa/if_lc_isa.c | 215 |
5 files changed, 1678 insertions, 1 deletions
diff --git a/sys/dev/ic/lemac.c b/sys/dev/ic/lemac.c new file mode 100644 index 00000000000..e31b3476adb --- /dev/null +++ b/sys/dev/ic/lemac.c @@ -0,0 +1,1111 @@ +/* $OpenBSD: lemac.c,v 1.1 2001/07/13 17:26:44 niklas Exp $ */ +/* $NetBSD: lemac.c,v 1.20 2001/06/13 10:46:02 wiz Exp $ */ + +/*- + * Copyright (c) 1994, 1995, 1997 Matt Thomas <matt@3am-software.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * DEC EtherWORKS 3 Ethernet Controllers + * + * Written by Matt Thomas + * BPF support code stolen directly from if_ec.c + * + * This driver supports the LEMAC DE203/204/205 cards. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/protosw.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/errno.h> +#include <sys/malloc.h> +#include <sys/device.h> + +#include <net/if.h> +#include <net/if_types.h> +#include <net/if_dl.h> +#include <net/route.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#include <machine/bus.h> + +#include <dev/ic/lemacreg.h> +#include <dev/ic/lemacvar.h> + +#if 0 +#include <uvm/uvm_extern.h> +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +int lemac_ifioctl(struct ifnet *, u_long, caddr_t); +int lemac_ifmedia_change(struct ifnet *const); +void lemac_ifmedia_status(struct ifnet *const, struct ifmediareq *); +void lemac_ifstart(struct ifnet *); +void lemac_init(struct lemac_softc *); +void lemac_init_adapmem(struct lemac_softc *); +void lemac_input(struct lemac_softc *, bus_addr_t, size_t); +void lemac_multicast_filter(struct lemac_softc *); +void lemac_multicast_op(u_int16_t *, const u_char *, int); +int lemac_read_eeprom(struct lemac_softc *); +int lemac_read_macaddr(unsigned char *, const bus_space_tag_t, + const bus_space_handle_t, const bus_addr_t, int); +void lemac_reset(struct lemac_softc *); +void lemac_rne_intr(struct lemac_softc *); +void lemac_rxd_intr(struct lemac_softc *, unsigned); +void lemac_tne_intr(struct lemac_softc *); +void lemac_txd_intr(struct lemac_softc *, unsigned); + +struct cfdriver lc_cd = { + NULL, "lc", DV_IFNET +}; + +static const u_int16_t lemac_allmulti_mctbl[16] = { + 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, + 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, + 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, + 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, +}; + +/* + * Some tuning/monitoring variables. + */ +unsigned lemac_txmax = 16; + +void +lemac_rxd_intr(struct lemac_softc *sc, unsigned cs_value) +{ + /* + * Handle CS_RXD (Receiver disabled) here. + * + * Check Free Memory Queue Count. If not equal to zero + * then just turn Receiver back on. If it is equal to + * zero then check to see if transmitter is disabled. + * Process transmit TXD loop once more. If all else + * fails then do software init (0xC0 to EEPROM Init) + * and rebuild Free Memory Queue. + */ + + sc->sc_cntrs.cntr_rxd_intrs++; + + /* + * Re-enable Receiver. + */ + + cs_value &= ~LEMAC_CS_RXD; + LEMAC_OUTB(sc, LEMAC_REG_CS, cs_value); + + if (LEMAC_INB(sc, LEMAC_REG_FMC) > 0) + return; + + if (cs_value & LEMAC_CS_TXD) + lemac_txd_intr(sc, cs_value); + + if ((LEMAC_INB(sc, LEMAC_REG_CS) & LEMAC_CS_RXD) == 0) + return; + + printf("%s: fatal RXD error, attempting recovery\n", + sc->sc_if.if_xname); + + lemac_reset(sc); + if (sc->sc_if.if_flags & IFF_UP) { + lemac_init(sc); + return; + } + + /* + * Error during initializion. Mark card as disabled. + */ + printf("%s: recovery failed -- board disabled\n", sc->sc_if.if_xname); +} + +void +lemac_tne_intr(struct lemac_softc *sc) +{ + unsigned txcount = LEMAC_INB(sc, LEMAC_REG_TDC); + + sc->sc_cntrs.cntr_tne_intrs++; + while (txcount-- > 0) { + unsigned txsts = LEMAC_INB(sc, LEMAC_REG_TDQ); + sc->sc_if.if_opackets++; /* another one done */ + if ((txsts & (LEMAC_TDQ_LCL|LEMAC_TDQ_NCL)) + || (txsts & LEMAC_TDQ_COL) == LEMAC_TDQ_EXCCOL) { + if (txsts & LEMAC_TDQ_NCL) + sc->sc_flags &= ~LEMAC_LINKUP; + sc->sc_if.if_oerrors++; + } else { + sc->sc_flags |= LEMAC_LINKUP; + if ((txsts & LEMAC_TDQ_COL) != LEMAC_TDQ_NOCOL) + sc->sc_if.if_collisions++; + } + } + sc->sc_if.if_flags &= ~IFF_OACTIVE; + lemac_ifstart(&sc->sc_if); +} + +void +lemac_txd_intr(struct lemac_softc *sc, unsigned cs_value) +{ + /* + * Read transmit status, remove transmit buffer from + * transmit queue and place on free memory queue, + * then reset transmitter. + * Increment appropriate counters. + */ + + sc->sc_cntrs.cntr_txd_intrs++; + if (sc->sc_txctl & LEMAC_TX_STP) { + sc->sc_if.if_oerrors++; + /* return page to free queue */ + LEMAC_OUTB(sc, LEMAC_REG_FMQ, LEMAC_INB(sc, LEMAC_REG_TDQ)); + } + + /* Turn back on transmitter if disabled */ + LEMAC_OUTB(sc, LEMAC_REG_CS, cs_value & ~LEMAC_CS_TXD); + sc->sc_if.if_flags &= ~IFF_OACTIVE; +} + +int +lemac_read_eeprom(struct lemac_softc *sc) +{ + int word_off, cksum; + + u_char *ep; + + cksum = 0; + ep = sc->sc_eeprom; + for (word_off = 0; word_off < LEMAC_EEP_SIZE / 2; word_off++) { + LEMAC_OUTB(sc, LEMAC_REG_PI1, word_off); + LEMAC_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEREAD); + + DELAY(LEMAC_EEP_DELAY); + + *ep = LEMAC_INB(sc, LEMAC_REG_EE1); + cksum += *ep++; + *ep = LEMAC_INB(sc, LEMAC_REG_EE2); + cksum += *ep++; + } + + /* + * Set up Transmit Control Byte for use later during transmit. + */ + + sc->sc_txctl |= LEMAC_TX_FLAGS; + + if ((sc->sc_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_SQE) == 0) + sc->sc_txctl &= ~LEMAC_TX_SQE; + + if (sc->sc_eeprom[LEMAC_EEP_SWFLAGS] & LEMAC_EEP_SW_LAB) + sc->sc_txctl |= LEMAC_TX_LAB; + + bcopy(&sc->sc_eeprom[LEMAC_EEP_PRDNM], sc->sc_prodname, + LEMAC_EEP_PRDNMSZ); + sc->sc_prodname[LEMAC_EEP_PRDNMSZ] = '\0'; + + return (cksum % 256); +} + +void +lemac_init_adapmem(struct lemac_softc *sc) +{ + int pg, conf; + + conf = LEMAC_INB(sc, LEMAC_REG_CNF); + + if ((sc->sc_eeprom[LEMAC_EEP_SETUP] & LEMAC_EEP_ST_DRAM) == 0) { + sc->sc_lastpage = 63; + conf &= ~LEMAC_CNF_DRAM; + } else { + sc->sc_lastpage = 127; + conf |= LEMAC_CNF_DRAM; + } + + LEMAC_OUTB(sc, LEMAC_REG_CNF, conf); + + for (pg = 1; pg <= sc->sc_lastpage; pg++) + LEMAC_OUTB(sc, LEMAC_REG_FMQ, pg); +} + +void +lemac_input(struct lemac_softc *sc, bus_addr_t offset, size_t length) +{ + struct ether_header eh; + struct mbuf *m; + + if (length - sizeof(eh) > ETHERMTU || + length - sizeof(eh) < ETHERMIN) { + sc->sc_if.if_ierrors++; + return; + } + if (LEMAC_USE_PIO_MODE(sc)) { + LEMAC_INSB(sc, LEMAC_REG_DAT, sizeof(eh), (void *)&eh); + } else { + LEMAC_GETBUF16(sc, offset, sizeof(eh) / 2, (void *)&eh); + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + sc->sc_if.if_ierrors++; + return; + } + if (length + 2 > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_free(m); + sc->sc_if.if_ierrors++; + return; + } + } + m->m_data += 2; + bcopy((caddr_t)&eh, m->m_data, sizeof(eh)); + if (LEMAC_USE_PIO_MODE(sc)) { + LEMAC_INSB(sc, LEMAC_REG_DAT, length - sizeof(eh), + mtod(m, caddr_t) + sizeof(eh)); + } else { + LEMAC_GETBUF16(sc, offset + sizeof(eh), + (length - sizeof(eh)) / 2, + (void *)(mtod(m, caddr_t) + sizeof(eh))); + if (length & 1) + m->m_data[length - 1] = LEMAC_GET8(sc, + offset + length - 1); + } +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf != NULL) { + m->m_pkthdr.len = m->m_len = length; + bpf_mtap(sc->sc_if.if_bpf, m); + } + + /* + * If this is single cast but not to us + * drop it! + */ + if ((eh.ether_dhost[0] & 1) == 0 && + !LEMAC_ADDREQUAL(eh.ether_dhost, sc->sc_arpcom.ac_enaddr)) { + m_freem(m); + return; + } +#endif + m->m_pkthdr.len = m->m_len = length; + m->m_pkthdr.rcvif = &sc->sc_if; + ether_input_mbuf(&sc->sc_if, m); +} + +void +lemac_rne_intr(struct lemac_softc *sc) +{ + int rxcount; + + sc->sc_cntrs.cntr_rne_intrs++; + rxcount = LEMAC_INB(sc, LEMAC_REG_RQC); + while (rxcount--) { + unsigned rxpg = LEMAC_INB(sc, LEMAC_REG_RQ); + u_int32_t rxlen; + + sc->sc_if.if_ipackets++; + if (LEMAC_USE_PIO_MODE(sc)) { + LEMAC_OUTB(sc, LEMAC_REG_IOP, rxpg); + LEMAC_OUTB(sc, LEMAC_REG_PI1, 0); + LEMAC_OUTB(sc, LEMAC_REG_PI2, 0); + LEMAC_INSB(sc, LEMAC_REG_DAT, sizeof(rxlen), + (void *)&rxlen); + } else { + LEMAC_OUTB(sc, LEMAC_REG_MPN, rxpg); + rxlen = LEMAC_GET32(sc, 0); + } + if (rxlen & LEMAC_RX_OK) { + sc->sc_flags |= LEMAC_LINKUP; + /* + * Get receive length - subtract out checksum. + */ + rxlen = ((rxlen >> 8) & 0x7FF) - 4; + lemac_input(sc, sizeof(rxlen), rxlen); + } else { + sc->sc_if.if_ierrors++; + } + /* Return this page to Free Memory Queue */ + LEMAC_OUTB(sc, LEMAC_REG_FMQ, rxpg); + } /* end while (recv_count--) */ + + return; +} + +/* + * This is the standard method of reading the DEC Address ROMS. + * I don't understand it but it does work. + */ +int +lemac_read_macaddr(unsigned char *hwaddr, const bus_space_tag_t iot, + const bus_space_handle_t ioh, const bus_addr_t ioreg, int skippat) +{ + int cksum, rom_cksum; + unsigned char addrbuf[6]; + + if (!skippat) { + int idx, idx2, found, octet; + static u_char testpat[] = { + 0xFF, 0, 0x55, 0xAA, 0xFF, 0, 0x55, 0xAA + }; + idx2 = found = 0; + + for (idx = 0; idx < 32; idx++) { + octet = bus_space_read_1(iot, ioh, ioreg); + + if (octet == testpat[idx2]) { + if (++idx2 == sizeof(testpat)) { + ++found; + break; + } + } else { + idx2 = 0; + } + } + + if (!found) + return (-1); + } + + if (hwaddr == NULL) + hwaddr = addrbuf; + + cksum = 0; + hwaddr[0] = bus_space_read_1(iot, ioh, ioreg); + hwaddr[1] = bus_space_read_1(iot, ioh, ioreg); + + /* hardware adddress can't be multicast */ + if (hwaddr[0] & 1) + return (-1); + + cksum = *(u_short *)&hwaddr[0]; + + hwaddr[2] = bus_space_read_1(iot, ioh, ioreg); + hwaddr[3] = bus_space_read_1(iot, ioh, ioreg); + cksum *= 2; + if (cksum > 65535) + cksum -= 65535; + cksum += *(u_short *)&hwaddr[2]; + if (cksum > 65535) + cksum -= 65535; + + hwaddr[4] = bus_space_read_1(iot, ioh, ioreg); + hwaddr[5] = bus_space_read_1(iot, ioh, ioreg); + cksum *= 2; + if (cksum > 65535) + cksum -= 65535; + cksum += *(u_short *)&hwaddr[4]; + if (cksum >= 65535) + cksum -= 65535; + + /* 00-00-00 is an illegal OUI */ + if (hwaddr[0] == 0 && hwaddr[1] == 0 && hwaddr[2] == 0) + return (-1); + + rom_cksum = bus_space_read_1(iot, ioh, ioreg); + rom_cksum |= bus_space_read_1(iot, ioh, ioreg) << 8; + + if (cksum != rom_cksum) + return (-1); + return (0); +} + +#if 0 +void +lemac_multicast_op(u_int16_t *mctbl, const u_char *mca, int enable) +{ + u_int idx, bit, crc; + + crc = ether_crc32_le(mca, ETHER_ADDR_LEN); + + /* + * The following two lines convert the N bit index into a + * longword index and a longword mask. + */ +#if LEMAC_MCTBL_BITS < 0 + crc >>= (32 + LEMAC_MCTBL_BITS); + crc &= (1 << -LEMAC_MCTBL_BITS) - 1; +#else + crc &= (1 << LEMAC_MCTBL_BITS) - 1; +#endif + bit = 1 << (crc & 0x0F); + idx = crc >> 4; + + /* + * Set or clear hash filter bit in our table. + */ + if (enable) { + mctbl[idx] |= bit; /* Set Bit */ + } else { + mctbl[idx] &= ~bit; /* Clear Bit */ + } +} +#endif + +void +lemac_multicast_filter(struct lemac_softc *sc) +{ +#if 0 + struct ether_multistep step; + struct ether_multi *enm; +#endif + + bzero(sc->sc_mctbl, LEMAC_MCTBL_BITS / 8); + +#if 0 + lemac_multicast_op(sc->sc_mctbl, etherbroadcastaddr, 1); + + ETHER_FIRST_MULTI(step, &sc->sc_ec, enm); + while (enm != NULL) { + if (!LEMAC_ADDREQUAL(enm->enm_addrlo, enm->enm_addrhi)) { + sc->sc_flags |= LEMAC_ALLMULTI; + sc->sc_if.if_flags |= IFF_ALLMULTI; + return; + } + lemac_multicast_op(sc->sc_mctbl, enm->enm_addrlo, TRUE); + ETHER_NEXT_MULTI(step, enm); + } +#endif + sc->sc_flags &= ~LEMAC_ALLMULTI; + sc->sc_if.if_flags &= ~IFF_ALLMULTI; +} + +/* + * Do a hard reset of the board; + */ +void +lemac_reset(struct lemac_softc *const sc) +{ + unsigned data; + + /* + * Initialize board.. + */ + sc->sc_flags &= ~LEMAC_LINKUP; + sc->sc_if.if_flags &= ~IFF_OACTIVE; + LEMAC_INTR_DISABLE(sc); + + LEMAC_OUTB(sc, LEMAC_REG_IOP, LEMAC_IOP_EEINIT); + DELAY(LEMAC_EEP_DELAY); + + /* + * Read EEPROM information. NOTE - the placement of this function + * is important because functions hereafter may rely on information + * read from the EEPROM. + */ + if ((data = lemac_read_eeprom(sc)) != LEMAC_EEP_CKSUM) { + printf("%s: reset: EEPROM checksum failed (0x%x)\n", + sc->sc_if.if_xname, data); + return; + } + + /* + * Update the control register to reflect the media choice + */ + data = LEMAC_INB(sc, LEMAC_REG_CTL); + if ((data & (LEMAC_CTL_APD|LEMAC_CTL_PSL)) != sc->sc_ctlmode) { + data &= ~(LEMAC_CTL_APD|LEMAC_CTL_PSL); + data |= sc->sc_ctlmode; + LEMAC_OUTB(sc, LEMAC_REG_CTL, data); + } + + /* + * Force to 2K mode if not already configured. + */ + + data = LEMAC_INB(sc, LEMAC_REG_MBR); + if (LEMAC_IS_2K_MODE(data)) { + sc->sc_flags |= LEMAC_2K_MODE; + } else if (LEMAC_IS_64K_MODE(data)) { + data = (((data * 2) & 0xF) << 4); + sc->sc_flags |= LEMAC_WAS_64K_MODE; + LEMAC_OUTB(sc, LEMAC_REG_MBR, data); + } else if (LEMAC_IS_32K_MODE(data)) { + data = ((data & 0xF) << 4); + sc->sc_flags |= LEMAC_WAS_32K_MODE; + LEMAC_OUTB(sc, LEMAC_REG_MBR, data); + } else { + sc->sc_flags |= LEMAC_PIO_MODE; + /* PIO mode */ + } + + /* + * Initialize Free Memory Queue, Init mcast table with broadcast. + */ + + lemac_init_adapmem(sc); + sc->sc_flags |= LEMAC_ALIVE; +} + +void +lemac_init(struct lemac_softc *const sc) +{ + if ((sc->sc_flags & LEMAC_ALIVE) == 0) + return; + + /* + * If the interface has the up flag + */ + if (sc->sc_if.if_flags & IFF_UP) { + int saved_cs = LEMAC_INB(sc, LEMAC_REG_CS); + LEMAC_OUTB(sc, LEMAC_REG_CS, + saved_cs | (LEMAC_CS_TXD | LEMAC_CS_RXD)); + LEMAC_OUTB(sc, LEMAC_REG_PA0, sc->sc_arpcom.ac_enaddr[0]); + LEMAC_OUTB(sc, LEMAC_REG_PA1, sc->sc_arpcom.ac_enaddr[1]); + LEMAC_OUTB(sc, LEMAC_REG_PA2, sc->sc_arpcom.ac_enaddr[2]); + LEMAC_OUTB(sc, LEMAC_REG_PA3, sc->sc_arpcom.ac_enaddr[3]); + LEMAC_OUTB(sc, LEMAC_REG_PA4, sc->sc_arpcom.ac_enaddr[4]); + LEMAC_OUTB(sc, LEMAC_REG_PA5, sc->sc_arpcom.ac_enaddr[5]); + + LEMAC_OUTB(sc, LEMAC_REG_IC, + LEMAC_INB(sc, LEMAC_REG_IC) | LEMAC_IC_IE); + + if (sc->sc_if.if_flags & IFF_PROMISC) { + LEMAC_OUTB(sc, LEMAC_REG_CS, + LEMAC_CS_MCE | LEMAC_CS_PME); + } else { + LEMAC_INTR_DISABLE(sc); + lemac_multicast_filter(sc); + if (sc->sc_flags & LEMAC_ALLMULTI) + bcopy(lemac_allmulti_mctbl, sc->sc_mctbl, + sizeof(sc->sc_mctbl)); + if (LEMAC_USE_PIO_MODE(sc)) { + LEMAC_OUTB(sc, LEMAC_REG_IOP, 0); + LEMAC_OUTB(sc, LEMAC_REG_PI1, + LEMAC_MCTBL_OFF & 0xFF); + LEMAC_OUTB(sc, LEMAC_REG_PI2, + LEMAC_MCTBL_OFF >> 8); + LEMAC_OUTSB(sc, LEMAC_REG_DAT, + sizeof(sc->sc_mctbl), + (void *)sc->sc_mctbl); + } else { + LEMAC_OUTB(sc, LEMAC_REG_MPN, 0); + LEMAC_PUTBUF8(sc, LEMAC_MCTBL_OFF, + sizeof(sc->sc_mctbl), + (void *)sc->sc_mctbl); + } + + LEMAC_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_MCE); + } + + LEMAC_OUTB(sc, LEMAC_REG_CTL, + LEMAC_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED); + + LEMAC_INTR_ENABLE(sc); + sc->sc_if.if_flags |= IFF_RUNNING; + lemac_ifstart(&sc->sc_if); + } else { + LEMAC_OUTB(sc, LEMAC_REG_CS, LEMAC_CS_RXD|LEMAC_CS_TXD); + + LEMAC_INTR_DISABLE(sc); + sc->sc_if.if_flags &= ~IFF_RUNNING; + } +} + +void +lemac_ifstart(struct ifnet *ifp) +{ + struct lemac_softc *const sc = LEMAC_IFP_TO_SOFTC(ifp); + + if ((ifp->if_flags & IFF_RUNNING) == 0) + return; + + LEMAC_INTR_DISABLE(sc); + + for (;;) { + struct mbuf *m; + struct mbuf *m0; + int tx_pg; + + IFQ_POLL(&ifp->if_snd, m); + if (m == NULL) + break; + + if ((sc->sc_csr.csr_tqc = LEMAC_INB(sc, LEMAC_REG_TQC)) >= + lemac_txmax) { + sc->sc_cntrs.cntr_txfull++; + ifp->if_flags |= IFF_OACTIVE; + break; + } + + /* + * get free memory page + */ + tx_pg = sc->sc_csr.csr_fmq = LEMAC_INB(sc, LEMAC_REG_FMQ); + + /* + * Check for good transmit page. + */ + if (tx_pg == 0 || tx_pg > sc->sc_lastpage) { + sc->sc_cntrs.cntr_txnospc++; + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m); + + /* + * The first four bytes of each transmit buffer are for + * control information. The first byte is the control + * byte, then the length (why not word aligned?), then + * the offset to the buffer. + */ + + if (LEMAC_USE_PIO_MODE(sc)) { + /* Shift 2K window. */ + LEMAC_OUTB(sc, LEMAC_REG_IOP, tx_pg); + LEMAC_OUTB(sc, LEMAC_REG_PI1, 0); + LEMAC_OUTB(sc, LEMAC_REG_PI2, 0); + LEMAC_OUTB(sc, LEMAC_REG_DAT, sc->sc_txctl); + LEMAC_OUTB(sc, LEMAC_REG_DAT, + (m->m_pkthdr.len >> 0) & 0xFF); + LEMAC_OUTB(sc, LEMAC_REG_DAT, + (m->m_pkthdr.len >> 8) & 0xFF); + LEMAC_OUTB(sc, LEMAC_REG_DAT, LEMAC_TX_HDRSZ); + for (m0 = m; m0 != NULL; m0 = m0->m_next) + LEMAC_OUTSB(sc, LEMAC_REG_DAT, + m0->m_len, m0->m_data); + } else { + bus_size_t txoff = /* (mtod(m, u_int32_t) & + (sizeof(u_int32_t) - 1)) + */ LEMAC_TX_HDRSZ; + /* Shift 2K window. */ + LEMAC_OUTB(sc, LEMAC_REG_MPN, tx_pg); + LEMAC_PUT8(sc, 0, sc->sc_txctl); + LEMAC_PUT8(sc, 1, (m->m_pkthdr.len >> 0) & 0xFF); + LEMAC_PUT8(sc, 2, (m->m_pkthdr.len >> 8) & 0xFF); + LEMAC_PUT8(sc, 3, txoff); + + /* + * Copy the packet to the board + */ + for (m0 = m; m0 != NULL; m0 = m0->m_next) { +#if 0 + LEMAC_PUTBUF8(sc, txoff, m0->m_len, + m0->m_data); + txoff += m0->m_len; +#else + const u_int8_t *cp = m0->m_data; + int len = m0->m_len; +#if 0 + if ((txoff & 3) == (((long)cp) & 3) && + len >= 4) { + if (txoff & 3) { + int alen = (~txoff & 3); + LEMAC_PUTBUF8(sc, txoff, alen, + cp); + cp += alen; + txoff += alen; + len -= alen; + } + if (len >= 4) { + LEMAC_PUTBUF32(sc, txoff, + len / 4, cp); + cp += len & ~3; + txoff += len & ~3; + len &= 3; + } + } +#endif + if ((txoff & 1) == (((long)cp) & 1) && + len >= 2) { + if (txoff & 1) { + int alen = (~txoff & 1); + LEMAC_PUTBUF8(sc, txoff, alen, + cp); + cp += alen; + txoff += alen; + len -= alen; + } + if (len >= 2) { + LEMAC_PUTBUF16(sc, txoff, + len / 2, (void *)cp); + cp += len & ~1; + txoff += len & ~1; + len &= 1; + } + } + if (len > 0) { + LEMAC_PUTBUF8(sc, txoff, len, cp); + txoff += len; + } +#endif + } + } + + /* tell chip to transmit this packet */ + LEMAC_OUTB(sc, LEMAC_REG_TQ, tx_pg); +#if NBPFILTER > 0 + if (sc->sc_if.if_bpf != NULL) + bpf_mtap(sc->sc_if.if_bpf, m); +#endif + m_freem(m); /* free the mbuf */ + } + LEMAC_INTR_ENABLE(sc); +} + +int +lemac_ifioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct lemac_softc *const sc = LEMAC_IFP_TO_SOFTC(ifp); + int s; + int error = 0; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + + s = splnet(); + + if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { + splx(s); + return (error); + } + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + lemac_init(sc); + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + arp_ifinit(&sc->sc_arpcom, ifa); + break; +#endif /* INET */ + +#ifdef NS + /* This magic copied from if_is.c; I don't use XNS, + * so I have no way of telling if this actually + * works or not. + */ + case AF_NS: { + struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + if (ns_nullhost(*ina)) { + ina->x_host = + *(union ns_host *)sc->sc_arpcom.ac_enaddr; + } else { + bcopy((caddr_t)ina->x_host.c_host, + sc->sc_arpcom.ac_enaddr, ifp->if_addrlen); + } + break; + } +#endif /* NS */ + + default: + break; + } + break; + + case SIOCSIFFLAGS: + lemac_init(sc); + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * Update multicast listeners + */ + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_arpcom) : + ether_delmulti(ifr, &sc->sc_arpcom); + + if (error == ENETRESET) { + /* Reset multicast filtering. */ + lemac_init(sc); + error = 0; + } + break; + + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + error = ifmedia_ioctl(ifp, (struct ifreq *)data, + &sc->sc_ifmedia, cmd); + break; + + case SIOCSIFMTU: + if (ifr->ifr_mtu > ETHERMTU || ifr->ifr_mtu < ETHERMIN) { + error = EINVAL; + } else if (ifp->if_mtu != ifr->ifr_mtu) { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + + default: + error = EINVAL; + break; + } + + splx(s); + return (error); +} + +int +lemac_ifmedia_change(struct ifnet *const ifp) +{ + struct lemac_softc *const sc = LEMAC_IFP_TO_SOFTC(ifp); + unsigned new_ctl; + + switch (IFM_SUBTYPE(sc->sc_ifmedia.ifm_media)) { + case IFM_10_T: + new_ctl = LEMAC_CTL_APD; + break; + case IFM_10_2: + case IFM_10_5: + new_ctl = LEMAC_CTL_APD|LEMAC_CTL_PSL; + break; + case IFM_AUTO: + new_ctl = 0; + break; + default: + return (EINVAL); + } + if (sc->sc_ctlmode != new_ctl) { + sc->sc_ctlmode = new_ctl; + lemac_reset(sc); + if (sc->sc_if.if_flags & IFF_UP) + lemac_init(sc); + } + return (0); +} + +/* + * Media status callback + */ +void +lemac_ifmedia_status(struct ifnet *const ifp, struct ifmediareq *req) +{ + struct lemac_softc *sc = LEMAC_IFP_TO_SOFTC(ifp); + unsigned data = LEMAC_INB(sc, LEMAC_REG_CNF); + + req->ifm_status = IFM_AVALID; + if (sc->sc_flags & LEMAC_LINKUP) + req->ifm_status |= IFM_ACTIVE; + + if (sc->sc_ctlmode & LEMAC_CTL_APD) { + if (sc->sc_ctlmode & LEMAC_CTL_PSL) { + req->ifm_active = IFM_10_5; + } else { + req->ifm_active = IFM_10_T; + } + } else { + /* + * The link bit of the configuration register reflects the + * current media choice when auto-port is enabled. + */ + if (data & LEMAC_CNF_NOLINK) { + req->ifm_active = IFM_10_5; + } else { + req->ifm_active = IFM_10_T; + } + } + + req->ifm_active |= IFM_ETHER; +} + +int +lemac_port_check(const bus_space_tag_t iot, const bus_space_handle_t ioh) +{ + unsigned char hwaddr[6]; + + if (lemac_read_macaddr(hwaddr, iot, ioh, LEMAC_REG_APD, 0) == 0) + return (1); + if (lemac_read_macaddr(hwaddr, iot, ioh, LEMAC_REG_APD, 1) == 0) + return (1); + return (0); +} + +void +lemac_info_get(const bus_space_tag_t iot, const bus_space_handle_t ioh, + bus_addr_t *maddr_p, bus_size_t *msize_p, int *irq_p) +{ + unsigned data; + + *irq_p = LEMAC_DECODEIRQ(bus_space_read_1(iot, ioh, LEMAC_REG_IC) & + LEMAC_IC_IRQMSK); + + data = bus_space_read_1(iot, ioh, LEMAC_REG_MBR); + if (LEMAC_IS_2K_MODE(data)) { + *maddr_p = data * (2 * 1024) + (512 * 1024); + *msize_p = 2 * 1024; + } else if (LEMAC_IS_64K_MODE(data)) { + *maddr_p = data * 64 * 1024; + *msize_p = 64 * 1024; + } else if (LEMAC_IS_32K_MODE(data)) { + *maddr_p = data * 32 * 1024; + *msize_p = 32* 1024; + } else { + *maddr_p = 0; + *msize_p = 0; + } +} + +/* + * What to do upon receipt of an interrupt. + */ +int +lemac_intr(void *arg) +{ + struct lemac_softc *const sc = arg; + int cs_value; + + LEMAC_INTR_DISABLE(sc); /* Mask interrupts */ + + /* + * Determine cause of interrupt. Receive events take + * priority over Transmit. + */ + + cs_value = LEMAC_INB(sc, LEMAC_REG_CS); + + /* + * Check for Receive Queue not being empty. + * Check for Transmit Done Queue not being empty. + */ + + if (cs_value & LEMAC_CS_RNE) + lemac_rne_intr(sc); + if (cs_value & LEMAC_CS_TNE) + lemac_tne_intr(sc); + + /* + * Check for Transmitter Disabled. + * Check for Receiver Disabled. + */ + + if (cs_value & LEMAC_CS_TXD) + lemac_txd_intr(sc, cs_value); + if (cs_value & LEMAC_CS_RXD) + lemac_rxd_intr(sc, cs_value); + + /* + * Toggle LED and unmask interrupts. + */ + + sc->sc_csr.csr_cs = LEMAC_INB(sc, LEMAC_REG_CS); + + LEMAC_OUTB(sc, LEMAC_REG_CTL, + LEMAC_INB(sc, LEMAC_REG_CTL) ^ LEMAC_CTL_LED); + LEMAC_INTR_ENABLE(sc); /* Unmask interrupts */ + +#if 0 + if (cs_value) + rnd_add_uint32(&sc->rnd_source, cs_value); +#endif + + return (1); +} + +void +lemac_shutdown(void *arg) +{ + lemac_reset((struct lemac_softc *)arg); +} + +const char *const lemac_modes[4] = { + "PIO mode (internal 2KB window)", + "2KB window", + "changed 32KB window to 2KB", + "changed 64KB window to 2KB", +}; + +void +lemac_ifattach(struct lemac_softc *sc) +{ + struct ifnet *const ifp = &sc->sc_if; + + bcopy(sc->sc_dv.dv_xname, ifp->if_xname, IFNAMSIZ); + + lemac_reset(sc); + + lemac_read_macaddr(sc->sc_arpcom.ac_enaddr, sc->sc_iot, sc->sc_ioh, + LEMAC_REG_APD, 0); + + printf(": %s\n", sc->sc_prodname); + + printf("%s: address %s, %dKB RAM, %s\n", ifp->if_xname, + ether_sprintf(sc->sc_arpcom.ac_enaddr), sc->sc_lastpage * 2 + 2, + lemac_modes[sc->sc_flags & LEMAC_MODE_MASK]); + + ifp->if_softc = (void *)sc; + ifp->if_start = lemac_ifstart; + ifp->if_ioctl = lemac_ifioctl; + + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX +#ifdef IFF_NOTRAILERS + | IFF_NOTRAILERS +#endif + | IFF_MULTICAST; + + if (sc->sc_flags & LEMAC_ALIVE) { + int media; + + IFQ_SET_READY(&ifp->if_snd); + + if_attach(ifp); + ether_ifattach(ifp); + +#if 0 + rnd_attach_source(&sc->rnd_source, sc->sc_dv.dv_xname, + RND_TYPE_NET, 0); +#endif + + ifmedia_init(&sc->sc_ifmedia, 0, lemac_ifmedia_change, + lemac_ifmedia_status); + if (sc->sc_prodname[4] == '5') /* DE205 is UTP/AUI */ + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_AUTO, 0, + 0); + if (sc->sc_prodname[4] != '3') /* DE204 & 205 have UTP */ + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_T, 0, + 0); + if (sc->sc_prodname[4] != '4') /* DE203 & 205 have BNC */ + ifmedia_add(&sc->sc_ifmedia, IFM_ETHER | IFM_10_5, 0, + 0); + switch (sc->sc_prodname[4]) { + case '3': + media = IFM_10_5; + break; + case '4': + media = IFM_10_T; + break; + default: + media = IFM_AUTO; + break; + } + ifmedia_set(&sc->sc_ifmedia, IFM_ETHER | media); + } else { + printf("%s: disabled due to error\n", ifp->if_xname); + } +} diff --git a/sys/dev/ic/lemacreg.h b/sys/dev/ic/lemacreg.h new file mode 100644 index 00000000000..045b8cb5878 --- /dev/null +++ b/sys/dev/ic/lemacreg.h @@ -0,0 +1,187 @@ +/* $OpenBSD: lemacreg.h,v 1.1 2001/07/13 17:26:44 niklas Exp $ */ +/* $NetBSD: lemacreg.h,v 1.2 2001/06/13 10:46:03 wiz Exp $ */ + +/* + * Copyright (c) 1994, 1995, 1997 Matt Thomas <matt@3am-software.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _LEMAC_H_ +#define _LEMAC_H_ + +#define LEMAC_IOBASE_LOW 0x200 +#define LEMAC_IOBASE_HIGH 0x400 + +/* + * This is list of registers used on a DEC EtherWORKS III card. + * Each board occupies a 32 byte register space. This can be + * in either EISA or ISA space. Currently we only support ISA + * space. + */ + +#define LEMAC_REG_CS 0x00 /* Control and Status */ +#define LEMAC_REG_CTL 0x01 /* Control */ +#define LEMAC_REG_IC 0x02 /* Interrupt Control */ +#define LEMAC_REG_TS 0x03 /* Transmit Status */ +#define LEMAC_REG_RSVD1 0x04 /* Reserved (not used) */ +#define LEMAC_REG_RSVD2 0x05 /* Reserved (not used) */ +#define LEMAC_REG_FMQ 0x06 /* Free Memory Queue */ +#define LEMAC_REG_FMC 0x07 /* Free Memory Queue Count */ +#define LEMAC_REG_RQ 0x08 /* Receive Queue */ +#define LEMAC_REG_RQC 0x09 /* Receive Queue Count */ +#define LEMAC_REG_TQ 0x0A /* Transmit Queue */ +#define LEMAC_REG_TQC 0x0B /* Transmit Queue Count */ +#define LEMAC_REG_TDQ 0x0C /* Transmit Done Queue */ +#define LEMAC_REG_TDC 0x0D /* Transmit Done Queue Count */ +#define LEMAC_REG_PI1 0x0E /* Page Index #1 */ +#define LEMAC_REG_PI2 0x0F /* Page Index #2 */ +#define LEMAC_REG_DAT 0x10 /* Data */ +#define LEMAC_REG_IOP 0x11 /* I/O Page */ +#define LEMAC_REG_IOB 0x12 /* I/O Base */ +#define LEMAC_REG_MPN 0x13 /* Memory Page */ +#define LEMAC_REG_MBR 0x14 /* Memory Base */ +#define LEMAC_REG_APD 0x15 /* Address PROM */ +#define LEMAC_REG_EE1 0x16 /* EEPROM Data #1 */ +#define LEMAC_REG_EE2 0x17 /* EEPROM Data #2 */ +#define LEMAC_REG_PA0 0x18 /* Physical Address (Byte 0) */ +#define LEMAC_REG_PA1 0x19 /* Physical Address (Byte 1) */ +#define LEMAC_REG_PA2 0x1A /* Physical Address (Byte 2) */ +#define LEMAC_REG_PA3 0x1B /* Physical Address (Byte 3) */ +#define LEMAC_REG_PA4 0x1C /* Physical Address (Byte 4) */ +#define LEMAC_REG_PA5 0x1D /* Physical Address (Byte 5) */ +#define LEMAC_REG_CNF 0x1E /* Configuration Management */ +#define LEMAC_IOSIZE 0x20 /* LEMAC uses 32 bytes of IOSPACE */ + + +#define LEMAC_REG_EID0 0x80 /* EISA Identification 0 */ +#define LEMAC_REG_EID1 0x81 /* EISA Identification 1 */ +#define LEMAC_REG_EID2 0x82 /* EISA Identification 2 */ +#define LEMAC_REG_EID3 0x83 /* EISA Identification 3 */ +#define LEMAC_REG_EIC 0x84 /* EISA Control */ + +/* Control Page (Page 0) Definitions */ + +#define LEMAC_MCTBL_BITS 9 +#define LEMAC_MCTBL_OFF 512 +#define LEMAC_MCTBL_SIZE (1 << (LEMAC_MCTBL_BITS - 3)) +#define LEMAC_CRC32_POLY 0xEDB88320UL /* CRC-32 Poly -- Little Endian) */ + +/* EEPROM Definitions */ + +#define LEMAC_EEP_CKSUM 0 /* The valid checksum is 0 */ +#define LEMAC_EEP_SIZE 32 /* EEPROM is 32 bytes */ +#define LEMAC_EEP_DELAY 2000 /* 2ms = 2000us */ +#define LEMAC_EEP_PRDNM 8 /* Product Name Offset */ +#define LEMAC_EEP_PRDNMSZ 8 /* Product Name Size */ +#define LEMAC_EEP_SWFLAGS 16 /* Software Options Offset */ +#define LEMAC_EEP_SETUP 23 /* Setup Options Offset */ + +#define LEMAC_EEP_SW_SQE 0x10 /* Enable TX_SQE on Transmits */ +#define LEMAC_EEP_SW_LAB 0x08 /* Enable TX_LAB on Transmits */ +#define LEMAC_EEP_ST_DRAM 0x02 /* Enable extra DRAM */ + +#define LEMAC_ADP_ROMSZ 32 /* Size of Address PROM */ + +/* Receive Status Definitions */ + +#define LEMAC_RX_PLL 0x01 /* Phase Lock Lost */ +#define LEMAC_RX_CRC 0x02 /* CRC Error */ +#define LEMAC_RX_DBE 0x04 /* Dribble Bit Error */ +#define LEMAC_RX_MCM 0x08 /* Multicast Match */ +#define LEMAC_RX_IAM 0x10 /* Individual Address Match */ +#define LEMAC_RX_OK 0x80 /* No Errors */ + +/* Transmit Status Definitions (not valid if TXD == 0) */ + +#define LEMAC_TS_RTRYMSK 0x0F /* Retries of last TX PDU */ +#define LEMAC_TS_ECL 0x10 /* Excessive collision of ... */ +#define LEMAC_TS_LCL 0x20 /* Late collision of ... */ +#define LEMAC_TS_ID 0x40 /* Initially Deferred ... */ +#define LEMAC_TS_NCL 0x08 /* No carrier loopback ... */ + +/* Transmit Control Definitions */ + +#define LEMAC_TX_ISA 0x01 /* Insert Source Address (no) */ +#define LEMAC_TX_IFC 0x02 /* Insert Frame Check (yes) */ +#define LEMAC_TX_PAD 0x04 /* Zero PAD to mininum length (yes) */ +#define LEMAC_TX_LAB 0x08 /* Less Agressive Backoff (no) */ +#define LEMAC_TX_QMD 0x10 /* Q-Mode (yes) */ +#define LEMAC_TX_STP 0x20 /* Stop on Error (no) */ +#define LEMAC_TX_SQE 0x40 /* SQE Enable (yes) */ + +#define LEMAC_TX_FLAGS (LEMAC_TX_IFC|LEMAC_TX_PAD|LEMAC_TX_QMD|\ + LEMAC_TX_SQE) +#define LEMAC_TX_HDRSZ 4 /* Size of TX header */ + +/* Transmit Done Queue Status Definitions */ + +#define LEMAC_TDQ_COL 0x03 /* Collision Mask */ +#define LEMAC_TDQ_NOCOL 0x00 /* No Collisions */ +#define LEMAC_TDQ_ONECOL 0x01 /* One Collision */ +#define LEMAC_TDQ_MULCOL 0x02 /* Multiple Collisions */ +#define LEMAC_TDQ_EXCCOL 0x03 /* Excesive Collisions */ +#define LEMAC_TDQ_ID 0x04 /* Initially Deferred */ +#define LEMAC_TDQ_LCL 0x08 /* Late Collision (will TX_STP) */ +#define LEMAC_TDQ_NCL 0x10 /* No carrier loopback */ +#define LEMAC_TDQ_SQE 0x20 /* SQE error */ + +/* Control / Status Definitions */ + +#define LEMAC_CS_RXD 0x01 /* Receiver Disabled */ +#define LEMAC_CS_TXD 0x02 /* Transmitter Disabled */ +#define LEMAC_CS_RNE 0x04 /* Receive Queue Not Empty */ +#define LEMAC_CS_TNE 0x08 /* Transmit Done Queue Not Empty */ +#define LEMAC_CS_MBZ4 0x10 /* MBZ */ +#define LEMAC_CS_MCE 0x20 /* Multicast Enable */ +#define LEMAC_CS_PME 0x40 /* Promiscuous Mode Enable */ +#define LEMAC_CS_RA 0x80 /* Runt Accept */ + +/* Control Definitions */ + +#define LEMAC_CTL_LED 0x02 /* LED state (inverted) */ +#define LEMAC_CTL_PSL 0x40 /* Port Select (1=AUI, 0=UTP) */ +#define LEMAC_CTL_APD 0x80 /* Auto Port Disable */ + +/* Interrupt Control Definitions */ + +#define LEMAC_IC_RXD 0x01 /* Enable RXD Interrupt */ +#define LEMAC_IC_TXD 0x02 /* Enable TXD Interrupt */ +#define LEMAC_IC_RNE 0x04 /* Enable RNE Interrupt */ +#define LEMAC_IC_TNE 0x08 /* Enable TNE Interrupt */ +#define LEMAC_IC_ALL 0x0F /* Enable RXD,TXD,RNE,TNE */ +#define LEMAC_IC_IRQMSK 0x60 /* Interrupt Select */ +#define LEMAC_IC_IRQ5 0x00 /* Select IRQ 5 */ +#define LEMAC_IC_IRQ10 0x20 /* Select IRQ 10 */ +#define LEMAC_IC_IRQ11 0x40 /* Select IRQ 11 */ +#define LEMAC_IC_IRQ15 0x60 /* Select IRQ 15 */ +#define LEMAC_IC_IE 0x80 /* Interrupt Enable */ + +/* I/O Page Definitions */ + +#define LEMAC_IOP_EEINIT 0xC0 /* Perform a board init/reset */ +#define LEMAC_IOP_EEREAD 0xE0 /* Start a read from EEPROM */ + +/* Configuration / Management Definitions */ + +#define LEMAC_CNF_DRAM 0x02 /* Extra on-board DRAM is available */ +#define LEMAC_CNF_NOLINK 0x20 /* UTP port is UP */ + +#endif /* _LEMAC_H_ */ diff --git a/sys/dev/ic/lemacvar.h b/sys/dev/ic/lemacvar.h new file mode 100644 index 00000000000..c3aac8cce9f --- /dev/null +++ b/sys/dev/ic/lemacvar.h @@ -0,0 +1,160 @@ +/* $OpenBSD: lemacvar.h,v 1.1 2001/07/13 17:26:44 niklas Exp $ */ +/* $NetBSD: lemacvar.h,v 1.6 2001/06/13 10:46:03 wiz Exp $ */ + +/* + * Copyright (c) 1997 Matt Thomas <matt@3am-software.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _LEMAC_VAR_H +#define _LEMAC_VAR_H + +/* + * Ethernet status, per interface. + */ +struct lemac_softc { + struct device sc_dv; + void *sc_ih; + void *sc_ats; + struct arpcom sc_arpcom; /* Ethernet common part */ + struct ifmedia sc_ifmedia; + bus_space_tag_t sc_iot; + bus_space_tag_t sc_memt; + bus_space_handle_t sc_ioh; + bus_space_handle_t sc_memh; + unsigned sc_flags; +#define LEMAC_PIO_MODE 0x0000U +#define LEMAC_2K_MODE 0x0001U +#define LEMAC_WAS_32K_MODE 0x0002U +#define LEMAC_WAS_64K_MODE 0x0003U +#define LEMAC_MODE_MASK 0x0003U +#define LEMAC_ALLMULTI 0x0010U +#define LEMAC_ALIVE 0x0020U +#define LEMAC_LINKUP 0x0040U + unsigned sc_lastpage; /* last 2K page */ + unsigned sc_txctl; /* Transmit Control Byte */ + unsigned sc_ctlmode; /* media ctl bits */ + struct { + u_int8_t csr_cs; + u_int8_t csr_tqc; + u_int8_t csr_fmq; + } sc_csr; + unsigned sc_laststatus; /* last read of LEMAC_REG_CS */ + u_int16_t sc_mctbl[LEMAC_MCTBL_SIZE/sizeof(u_int16_t)]; + /* local copy of multicast table */ + struct { + unsigned cntr_txnospc; /* total # of no trnasmit memory */ + unsigned cntr_txfull; /* total # of tranmitter full */ + unsigned cntr_tne_intrs;/* total # of tranmit done intrs */ + unsigned cntr_rne_intrs;/* total # of receive done intrs */ + unsigned cntr_txd_intrs;/* total # of tranmit error intrs */ + unsigned cntr_rxd_intrs;/* total # of receive error intrs */ + } sc_cntrs; + + /* + * We rely on sc_enaddr being aligned on (at least) a 16 bit boundary + */ + unsigned char sc_enaddr[ETHER_ADDR_LEN];/* current Ethernet address */ + char sc_prodname[LEMAC_EEP_PRDNMSZ+1]; /* product name DE20x-xx */ + u_int8_t sc_eeprom[LEMAC_EEP_SIZE]; /* local copy eeprom */ +}; + +#define sc_if sc_arpcom.ac_if + +#define LEMAC_IFP_TO_SOFTC(ifp) ((struct lemac_softc *)((ifp)->if_softc)) +#define LEMAC_USE_PIO_MODE(sc) \ + (((sc->sc_flags & LEMAC_MODE_MASK) == LEMAC_PIO_MODE) || \ + (sc->sc_if.if_flags & IFF_LINK0)) + +#define LEMAC_OUTB(sc, o, v) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, (o), (v)) +#define LEMAC_OUTSB(sc, o, l, p) \ + bus_space_write_multi_1((sc)->sc_iot, (sc)->sc_ioh, (o), (p), (l)) +#define LEMAC_INB(sc, o) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, (o)) +#define LEMAC_INSB(sc, o, l, p) \ + bus_space_read_multi_1((sc)->sc_iot, (sc)->sc_ioh, (o), (p), (l)) + +#define LEMAC_PUTBUF8(sc, o, l, p) \ + bus_space_write_region_1((sc)->sc_memt, (sc)->sc_memh, (o), (p), (l)) +#define LEMAC_PUTBUF16(sc, o, l, p) \ + bus_space_write_raw_region_2((sc)->sc_memt, (sc)->sc_memh, (o), (p), \ + (l) << 1) +#define LEMAC_PUTBUF32(sc, o, l, p) \ + bus_space_write_raw_region_4((sc)->sc_memt, (sc)->sc_memh, (o), (p), \ + (l) << 2) + +#define LEMAC_PUT8(sc, o, v) \ + bus_space_write_1((sc)->sc_memt, (sc)->sc_memh, (o), (v)) +#define LEMAC_PUT16(sc, o, v) \ + bus_space_write_2((sc)->sc_memt, (sc)->sc_memh, (o), htole16(v)) +#define LEMAC_PUT32(sc, o, v) \ + bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), htole32(v)) + +#define LEMAC_GETBUF8(sc, o, l, p) \ + bus_space_read_region_1((sc)->sc_memt, (sc)->sc_memh, (o), (p), (l)) +#define LEMAC_GETBUF16(sc, o, l, p) \ + bus_space_read_raw_region_2((sc)->sc_memt, (sc)->sc_memh, (o), (p), \ + (l) << 1) +#define LEMAC_GETBUF32(sc, o, l, p) \ + bus_space_read_raw_region_4((sc)->sc_memt, (sc)->sc_memh, (o), (p), \ + (l) << 2) + +#define LEMAC_GET8(sc, o) \ + bus_space_read_1((sc)->sc_memt, (sc)->sc_memh, (o)) +#define LEMAC_GET16(sc, o) \ + letoh16(bus_space_read_2((sc)->sc_memt, (sc)->sc_memh, (o))) +#define LEMAC_GET32(sc, o) \ + letoh32(bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o))) + +#define LEMAC_INTR_ENABLE(sc) \ + LEMAC_OUTB(sc, LEMAC_REG_IC, \ + LEMAC_INB(sc, LEMAC_REG_IC) | LEMAC_IC_ALL) + +#define LEMAC_INTR_DISABLE(sc) \ + LEMAC_OUTB(sc, LEMAC_REG_IC, \ + LEMAC_INB(sc, LEMAC_REG_IC) & ~LEMAC_IC_ALL) + +#define LEMAC_IS_64K_MODE(mbase) (((mbase) >= 0x0A) && ((mbase) <= 0x0F)) +#define LEMAC_IS_32K_MODE(mbase) (((mbase) >= 0x14) && ((mbase) <= 0x1F)) +#define LEMAC_IS_2K_MODE(mbase) ((mbase) >= 0x40) + +#define LEMAC_DECODEIRQ(i) ((0xFBA5 >> ((i) >> 3)) & 0x0F) + +#define LEMAC_ADDREQUAL(a1, a2) \ + (((u_int16_t *)a1)[0] == ((u_int16_t *)a2)[0] && \ + ((u_int16_t *)a1)[1] == ((u_int16_t *)a2)[1] && \ + ((u_int16_t *)a1)[2] == ((u_int16_t *)a2)[2]) + +#define LEMAC_ADDRBRDCST(a1) \ + (((u_int16_t *)a1)[0] == 0xFFFFU && \ + ((u_int16_t *)a1)[1] == 0xFFFFU && \ + ((u_int16_t *)a1)[2] == 0xFFFFU) + +void lemac_ifattach(struct lemac_softc *); +void lemac_info_get(const bus_space_tag_t, const bus_space_handle_t, + bus_addr_t *, bus_size_t *, int *); +int lemac_port_check(const bus_space_tag_t, const bus_space_handle_t); +int lemac_intr(void *); +void lemac_shutdown(void *); + +#endif /* _LEMACVAR_H */ diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa index 9c7e5b2b533..41aa7133719 100644 --- a/sys/dev/isa/files.isa +++ b/sys/dev/isa/files.isa @@ -1,4 +1,4 @@ -# $OpenBSD: files.isa,v 1.65 2001/01/30 00:00:33 aaron Exp $ +# $OpenBSD: files.isa,v 1.66 2001/07/13 17:26:44 niklas Exp $ # $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $ # # Config file and device description for machine-independent ISA code. @@ -149,6 +149,10 @@ file dev/isa/wt.c wt needs-flag # ISA networking drivers # +# DEC EtherWORKS III (LEMAC-based: DE203, DE204, DE205) cards +attach lc at isa with lc_isa +file dev/isa/if_lc_isa.c lc_isa + # Novell NE1000, NE2000, and clones # Must be before the WD/SMC probe which it is hurt by. attach ne at isa with ne_isa: rtl80x9 diff --git a/sys/dev/isa/if_lc_isa.c b/sys/dev/isa/if_lc_isa.c new file mode 100644 index 00000000000..c50940103b9 --- /dev/null +++ b/sys/dev/isa/if_lc_isa.c @@ -0,0 +1,215 @@ +/* $OpenBSD: if_lc_isa.c,v 1.1 2001/07/13 17:26:44 niklas Exp $ */ +/* $NetBSD: if_lc_isa.c,v 1.10 2001/06/13 10:46:03 wiz Exp $ */ + +/*- + * Copyright (c) 1994, 1995, 1997 Matt Thomas <matt@3am-software.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * DEC EtherWORKS 3 Ethernet Controllers + * + * Written by Matt Thomas + * + * This driver supports the LEMAC (DE203, DE204, and DE205) cards. + */ + +#include "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> +#include <sys/select.h> +#include <sys/device.h> +#include <sys/queue.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#ifdef NS +#include <netns/ns.h> +#include <netns/ns_if.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/cpu.h> +#include <machine/bus.h> +#include <machine/intr.h> + +#include <dev/ic/lemacreg.h> +#include <dev/ic/lemacvar.h> + +#include <dev/isa/isavar.h> + +extern struct cfdriver lc_cd; + +int lemac_isa_find __P((struct lemac_softc *, struct isa_attach_args *, + int)); +int lemac_isa_probe __P((struct device *, void *, void *)); +void lemac_isa_attach __P((struct device *, struct device *, void *)); + +struct cfattach lc_isa_ca = { + sizeof(struct lemac_softc), lemac_isa_probe, lemac_isa_attach +}; + +int +lemac_isa_find(sc, ia, attach) + struct lemac_softc *sc; + struct isa_attach_args *ia; + int attach; +{ + bus_addr_t maddr; + bus_addr_t msize; + int rv = 0, irq; + + /* + * Disallow wildcarded i/o addresses. + */ + if (ia->ia_iobase == IOBASEUNK) + return 0; + + /* + * Make sure this is a valid LEMAC address. + */ + if (ia->ia_iobase & (LEMAC_IOSIZE - 1)) + return 0; + + sc->sc_iot = ia->ia_iot; + + /* + * Map the LEMAC's port space for the probe sequence. + */ + ia->ia_iosize = LEMAC_IOSIZE; + + if (bus_space_map(sc->sc_iot, ia->ia_iobase, ia->ia_iosize, 0, + &sc->sc_ioh)) { + if (attach) + printf(": can't map i/o space\n"); + return (0); + } + + /* + * Read the Ethernet address from the EEPROM. + * It must start with one of the DEC OUIs and pass the + * DEC ethernet checksum test. + */ + if (lemac_port_check(sc->sc_iot, sc->sc_ioh) == 0) + goto outio; + + /* + * Get information about memory space and attempt to map it. + */ + lemac_info_get(sc->sc_iot, sc->sc_ioh, &maddr, &msize, &irq); + + if (ia->ia_maddr != maddr && ia->ia_maddr != MADDRUNK) + goto outio; + + if (maddr != 0 && msize != 0) { + sc->sc_memt = ia->ia_memt; + if (bus_space_map(ia->ia_memt, maddr, msize, 0, + &sc->sc_memh)) { + if (attach) + printf(": can't map mem space\n"); + goto outio; + } + } + + /* + * Double-check IRQ configuration. + */ + if (ia->ia_irq != irq && ia->ia_irq != IRQUNK) + printf("%s: overriding IRQ %d to %d\n", sc->sc_dv.dv_xname, + ia->ia_irq, irq); + + if (attach) { + sc->sc_ats = shutdownhook_establish(lemac_shutdown, sc); + if (sc->sc_ats == NULL) + printf( + "\n%s: warning: can't establish shutdown hook\n", + sc->sc_dv.dv_xname); + + lemac_ifattach(sc); + + sc->sc_ih = isa_intr_establish(ia->ia_ic, irq, IST_EDGE, + IPL_NET, lemac_intr, sc, sc->sc_dv.dv_xname); + } + + /* + * I guess we've found one. + */ + rv = 1; + + ia->ia_maddr = maddr; + ia->ia_msize = msize; + ia->ia_irq = irq; + + if (maddr != 0 && msize != 0 && (rv == 0 || !attach)) + bus_space_unmap(sc->sc_memt, sc->sc_memh, msize); +outio: + if (rv == 0 || !attach) + bus_space_unmap(sc->sc_iot, sc->sc_ioh, LEMAC_IOSIZE); + return (rv); +} + +int +lemac_isa_probe(parent, match, aux) + struct device *parent; + void *match; + void *aux; +{ + struct isa_attach_args *ia = aux; + struct cfdata *cf = match; + struct lemac_softc sc; + + sprintf(sc.sc_dv.dv_xname, "%s%d", lc_cd.cd_name, cf->cf_unit); + + return (lemac_isa_find(&sc, ia, 0)); +} + +void +lemac_isa_attach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct lemac_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + + lemac_isa_find(sc, ia, 1); +} |