diff options
Diffstat (limited to 'sys/dev/ep')
-rw-r--r-- | sys/dev/ep/if_ep.c | 1000 | ||||
-rw-r--r-- | sys/dev/ep/if_ep_eisa.c | 266 | ||||
-rw-r--r-- | sys/dev/ep/if_ep_isa.c | 424 | ||||
-rw-r--r-- | sys/dev/ep/if_ep_mca.c | 165 | ||||
-rw-r--r-- | sys/dev/ep/if_ep_pccard.c | 286 | ||||
-rw-r--r-- | sys/dev/ep/if_epreg.h | 434 | ||||
-rw-r--r-- | sys/dev/ep/if_epvar.h | 84 |
7 files changed, 2659 insertions, 0 deletions
diff --git a/sys/dev/ep/if_ep.c b/sys/dev/ep/if_ep.c new file mode 100644 index 0000000..fd3d90a --- /dev/null +++ b/sys/dev/ep/if_ep.c @@ -0,0 +1,1000 @@ +/* + * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Herb Peyerl. + * 4. The name of Herb Peyerl 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. + * + * if_ep.c,v 1.19 1995/01/24 20:53:45 davidg Exp + */ + +/* + * Modified from the FreeBSD 1.1.5.1 version by: + * Andres Vega Garcia + * INRIA - Sophia Antipolis, France + * avega@sophia.inria.fr + */ + +/* + * $FreeBSD$ + * + * Promiscuous mode added and interrupt logic slightly changed + * to reduce the number of adapter failures. Transceiver select + * logic changed to use value from EEPROM. Autoconfiguration + * features added. + * Done by: + * Serge Babkin + * Chelindbank (Chelyabinsk, Russia) + * babkin@hq.icb.chel.su + */ + +/* + * Pccard support for 3C589 by: + * HAMADA Naoki + * nao@tom-yam.or.jp + */ + +/* + * MAINTAINER: Matthew N. Dodd <winter@jurai.net> + * <mdodd@FreeBSD.org> + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/sockio.h> + +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> +#include <net/ethernet.h> +#include <net/bpf.h> + + +#include <dev/ep/if_epreg.h> +#include <dev/ep/if_epvar.h> + +/* Exported variables */ +devclass_t ep_devclass; + +#if 0 +static char * ep_conn_type[] = {"UTP", "AUI", "???", "BNC"}; +static int if_media2ep_media[] = { 0, 0, 0, UTP, BNC, AUI }; +#endif + +static int ep_media2if_media[] = + { IFM_10_T, IFM_10_5, IFM_NONE, IFM_10_2, IFM_NONE }; + +/* if functions */ +static void ep_if_init (void *); +static int ep_if_ioctl (struct ifnet *, u_long, caddr_t); +static void ep_if_start (struct ifnet *); +static void ep_if_watchdog (struct ifnet *); + +/* if_media functions */ +static int ep_ifmedia_upd (struct ifnet *); +static void ep_ifmedia_sts (struct ifnet *, struct ifmediareq *); + +static void epstop (struct ep_softc *); +static void epread (struct ep_softc *); +static int eeprom_rdy (struct ep_softc *); + +#define EP_FTST(sc, f) (sc->stat & (f)) +#define EP_FSET(sc, f) (sc->stat |= (f)) +#define EP_FRST(sc, f) (sc->stat &= ~(f)) + +static int +eeprom_rdy(sc) + struct ep_softc *sc; +{ + int i; + + for (i = 0; is_eeprom_busy(BASE) && i < MAX_EEPROMBUSY; i++) { + DELAY(100); + } + if (i >= MAX_EEPROMBUSY) { + printf("ep%d: eeprom failed to come ready.\n", sc->unit); + return (ENXIO); + } + return (0); +} + +/* + * get_e: gets a 16 bits word from the EEPROM. we must have set the window + * before + */ +int +get_e(sc, offset, result) + struct ep_softc *sc; + u_int16_t offset; + u_int16_t *result; +{ + + if (eeprom_rdy(sc)) + return (ENXIO); + outw(BASE + EP_W0_EEPROM_COMMAND, + (EEPROM_CMD_RD << sc->epb.cmd_off) | offset); + if (eeprom_rdy(sc)) + return (ENXIO); + (*result) = inw(BASE + EP_W0_EEPROM_DATA); + + return (0); +} + +int +ep_get_macaddr(sc, addr) + struct ep_softc * sc; + u_char * addr; +{ + int i; + u_int16_t result; + int error; + u_int16_t * macaddr; + + macaddr = (u_int16_t *)addr; + + GO_WINDOW(0); + for(i = EEPROM_NODE_ADDR_0; i <= EEPROM_NODE_ADDR_2; i++) { + error = get_e(sc, i, &result); + if (error) + return (error); + macaddr[i] = htons(result); + } + + return (0); +} + +int +ep_alloc(device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + int rid; + int error = 0; + u_int16_t result; + + rid = 0; + sc->iobase = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->iobase) { + device_printf(dev, "No I/O space?!\n"); + error = ENXIO; + goto bad; + } + + rid = 0; + sc->irq = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!sc->irq) { + device_printf(dev, "No irq?!\n"); + error = ENXIO; + goto bad; + } + + sc->dev = dev; + sc->unit = device_get_unit(dev); + sc->stat = 0; /* 16 bit access */ + + sc->ep_io_addr = rman_get_start(sc->iobase); + + sc->ep_btag = rman_get_bustag(sc->iobase); + sc->ep_bhandle = rman_get_bushandle(sc->iobase); + + sc->ep_connectors = 0; + sc->ep_connector = 0; + + GO_WINDOW(0); + sc->epb.cmd_off = 0; + + error = get_e(sc, EEPROM_PROD_ID, &result); + if (error) + goto bad; + sc->epb.prod_id = result; + + error = get_e(sc, EEPROM_RESOURCE_CFG, &result); + if (error) + goto bad; + sc->epb.res_cfg = result; + +bad: + return (error); +} + +void +ep_get_media(sc) + struct ep_softc * sc; +{ + u_int16_t config; + + GO_WINDOW(0); + config = inw(BASE + EP_W0_CONFIG_CTRL); + if (config & IS_AUI) + sc->ep_connectors |= AUI; + if (config & IS_BNC) + sc->ep_connectors |= BNC; + if (config & IS_UTP) + sc->ep_connectors |= UTP; + + if (!(sc->ep_connectors & 7)) { + if (bootverbose) + device_printf(sc->dev, "no connectors!\n"); + } + + /* + * This works for most of the cards so we'll do it here. + * The cards that require something different can override + * this later on. + */ + sc->ep_connector = inw(BASE + EP_W0_ADDRESS_CFG) >> ACF_CONNECTOR_BITS; + + return; +} + +void +ep_free(device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + + if (sc->ep_intrhand) + bus_teardown_intr(dev, sc->irq, sc->ep_intrhand); + if (sc->iobase) + bus_release_resource(dev, SYS_RES_IOPORT, 0, sc->iobase); + if (sc->irq) + bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq); + + return; +} + +int +ep_attach(sc) + struct ep_softc * sc; +{ + struct ifnet * ifp = NULL; + struct ifmedia * ifm = NULL; + u_short * p; + int i; + int attached; + int error; + + sc->gone = 0; + + error = ep_get_macaddr(sc, (u_char *)&sc->arpcom.ac_enaddr); + if (error) { + device_printf(sc->dev, "Unable to retrieve Ethernet address!\n"); + return (ENXIO); + } + + /* + * Setup the station address + */ + p = (u_short *)&sc->arpcom.ac_enaddr; + GO_WINDOW(2); + for (i = 0; i < 3; i++) { + outw(BASE + EP_W2_ADDR_0 + (i * 2), ntohs(p[i])); + } + + device_printf(sc->dev, "Ethernet address %6D\n", + sc->arpcom.ac_enaddr, ":"); + + ifp = &sc->arpcom.ac_if; + attached = (ifp->if_softc != 0); + + ifp->if_softc = sc; + ifp->if_unit = sc->unit; + ifp->if_name = "ep"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_output = ether_output; + ifp->if_start = ep_if_start; + ifp->if_ioctl = ep_if_ioctl; + ifp->if_watchdog = ep_if_watchdog; + ifp->if_init = ep_if_init; + ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; + + if (!sc->epb.mii_trans) { + ifmedia_init(&sc->ifmedia, 0, ep_ifmedia_upd, ep_ifmedia_sts); + + if (sc->ep_connectors & AUI) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL); + if (sc->ep_connectors & UTP) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL); + if (sc->ep_connectors & BNC) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL); + if (!sc->ep_connectors) + ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_NONE, 0, NULL); + + ifmedia_set(&sc->ifmedia, IFM_ETHER|ep_media2if_media[sc->ep_connector]); + + ifm = &sc->ifmedia; + ifm->ifm_media = ifm->ifm_cur->ifm_media; + ep_ifmedia_upd(ifp); + } + + if (!attached) + ether_ifattach(ifp, sc->arpcom.ac_enaddr); + +#ifdef EP_LOCAL_STATS + sc->rx_no_first = sc->rx_no_mbuf = sc->rx_bpf_disc = + sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0; +#endif + EP_FSET(sc, F_RX_FIRST); + sc->top = sc->mcur = 0; + + epstop(sc); + + return 0; +} + +int +ep_detach(device_t dev) +{ + struct ep_softc *sc; + struct ifnet *ifp; + + sc = device_get_softc(dev); + ifp = &sc->arpcom.ac_if; + + if (sc->gone) { + device_printf(dev, "already unloaded\n"); + return (0); + } + + epstop(sc); + + ifp->if_flags &= ~IFF_RUNNING; + ether_ifdetach(ifp); + + sc->gone = 1; + ep_free(dev); + + return (0); +} + +/* + * The order in here seems important. Otherwise we may not receive + * interrupts. ?! + */ +static void +ep_if_init(xsc) + void *xsc; +{ + struct ep_softc *sc = xsc; + register struct ifnet *ifp = &sc->arpcom.ac_if; + int s, i; + + if (sc->gone) + return; + + /* + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; + */ + + s = splimp(); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + + GO_WINDOW(0); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP); + GO_WINDOW(0); + + /* Disable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, 0); + + /* Enable the card */ + outw(BASE + EP_W0_CONFIG_CTRL, ENABLE_DRQ_IRQ); + + GO_WINDOW(2); + + /* Reload the ether_addr. */ + for (i = 0; i < 6; i++) + outb(BASE + EP_W2_ADDR_0 + i, sc->arpcom.ac_enaddr[i]); + + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + + /* Window 1 is operating window */ + GO_WINDOW(1); + for (i = 0; i < 31; i++) + inb(BASE + EP_W1_TX_STATUS); + + /* get rid of stray intr's */ + outw(BASE + EP_COMMAND, ACK_INTR | 0xff); + + outw(BASE + EP_COMMAND, SET_RD_0_MASK | S_5_INTS); + + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); + + if (ifp->if_flags & IFF_PROMISC) + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST | FIL_ALL); + else + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST); + + if (!sc->epb.mii_trans) { + ep_ifmedia_upd(ifp); + } + + outw(BASE + EP_COMMAND, RX_ENABLE); + outw(BASE + EP_COMMAND, TX_ENABLE); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; /* just in case */ + +#ifdef EP_LOCAL_STATS + sc->rx_no_first = sc->rx_no_mbuf = + sc->rx_overrunf = sc->rx_overrunl = sc->tx_underrun = 0; +#endif + EP_FSET(sc, F_RX_FIRST); + if (sc->top) { + m_freem(sc->top); + sc->top = sc->mcur = 0; + } + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH); + outw(BASE + EP_COMMAND, SET_TX_START_THRESH | 16); + + /* + * Store up a bunch of mbuf's for use later. (MAX_MBS). First we free up + * any that we had in case we're being called from intr or somewhere + * else. + */ + + GO_WINDOW(1); + ep_if_start(ifp); + + splx(s); +} + +static void +ep_if_start(ifp) + struct ifnet *ifp; +{ + struct ep_softc *sc = ifp->if_softc; + u_int len; + struct mbuf *m; + struct mbuf *top; + int s, pad; + + if (sc->gone) { + return; + } + + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + if (ifp->if_flags & IFF_OACTIVE) { + return; + } + +startagain: + /* Sneak a peek at the next packet */ + m = ifp->if_snd.ifq_head; + if (m == 0) { + return; + } + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; + + pad = (4 - len) & 3; + + /* + * The 3c509 automatically pads short packets to minimum ethernet length, + * but we drop packets that are too large. Perhaps we should truncate + * them instead? + */ + if (len + pad > ETHER_MAX_LEN) { + /* packet is obviously too large: toss it */ + ++ifp->if_oerrors; + IF_DEQUEUE(&ifp->if_snd, m); + m_freem(m); + goto readcheck; + } + if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { + /* no room in FIFO */ + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | (len + pad + 4)); + /* make sure */ + if (inw(BASE + EP_W1_FREE_TX) < len + pad + 4) { + ifp->if_flags |= IFF_OACTIVE; + return; + } + } else { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | EP_THRESH_DISABLE); + } + + IF_DEQUEUE(&ifp->if_snd, m); + + s = splhigh(); + + outw(BASE + EP_W1_TX_PIO_WR_1, len); + outw(BASE + EP_W1_TX_PIO_WR_1, 0x0); /* Second dword meaningless */ + + if (EP_FTST(sc, F_ACCESS_32_BITS)) { + for (top = m; m != 0; m = m->m_next) { + if (m->m_len > 3) + outsl(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t), m->m_len / 4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + (m->m_len & (~3)), m->m_len & 3); + } + } else { + for (top = m; m != 0; m = m->m_next) { + if (m->m_len > 1) + outsw(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t), m->m_len / 2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); + } + } + + while (pad--) + outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ + + splx(s); + + BPF_MTAP(ifp, top); + + ifp->if_timer = 2; + ifp->if_opackets++; + m_freem(top); + + /* + * Is another packet coming in? We don't want to overflow the tiny RX + * fifo. + */ +readcheck: + if (inw(BASE + EP_W1_RX_STATUS) & RX_BYTES_MASK) { + /* + * we check if we have packets left, in that case we prepare to come + * back later + */ + if (ifp->if_snd.ifq_head) { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); + } + return; + } + goto startagain; +} + +void +ep_intr(arg) + void *arg; +{ + struct ep_softc *sc; + register int status; + struct ifnet *ifp; + int x; + + x = splbio(); + + sc = (struct ep_softc *)arg; + + /* + * quick fix: Try to detect an interrupt when the card goes away. + */ + if (sc->gone || inw(BASE + EP_STATUS) == 0xffff) { + splx(x); + return; + } + + ifp = &sc->arpcom.ac_if; + + outw(BASE + EP_COMMAND, SET_INTR_MASK); /* disable all Ints */ + +rescan: + + while ((status = inw(BASE + EP_STATUS)) & S_5_INTS) { + + /* first acknowledge all interrupt sources */ + outw(BASE + EP_COMMAND, ACK_INTR | (status & S_MASK)); + + if (status & (S_RX_COMPLETE | S_RX_EARLY)) + epread(sc); + if (status & S_TX_AVAIL) { + /* we need ACK */ + ifp->if_timer = 0; + ifp->if_flags &= ~IFF_OACTIVE; + GO_WINDOW(1); + inw(BASE + EP_W1_FREE_TX); + ep_if_start(ifp); + } + if (status & S_CARD_FAILURE) { + ifp->if_timer = 0; +#ifdef EP_LOCAL_STATS + printf("\nep%d:\n\tStatus: %x\n", sc->unit, status); + GO_WINDOW(4); + printf("\tFIFO Diagnostic: %x\n", inw(BASE + EP_W4_FIFO_DIAG)); + printf("\tStat: %x\n", sc->stat); + printf("\tIpackets=%d, Opackets=%d\n", + ifp->if_ipackets, ifp->if_opackets); + printf("\tNOF=%d, NOMB=%d, RXOF=%d, RXOL=%d, TXU=%d\n", + sc->rx_no_first, sc->rx_no_mbuf, sc->rx_overrunf, + sc->rx_overrunl, sc->tx_underrun); +#else + +#ifdef DIAGNOSTIC + printf("ep%d: Status: %x (input buffer overflow)\n", sc->unit, status); +#else + ++ifp->if_ierrors; +#endif + +#endif + ep_if_init(sc); + splx(x); + return; + } + if (status & S_TX_COMPLETE) { + ifp->if_timer = 0; + /* we need ACK. we do it at the end */ + /* + * We need to read TX_STATUS until we get a 0 status in order to + * turn off the interrupt flag. + */ + while ((status = inb(BASE + EP_W1_TX_STATUS)) & TXS_COMPLETE) { + if (status & TXS_SUCCES_INTR_REQ); + else if (status & (TXS_UNDERRUN | TXS_JABBER | TXS_MAX_COLLISION)) { + outw(BASE + EP_COMMAND, TX_RESET); + if (status & TXS_UNDERRUN) { +#ifdef EP_LOCAL_STATS + sc->tx_underrun++; +#endif + } else { + if (status & TXS_JABBER); + else /* TXS_MAX_COLLISION - we shouldn't get here */ + ++ifp->if_collisions; + } + ++ifp->if_oerrors; + outw(BASE + EP_COMMAND, TX_ENABLE); + /* + * To have a tx_avail_int but giving the chance to the + * Reception + */ + if (ifp->if_snd.ifq_head) { + outw(BASE + EP_COMMAND, SET_TX_AVAIL_THRESH | 8); + } + } + outb(BASE + EP_W1_TX_STATUS, 0x0); /* pops up the next + * status */ + } /* while */ + ifp->if_flags &= ~IFF_OACTIVE; + GO_WINDOW(1); + inw(BASE + EP_W1_FREE_TX); + ep_if_start(ifp); + } /* end TX_COMPLETE */ + } + + outw(BASE + EP_COMMAND, C_INTR_LATCH); /* ACK int Latch */ + + if ((status = inw(BASE + EP_STATUS)) & S_5_INTS) + goto rescan; + + /* re-enable Ints */ + outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); + + splx(x); +} + +static void +epread(sc) + register struct ep_softc *sc; +{ + struct mbuf *top, *mcur, *m; + struct ifnet *ifp; + int lenthisone; + + short rx_fifo2, status; + register short rx_fifo; + + ifp = &sc->arpcom.ac_if; + status = inw(BASE + EP_W1_RX_STATUS); + +read_again: + + if (status & ERR_RX) { + ++ifp->if_ierrors; + if (status & ERR_RX_OVERRUN) { + /* + * we can think the rx latency is actually greather than we + * expect + */ +#ifdef EP_LOCAL_STATS + if (EP_FTST(sc, F_RX_FIRST)) + sc->rx_overrunf++; + else + sc->rx_overrunl++; +#endif + } + goto out; + } + rx_fifo = rx_fifo2 = status & RX_BYTES_MASK; + + if (EP_FTST(sc, F_RX_FIRST)) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (!m) + goto out; + if (rx_fifo >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + sc->top = sc->mcur = top = m; +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + top->m_data += EOFF; + + /* Read what should be the header. */ + insw(BASE + EP_W1_RX_PIO_RD_1, + mtod(top, caddr_t), sizeof(struct ether_header) / 2); + top->m_len = sizeof(struct ether_header); + rx_fifo -= sizeof(struct ether_header); + sc->cur_len = rx_fifo2; + } else { + /* come here if we didn't have a complete packet last time */ + top = sc->top; + m = sc->mcur; + sc->cur_len += rx_fifo2; + } + + /* Reads what is left in the RX FIFO */ + while (rx_fifo > 0) { + lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); + if (lenthisone == 0) { /* no room in this one */ + mcur = m; + MGET(m, M_DONTWAIT, MT_DATA); + if (!m) + goto out; + if (rx_fifo >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + m->m_len = 0; + mcur->m_next = m; + lenthisone = min(rx_fifo, M_TRAILINGSPACE(m)); + } + if (EP_FTST(sc, F_ACCESS_32_BITS)) { /* default for EISA configured cards*/ + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); + } else { + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + } + rx_fifo -= lenthisone; + } + + if (status & ERR_RX_INCOMPLETE) { /* we haven't received the complete + * packet */ + sc->mcur = m; +#ifdef EP_LOCAL_STATS + sc->rx_no_first++; /* to know how often we come here */ +#endif + EP_FRST(sc, F_RX_FIRST); + if (!((status = inw(BASE + EP_W1_RX_STATUS)) & ERR_RX_INCOMPLETE)) { + /* we see if by now, the packet has completly arrived */ + goto read_again; + } + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_NEXT_EARLY_THRESH); + return; + } + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + ++ifp->if_ipackets; + EP_FSET(sc, F_RX_FIRST); + top->m_pkthdr.rcvif = &sc->arpcom.ac_if; + top->m_pkthdr.len = sc->cur_len; + + (*ifp->if_input)(ifp, top); + sc->top = 0; + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH); + return; + +out: + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + if (sc->top) { + m_freem(sc->top); + sc->top = 0; +#ifdef EP_LOCAL_STATS + sc->rx_no_mbuf++; +#endif + } + EP_FSET(sc, F_RX_FIRST); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, SET_RX_EARLY_THRESH | RX_INIT_EARLY_THRESH); +} + +static int +ep_ifmedia_upd(ifp) + struct ifnet * ifp; +{ + struct ep_softc * sc = ifp->if_softc; + int i = 0, j; + + GO_WINDOW(0); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, DISABLE_UTP); + GO_WINDOW(0); + + switch (IFM_SUBTYPE(sc->ifmedia.ifm_media)) { + case IFM_10_T: + if (sc->ep_connectors & UTP) { + i = ACF_CONNECTOR_UTP; + GO_WINDOW(4); + outw(BASE + EP_W4_MEDIA_TYPE, ENABLE_UTP); + } + break; + case IFM_10_2: + if (sc->ep_connectors & BNC) { + i = ACF_CONNECTOR_BNC; + outw(BASE + EP_COMMAND, START_TRANSCEIVER); + DELAY(DELAY_MULTIPLE * 1000); + } + break; + case IFM_10_5: + if (sc->ep_connectors & AUI) + i = ACF_CONNECTOR_AUI; + break; + default: + i = sc->ep_connector; + device_printf(sc->dev, + "strange connector type in EEPROM: assuming AUI\n"); + } + + GO_WINDOW(0); + j = inw(BASE + EP_W0_ADDRESS_CFG) & 0x3fff; + outw(BASE + EP_W0_ADDRESS_CFG, j | (i << ACF_CONNECTOR_BITS)); + + return (0); +} + +static void +ep_ifmedia_sts(ifp, ifmr) + struct ifnet * ifp; + struct ifmediareq * ifmr; +{ + struct ep_softc * sc = ifp->if_softc; + + ifmr->ifm_active = sc->ifmedia.ifm_media; + + return; +} + +static int +ep_if_ioctl(ifp, cmd, data) + struct ifnet * ifp; + u_long cmd; + caddr_t data; +{ + struct ep_softc * sc = ifp->if_softc; + struct ifreq * ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + switch (cmd) { + case SIOCSIFFLAGS: + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + ifp->if_flags &= ~IFF_RUNNING; + epstop(sc); + } else { + /* reinitialize card on any parameter change */ + ep_if_init(sc); + } + break; +#ifdef notdef + case SIOCGHWADDR: + bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data, + sizeof(sc->sc_addr)); + break; +#endif + case SIOCADDMULTI: + case SIOCDELMULTI: + /* + * The Etherlink III has no programmable multicast + * filter. We always initialize the card to be + * promiscuous to multicast, since we're always a + * member of the ALL-SYSTEMS group, so there's no + * need to process SIOC*MULTI requests. + */ + error = 0; + break; + case SIOCSIFMEDIA: + case SIOCGIFMEDIA: + if (!sc->epb.mii_trans) { + error = ifmedia_ioctl(ifp, ifr, &sc->ifmedia, cmd); + } else { + error = EINVAL; + } + break; + default: + error = ether_ioctl(ifp, cmd, data); + break; + } + + (void)splx(s); + + return (error); +} + +static void +ep_if_watchdog(ifp) + struct ifnet *ifp; +{ + struct ep_softc *sc = ifp->if_softc; + + /* + printf("ep: watchdog\n"); + + log(LOG_ERR, "ep%d: watchdog\n", ifp->if_unit); + ifp->if_oerrors++; + */ + + if (sc->gone) { + return; + } + + ifp->if_flags &= ~IFF_OACTIVE; + ep_if_start(ifp); + ep_intr(ifp->if_softc); +} + +static void +epstop(sc) + struct ep_softc *sc; +{ + if (sc->gone) { + return; + } + + outw(BASE + EP_COMMAND, RX_DISABLE); + outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + + outw(BASE + EP_COMMAND, TX_DISABLE); + outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); + DELAY(800); + + outw(BASE + EP_COMMAND, RX_RESET); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + outw(BASE + EP_COMMAND, TX_RESET); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + + outw(BASE + EP_COMMAND, C_INTR_LATCH); + outw(BASE + EP_COMMAND, SET_RD_0_MASK); + outw(BASE + EP_COMMAND, SET_INTR_MASK); + outw(BASE + EP_COMMAND, SET_RX_FILTER); +} diff --git a/sys/dev/ep/if_ep_eisa.c b/sys/dev/ep/if_ep_eisa.c new file mode 100644 index 0000000..e9b8348 --- /dev/null +++ b/sys/dev/ep/if_ep_eisa.c @@ -0,0 +1,266 @@ +/* + * Product specific probe and attach routines for: + * 3COM 3C579 and 3C509(in eisa config mode) ethernet controllers + * + * Copyright (c) 1996 Justin T. Gibbs + * 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 immediately at the beginning of the file, without modification, + * this list of conditions, and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Absolutely no warranty of function or purpose is made by the author + * Justin T. Gibbs. + * 4. Modifications may be freely made to this file if the above conditions + * are met. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + + +#include <dev/eisa/eisaconf.h> + +#include <dev/ep/if_epreg.h> +#include <dev/ep/if_epvar.h> + +#define EISA_DEVICE_ID_3COM_3C509_TP 0x506d5090 +#define EISA_DEVICE_ID_3COM_3C509_BNC 0x506d5091 +#define EISA_DEVICE_ID_3COM_3C579_TP 0x506d5092 +#define EISA_DEVICE_ID_3COM_3C579_BNC 0x506d5093 +#define EISA_DEVICE_ID_3COM_3C509_COMBO 0x506d5094 +#define EISA_DEVICE_ID_3COM_3C509_TPO 0x506d5095 + +#define EP_EISA_SLOT_OFFSET 0x0c80 +#define EP_EISA_IOSIZE 0x000a + +#define EISA_IOCONF 0x0008 +#define IRQ_CHANNEL 0xf000 +#define INT_3 0x3000 +#define INT_5 0x5000 +#define INT_7 0x7000 +#define INT_9 0x9000 +#define INT_10 0xa000 +#define INT_11 0xb000 +#define INT_12 0xc000 +#define INT_15 0xf000 +#define EISA_BPROM_MEDIA_CONF 0x0006 +#define TRANS_TYPE 0xc000 +#define TRANS_TP 0x0000 +#define TRANS_AUI 0x4000 +#define TRANS_BNC 0xc000 + +static const char *ep_match(eisa_id_t type); + +static const char* +ep_match(eisa_id_t type) +{ + switch(type) { + case EISA_DEVICE_ID_3COM_3C509_TP: + return "3Com 3C509-TP Network Adapter"; + break; + case EISA_DEVICE_ID_3COM_3C509_BNC: + return "3Com 3C509-BNC Network Adapter"; + break; + case EISA_DEVICE_ID_3COM_3C579_TP: + return "3Com 3C579-TP EISA Network Adapter"; + break; + case EISA_DEVICE_ID_3COM_3C579_BNC: + return "3Com 3C579-BNC EISA Network Adapter"; + break; + case EISA_DEVICE_ID_3COM_3C509_COMBO: + return "3Com 3C509-Combo Network Adapter"; + break; + case EISA_DEVICE_ID_3COM_3C509_TPO: + return "3Com 3C509-TPO Network Adapter"; + break; + default: + break; + } + return (NULL); +} + +static int +ep_eisa_probe(device_t dev) +{ + const char *desc; + u_long iobase; + u_short conf; + u_long port; + int irq; + int int_trig; + + desc = ep_match(eisa_get_id(dev)); + if (!desc) + return (ENXIO); + device_set_desc(dev, desc); + + port = (eisa_get_slot(dev) * EISA_SLOT_SIZE); + iobase = port + EP_EISA_SLOT_OFFSET; + + /* We must be in EISA configuration mode */ + if ((inw(iobase + EP_W0_ADDRESS_CFG) & 0x1f) != 0x1f) + return ENXIO; + + eisa_add_iospace(dev, iobase, EP_EISA_IOSIZE, RESVADDR_NONE); + eisa_add_iospace(dev, port, EP_IOSIZE, RESVADDR_NONE); + + conf = inw(iobase + EISA_IOCONF); + /* Determine our IRQ */ + switch (conf & IRQ_CHANNEL) { + case INT_3: + irq = 3; + break; + case INT_5: + irq = 5; + break; + case INT_7: + irq = 7; + break; + case INT_9: + irq = 9; + break; + case INT_10: + irq = 10; + break; + case INT_11: + irq = 11; + break; + case INT_12: + irq = 12; + break; + case INT_15: + irq = 15; + break; + default: + /* Disabled */ + printf("ep: 3COM Network Adapter at " + "slot %d has its IRQ disabled. " + "Probe failed.\n", + eisa_get_slot(dev)); + return ENXIO; + } + + switch(eisa_get_id(dev)) { + case EISA_DEVICE_ID_3COM_3C579_BNC: + case EISA_DEVICE_ID_3COM_3C579_TP: + int_trig = EISA_TRIGGER_LEVEL; + break; + default: + int_trig = EISA_TRIGGER_EDGE; + break; + } + + eisa_add_intr(dev, irq, int_trig); + + return 0; +} + +static int +ep_eisa_attach(device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + struct resource * eisa_io = NULL; + u_int32_t eisa_iobase; + int irq; + int error = 0; + int rid; + + rid = 1; + eisa_io = bus_alloc_resource(dev, SYS_RES_IOPORT, &rid, + 0, ~0, 1, RF_ACTIVE); + if (!eisa_io) { + device_printf(dev, "No I/O space?!\n"); + error = ENXIO; + goto bad; + } + eisa_iobase = rman_get_start(eisa_io); + + /* Reset and Enable the card */ + outb(eisa_iobase + EP_W0_CONFIG_CTRL, W0_P4_CMD_RESET_ADAPTER); + DELAY(1000); /* we must wait at least 1 ms */ + outb(eisa_iobase + EP_W0_CONFIG_CTRL, W0_P4_CMD_ENABLE_ADAPTER); + /* Now the registers are availible through the lower ioport */ + + if ((error = ep_alloc(dev))) { + device_printf(dev, "ep_alloc() failed! (%d)\n", error); + goto bad; + } + + switch(eisa_get_id(dev)) { + case EISA_DEVICE_ID_3COM_3C579_BNC: + case EISA_DEVICE_ID_3COM_3C579_TP: + sc->stat = F_ACCESS_32_BITS; + break; + default: + break; + } + + ep_get_media(sc); + + irq = rman_get_start(sc->irq); + if (irq == 9) + irq = 2; + + GO_WINDOW(0); + SET_IRQ(BASE, irq); + + if ((error = ep_attach(sc))) { + device_printf(dev, "ep_attach() failed! (%d)\n", error); + goto bad; + } + + if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ep_intr, + sc, &sc->ep_intrhand))) { + device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); + goto bad; + } + + return (0); + + bad: + if (eisa_io) + bus_release_resource(dev, SYS_RES_IOPORT, 0, eisa_io); + + ep_free(dev); + return (error); +} + +static device_method_t ep_eisa_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ep_eisa_probe), + DEVMETHOD(device_attach, ep_eisa_attach), + DEVMETHOD(device_detach, ep_detach), + + { 0, 0 } +}; + +static driver_t ep_eisa_driver = { + "ep", + ep_eisa_methods, + sizeof(struct ep_softc), +}; + +extern devclass_t ep_devclass; + +DRIVER_MODULE(ep, eisa, ep_eisa_driver, ep_devclass, 0, 0); diff --git a/sys/dev/ep/if_ep_isa.c b/sys/dev/ep/if_ep_isa.c new file mode 100644 index 0000000..1bef908 --- /dev/null +++ b/sys/dev/ep/if_ep_isa.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Herb Peyerl. + * 4. The name of Herb Peyerl 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + +#include <isa/isavar.h> + +#include <dev/ep/if_epreg.h> +#include <dev/ep/if_epvar.h> + +#ifdef __i386__ +#include <i386/isa/elink.h> +#endif + +#ifdef __i386__ +static u_int16_t get_eeprom_data (int, int); +static void ep_isa_identify (driver_t *, device_t); +#endif +static int ep_isa_probe (device_t); +static int ep_isa_attach (device_t); +static int ep_eeprom_cksum (struct ep_softc *); + +struct isa_ident { + u_int32_t id; + char * name; +}; +const char * ep_isa_match_id (u_int32_t, struct isa_ident *); + +#define ISA_ID_3C509_XXX 0x0506d509 +#define ISA_ID_3C509_TP 0x506d5090 +#define ISA_ID_3C509_BNC 0x506d5091 +#define ISA_ID_3C509_COMBO 0x506d5094 +#define ISA_ID_3C509_TPO 0x506d5095 +#define ISA_ID_3C509_TPC 0x506d5098 +#ifdef PC98 +#define ISA_ID_3C569B_COMBO 0x506d5694 +#define ISA_ID_3C569B_TPO 0x506d5695 +#endif + +#ifdef __i386__ +static struct isa_ident ep_isa_devs[] = { + { ISA_ID_3C509_TP, "3Com 3C509-TP EtherLink III" }, + { ISA_ID_3C509_BNC, "3Com 3C509-BNC EtherLink III" }, + { ISA_ID_3C509_COMBO, "3Com 3C509-Combo EtherLink III" }, + { ISA_ID_3C509_TPO, "3Com 3C509-TPO EtherLink III" }, + { ISA_ID_3C509_TPC, "3Com 3C509-TPC EtherLink III" }, +#ifdef PC98 + { ISA_ID_3C569B_COMBO, "3Com 3C569B-J-Combo EtherLink III" }, + { ISA_ID_3C569B_TPO, "3Com 3C569B-J-TPO EtherLink III" }, +#endif + { 0, NULL }, +}; +#endif + +static struct isa_pnp_id ep_ids[] = { + { 0x90506d50, "3Com 3C509B-TP EtherLink III (PnP)" }, /* TCM5090 */ + { 0x91506d50, "3Com 3C509B-BNC EtherLink III (PnP)" },/* TCM5091 */ + { 0x94506d50, "3Com 3C509B-Combo EtherLink III (PnP)" },/* TCM5094 */ + { 0x95506d50, "3Com 3C509B-TPO EtherLink III (PnP)" },/* TCM5095 */ + { 0x98506d50, "3Com 3C509B-TPC EtherLink III (PnP)" },/* TCM5098 */ + { 0xf780d041, NULL }, /* PNP80f7 */ + { 0, NULL }, +}; + +/* + * We get eeprom data from the id_port given an offset into the eeprom. + * Basically; after the ID_sequence is sent to all of the cards; they enter + * the ID_CMD state where they will accept command requests. 0x80-0xbf loads + * the eeprom data. We then read the port 16 times and with every read; the + * cards check for contention (ie: if one card writes a 0 bit and another + * writes a 1 bit then the host sees a 0. At the end of the cycle; each card + * compares the data on the bus; if there is a difference then that card goes + * into ID_WAIT state again). In the meantime; one bit of data is returned in + * the AX register which is conveniently returned to us by inb(). Hence; we + * read 16 times getting one bit of data with each read. + */ +#ifdef __i386__ +static u_int16_t +get_eeprom_data(id_port, offset) + int id_port; + int offset; +{ + int i; + u_int16_t data = 0; + + outb(id_port, EEPROM_CMD_RD|offset); + DELAY(BIT_DELAY_MULTIPLE * 1000); + for (i = 0; i < 16; i++) { + DELAY(50); + data = (data << 1) | (inw(id_port) & 1); + } + return (data); +} +#endif + +const char * +ep_isa_match_id (id, isa_devs) + u_int32_t id; + struct isa_ident * isa_devs; +{ + struct isa_ident * i = isa_devs; + while(i->name != NULL) { + if (id == i->id) + return (i->name); + i++; + } + /* + * If we see a card that is likely to be a 3c509 + * return something so that it will work; be annoying + * so that the user will tell us about it though. + */ + if ((id >> 4) == ISA_ID_3C509_XXX) { + return ("Unknown 3c509; notify maintainer!"); + } + return (NULL); +} + +#ifdef __i386__ +static void +ep_isa_identify (driver_t *driver, device_t parent) +{ + int tag = EP_LAST_TAG; + int found = 0; + int i; + int j; + const char * desc; + u_int16_t data; + u_int32_t irq; + u_int32_t ioport; + u_int32_t isa_id; + device_t child; + + outb(ELINK_ID_PORT, 0); + outb(ELINK_ID_PORT, 0); + elink_idseq(ELINK_509_POLY); + elink_reset(); + + DELAY(DELAY_MULTIPLE * 10000); + + for (i = 0; i < EP_MAX_BOARDS; i++) { + + outb(ELINK_ID_PORT, 0); + outb(ELINK_ID_PORT, 0); + elink_idseq(ELINK_509_POLY); + DELAY(400); + + /* For the first probe, clear all + * board's tag registers. + * Otherwise kill off already-found + * boards. -- linux 3c509.c + */ + if (i == 0) { + outb(ELINK_ID_PORT, 0xd0); + } else { + outb(ELINK_ID_PORT, 0xd8); + } + + /* Get out of loop if we're out of cards. */ + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); + if (data != MFG_ID) { + break; + } + + /* resolve contention using the Ethernet address */ + for (j = 0; j < 3; j++) { + (void)get_eeprom_data(ELINK_ID_PORT, j); + } + + /* + * Construct an 'isa_id' in 'EISA' + * format. + */ + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_MFG_ID); + isa_id = (htons(data) << 16); + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_PROD_ID); + isa_id |= htons(data); + + /* Find known ISA boards */ + desc = ep_isa_match_id(isa_id, ep_isa_devs); + if (!desc) { + if (bootverbose) { + device_printf(parent, "if_ep: unknown ID 0x%08x\n", + isa_id); + } + continue; + } + + /* Retreive IRQ */ + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_RESOURCE_CFG); + irq = (data >> 12); + + /* Retreive IOPORT */ + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_ADDR_CFG); +#ifdef PC98 + ioport = (((data & ADDR_CFG_MASK) * 0x100) + 0x40d0); +#else + ioport = (((data & ADDR_CFG_MASK) << 4) + 0x200); +#endif + + if ((data & ADDR_CFG_MASK) == ADDR_CFG_EISA) { + device_printf(parent, "if_ep: <%s> at port 0x%03x in EISA mode!\n", + desc, ioport); + /* Set the adaptor tag so that the next card can be found. */ + outb(ELINK_ID_PORT, tag--); + continue; + } + + /* Test for an adapter with PnP support. */ + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_CAP); + if (data == CAP_ISA) { + data = get_eeprom_data(ELINK_ID_PORT, EEPROM_INT_CONFIG_1); + if (data & ICW1_IAS_PNP) { + if (bootverbose) { + device_printf(parent, "if_ep: <%s> at 0x%03x in PnP mode!\n", + desc, ioport); + } + /* Set the adaptor tag so that the next card can be found. */ + outb(ELINK_ID_PORT, tag--); + continue; + } + } + + /* Set the adaptor tag so that the next card can be found. */ + outb(ELINK_ID_PORT, tag--); + + /* Activate the adaptor at the EEPROM location. */ + outb(ELINK_ID_PORT, ACTIVATE_ADAPTER_TO_CONFIG); + + /* Test for an adapter in TEST mode. */ + outw(ioport + EP_COMMAND, WINDOW_SELECT | 0); + data = inw(ioport + EP_W0_EEPROM_COMMAND); + if (data & EEPROM_TST_MODE) { + device_printf(parent, "if_ep: <%s> at port 0x%03x in TEST mode! Erase pencil mark.\n", + desc, ioport); + continue; + } + + child = BUS_ADD_CHILD(parent, ISA_ORDER_SPECULATIVE, "ep", -1); + device_set_desc_copy(child, desc); + device_set_driver(child, driver); + bus_set_resource(child, SYS_RES_IRQ, 0, irq, 1); + bus_set_resource(child, SYS_RES_IOPORT, 0, ioport, EP_IOSIZE); + + if (bootverbose) { + device_printf(parent, "if_ep: <%s> at port 0x%03x-0x%03x irq %d\n", + desc, ioport, ioport + EP_IOSIZE, irq); + } + + found++; + } + + return; +} +#endif + +static int +ep_isa_probe (device_t dev) +{ + int error = 0; + + /* Check isapnp ids */ + error = ISA_PNP_PROBE(device_get_parent(dev), dev, ep_ids); + + /* If the card had a PnP ID that didn't match any we know about */ + if (error == ENXIO) { + return (error); + } + + /* If we had some other problem. */ + if (!(error == 0 || error == ENOENT)) { + return (error); + } + + /* If we have the resources we need then we're good to go. */ + if ((bus_get_resource_start(dev, SYS_RES_IOPORT, 0) != 0) && + (bus_get_resource_start(dev, SYS_RES_IRQ, 0) != 0)) { + return (0); + } + + return (ENXIO); +} + +static int +ep_isa_attach (device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + int error = 0; + + if ((error = ep_alloc(dev))) { + device_printf(dev, "ep_alloc() failed! (%d)\n", error); + goto bad; + } + + ep_get_media(sc); + + GO_WINDOW(0); + SET_IRQ(BASE, rman_get_start(sc->irq)); + + if ((error = ep_attach(sc))) { + device_printf(dev, "ep_attach() failed! (%d)\n", error); + goto bad; + } + + error = ep_eeprom_cksum(sc); + if (error) { + device_printf(sc->dev, "Invalid EEPROM checksum!\n"); + goto bad; + } + + if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ep_intr, + sc, &sc->ep_intrhand))) { + device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); + goto bad; + } + + return (0); +bad: + ep_free(dev); + return (error); +} + +static int +ep_eeprom_cksum (sc) + struct ep_softc * sc; +{ + int i; + int error; + u_int16_t val; + u_int16_t cksum; + u_int8_t cksum_high = 0; + u_int8_t cksum_low = 0; + + error = get_e(sc, 0x0f, &val); + if (error) + return (ENXIO); + cksum = val; + + for (i = 0; i < 0x0f; i++) { + error = get_e(sc, i, &val); + if (error) + return (ENXIO); + switch (i) { + case 0x08: + case 0x09: + case 0x0d: + cksum_low ^= (u_int8_t)(val & 0x00ff) ^ + (u_int8_t)((val & 0xff00) >> 8); + break; + default: + cksum_high ^= (u_int8_t)(val & 0x00ff) ^ + (u_int8_t)((val & 0xff00) >> 8); + break; + } + } + return (cksum != ((u_int16_t)cksum_low | (u_int16_t)(cksum_high << 8))); +} + +static device_method_t ep_isa_methods[] = { + /* Device interface */ +#ifdef __i386__ + DEVMETHOD(device_identify, ep_isa_identify), +#endif + DEVMETHOD(device_probe, ep_isa_probe), + DEVMETHOD(device_attach, ep_isa_attach), + DEVMETHOD(device_detach, ep_detach), + + { 0, 0 } +}; + +static driver_t ep_isa_driver = { + "ep", + ep_isa_methods, + sizeof(struct ep_softc), +}; + +extern devclass_t ep_devclass; + +DRIVER_MODULE(ep, isa, ep_isa_driver, ep_devclass, 0, 0); +#ifdef __i386__ +MODULE_DEPEND(ep, elink, 1, 1, 1); +#endif diff --git a/sys/dev/ep/if_ep_mca.c b/sys/dev/ep/if_ep_mca.c new file mode 100644 index 0000000..b3a0506 --- /dev/null +++ b/sys/dev/ep/if_ep_mca.c @@ -0,0 +1,165 @@ +/*- + * Copyright (c) 1999 Matthew N. Dodd <winter@jurai.net> + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> +#include <sys/rman.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + +#include <dev/mca/mca_busreg.h> +#include <dev/mca/mca_busvar.h> + +#include <dev/ep/if_epreg.h> +#include <dev/ep/if_epvar.h> + +#define EP_MCA_627C 0x627C +#define EP_MCA_627D 0x627D +#define EP_MCA_62DB 0x62db +#define EP_MCA_62F6 0x62f6 +#define EP_MCA_62F7 0x62f7 + +static struct mca_ident ep_mca_devs[] = { + { EP_MCA_627C, "3Com 3C529 Network Adapter" }, + { EP_MCA_627D, "3Com 3C529-TP Network Adapter" }, + + /* + * These are from the linux 3c509 driver. + * I have not seen the ADFs for them and have + * not tested or even seen the hardware. + * Someone with the ADFs should replace the names with + * whatever is in the AdapterName field of the ADF. + * (and fix the media setup for the cards as well.) + */ + { EP_MCA_62DB, "3Com 3c529 EtherLink III (test mode)" }, + { EP_MCA_62F6, "3Com 3c529 EtherLink III (TP or coax)" }, + { EP_MCA_62F7, "3Com 3c529 EtherLink III (TP)" }, + + { 0, NULL }, +}; + +#define EP_MCA_IOPORT_POS MCA_ADP_POS(MCA_POS2) +#define EP_MCA_IOPORT_MASK 0xfc +#define EP_MCA_IOPORT_SIZE EP_IOSIZE +#define EP_MCA_IOPORT(pos) ((((u_int32_t)pos & EP_MCA_IOPORT_MASK) \ + | 0x02) << 8) + +#define EP_MCA_IRQ_POS MCA_ADP_POS(MCA_POS3) +#define EP_MCA_IRQ_MASK 0x0f +#define EP_MCA_IRQ(pos) (pos & EP_MCA_IRQ_MASK) + +#define EP_MCA_MEDIA_POS MCA_ADP_POS(MCA_POS2) +#define EP_MCA_MEDIA_MASK 0x03 +#define EP_MCA_MEDIA(pos) (pos & EP_MCA_MEDIA_MASK) + +static int +ep_mca_probe (device_t dev) +{ + const char * desc; + u_int32_t iobase = 0; + u_int8_t irq = 0; + u_int8_t pos; + + desc = mca_match_id(mca_get_id(dev), ep_mca_devs); + if (!desc) + return (ENXIO); + device_set_desc(dev, desc); + + pos = mca_pos_read(dev, EP_MCA_IOPORT_POS); + iobase = EP_MCA_IOPORT(pos); + + pos = mca_pos_read(dev, EP_MCA_IRQ_POS); + irq = EP_MCA_IRQ(pos); + + mca_add_iospace(dev, iobase, EP_MCA_IOPORT_SIZE); + mca_add_irq(dev, irq); + + return (0); +} + +static int +ep_mca_attach (device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + int error = 0; + + if ((error = ep_alloc(dev))) { + device_printf(dev, "ep_alloc() failed! (%d)\n", error); + goto bad; + } + sc->stat = F_ACCESS_32_BITS; + + ep_get_media(sc); + + GO_WINDOW(0); + SET_IRQ(BASE, rman_get_start(sc->irq)); + + if ((error = ep_attach(sc))) { + device_printf(dev, "ep_attach() failed! (%d)\n", error); + goto bad; + } + + if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ep_intr, + sc, &sc->ep_intrhand))) { + device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); + goto bad; + } + + return (0); +bad: + ep_free(dev); + return (error); +} + +static device_method_t ep_mca_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, ep_mca_probe), + DEVMETHOD(device_attach, ep_mca_attach), + DEVMETHOD(device_detach, ep_detach), + + { 0, 0 } +}; + +static driver_t ep_mca_driver = { + "ep", + ep_mca_methods, + sizeof(struct ep_softc), +}; + +static devclass_t ep_devclass; + +DRIVER_MODULE(ep, mca, ep_mca_driver, ep_devclass, 0, 0); diff --git a/sys/dev/ep/if_ep_pccard.c b/sys/dev/ep/if_ep_pccard.c new file mode 100644 index 0000000..5a3e70a --- /dev/null +++ b/sys/dev/ep/if_ep_pccard.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 1994 Herb Peyerl <hpeyerl@novatel.ca> + * 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. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Herb Peyerl. + * 4. The name of Herb Peyerl 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. + * + * $FreeBSD$ + */ + +/* + * Pccard support for 3C589 by: + * HAMADA Naoki + * nao@tom-yam.or.jp + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/socket.h> + +#include <sys/module.h> +#include <sys/bus.h> + +#include <machine/bus.h> +#include <machine/resource.h> + +#include <net/ethernet.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <net/if_media.h> + + +#include <dev/ep/if_epreg.h> +#include <dev/ep/if_epvar.h> + +#include <dev/pccard/pccardvar.h> +#include <dev/pccard/pccarddevs.h> +#include "card_if.h" + +static const char *ep_pccard_identify(u_short id); + +/* + * Initialize the device - called from Slot manager. + */ +static int +ep_pccard_probe(device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + struct ep_board * epb = &sc->epb; + const char * desc; + u_int16_t result; + int error; + + error = ep_alloc(dev); + if (error) + return error; + + /* + * XXX - Certain (newer?) 3Com cards need epb->cmd_off == + * 2. Sadly, you need to have a correct cmd_off in order to + * identify the card. So we have to hit it with both and + * cross our virtual fingers. There's got to be a better way + * to do this. jyoung@accessus.net 09/11/1999 + */ + + epb->cmd_off = 0; + + error = get_e(sc, EEPROM_PROD_ID, &result); /* XXX check return */ + epb->prod_id = result; + + if ((desc = ep_pccard_identify(epb->prod_id)) == NULL) { + if (bootverbose) + device_printf(dev, "Pass 1 of 2 detection " + "failed (nonfatal) id 0x%x\n", epb->prod_id); + epb->cmd_off = 2; + error = get_e(sc, EEPROM_PROD_ID, &result); /* XXX check return */ + epb->prod_id = result; + if ((desc = ep_pccard_identify(epb->prod_id)) == NULL) { + device_printf(dev, "Unit failed to come ready or " + "product ID unknown! (id 0x%x)\n", epb->prod_id); + ep_free(dev); + return (ENXIO); + } + } + device_set_desc(dev, desc); + + /* + * For some reason the 3c574 needs this. + */ + error = ep_get_macaddr(sc, (u_char *)&sc->arpcom.ac_enaddr); + + ep_free(dev); + return (0); +} + +static const char * +ep_pccard_identify(u_short id) +{ + /* Determine device type and associated MII capabilities */ + switch (id) { + case 0x6055: /* 3C556 */ + return ("3Com 3C556"); + case 0x4057: /* 3C574 */ + return ("3Com 3C574"); + case 0x4b57: /* 3C574B */ + return ("3Com 3C574B, Megahertz 3CCFE574BT or " + "Fast Etherlink 3C574-TX"); + case 0x2b57: /* 3CXSH572BT */ + return ("3Com OfficeConnect 572BT"); + case 0x9058: /* 3C589 */ + return ("3Com Etherlink III 3C589"); + case 0x2056: /* 3C562/3C563 */ + return ("3Com 3C562D/3C563D"); + case 0x0010: /* 3C1 */ + return ("3Com Megahertz C1"); + } + return (NULL); +} + +static int +ep_pccard_card_attach(struct ep_board *epb) +{ + /* Determine device type and associated MII capabilities */ + switch (epb->prod_id) { + case 0x6055: /* 3C556 */ + case 0x2b57: /* 3C572BT */ + case 0x4057: /* 3C574 */ + case 0x4b57: /* 3C574B */ + case 0x0010: /* 3C1 */ + epb->mii_trans = 1; + return (1); + case 0x2056: /* 3C562D/3C563D */ + case 0x9058: /* 3C589 */ + epb->mii_trans = 0; + return (1); + } + return (0); +} + +static int +ep_pccard_attach(device_t dev) +{ + struct ep_softc * sc = device_get_softc(dev); + u_int16_t result; + int error = 0; + + if ((error = ep_alloc(dev))) { + device_printf(dev, "ep_alloc() failed! (%d)\n", error); + goto bad; + } + + sc->epb.cmd_off = 0; + + error = get_e(sc, EEPROM_PROD_ID, &result); /* XXX check return */ + sc->epb.prod_id = result; + + if (!ep_pccard_card_attach(&sc->epb)) { + sc->epb.cmd_off = 2; + error = get_e(sc, EEPROM_PROD_ID, &result); + sc->epb.prod_id = result; + error = get_e(sc, EEPROM_RESOURCE_CFG, &result); + sc->epb.res_cfg = result; + if (!ep_pccard_card_attach(&sc->epb)) { + device_printf(dev, + "Probe found ID, attach failed so ignore card!\n"); + error = ENXIO; + goto bad; + } + } + + error = get_e(sc, EEPROM_ADDR_CFG, &result); + + /* ROM size = 0, ROM base = 0 */ + /* For now, ignore AUTO SELECT feature of 3C589B and later. */ + outw(BASE + EP_W0_ADDRESS_CFG, result & 0xc000); + + /* Fake IRQ must be 3 */ + outw(BASE + EP_W0_RESOURCE_CFG, (sc->epb.res_cfg & 0x0fff) | 0x3000); + + outw(BASE + EP_W0_PRODUCT_ID, sc->epb.prod_id); + + if (sc->epb.mii_trans) { + /* + * turn on the MII transciever + */ + GO_WINDOW(3); + outw(BASE + EP_W3_OPTIONS, 0x8040); + DELAY(1000); + outw(BASE + EP_W3_OPTIONS, 0xc040); + outw(BASE + EP_COMMAND, RX_RESET); + outw(BASE + EP_COMMAND, TX_RESET); + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS); + DELAY(1000); + outw(BASE + EP_W3_OPTIONS, 0x8040); + } else { + ep_get_media(sc); + } + + if ((error = ep_attach(sc))) { + device_printf(dev, "ep_attach() failed! (%d)\n", error); + goto bad; + } + + if ((error = bus_setup_intr(dev, sc->irq, INTR_TYPE_NET, ep_intr, + sc, &sc->ep_intrhand))) { + device_printf(dev, "bus_setup_intr() failed! (%d)\n", error); + goto bad; + } + + return (0); +bad: + ep_free(dev); + return (error); +} + +static const struct pccard_product ep_pccard_products[] = { + PCMCIA_CARD(3COM, 3C1, 0), + PCMCIA_CARD(3COM, 3C562, 0), + PCMCIA_CARD(3COM, 3C574, 0), /* ROADRUNNER */ + PCMCIA_CARD(3COM, 3C589, 0), + PCMCIA_CARD(3COM, 3CCFEM556BI, 0), /* ROADRUNNER */ + PCMCIA_CARD(3COM, 3CXEM556, 0), + PCMCIA_CARD(3COM, 3CXEM556INT, 0), + { NULL } +}; + +static int +ep_pccard_match(device_t dev) +{ + const struct pccard_product *pp; + + if ((pp = pccard_product_lookup(dev, ep_pccard_products, + sizeof(ep_pccard_products[0]), NULL)) != NULL) { + if (pp->pp_name != NULL) + device_set_desc(dev, pp->pp_name); + return 0; + } + return EIO; +} + +static device_method_t ep_pccard_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, pccard_compat_probe), + DEVMETHOD(device_attach, pccard_compat_attach), + DEVMETHOD(device_detach, ep_detach), + + /* Card interface */ + DEVMETHOD(card_compat_match, ep_pccard_match), + DEVMETHOD(card_compat_probe, ep_pccard_probe), + DEVMETHOD(card_compat_attach, ep_pccard_attach), + + { 0, 0 } +}; + +static driver_t ep_pccard_driver = { + "ep", + ep_pccard_methods, + sizeof(struct ep_softc), +}; + +extern devclass_t ep_devclass; + +DRIVER_MODULE(ep, pccard, ep_pccard_driver, ep_devclass, 0, 0); diff --git a/sys/dev/ep/if_epreg.h b/sys/dev/ep/if_epreg.h new file mode 100644 index 0000000..9fb95f5 --- /dev/null +++ b/sys/dev/ep/if_epreg.h @@ -0,0 +1,434 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. + * + * $FreeBSD$ + */ + +/* + * DELAY_MULTIPLE: How much to boost "base" delays, except + * for the inter-bit delays in get_eeprom_data. A cyrix Media GX needed this. + */ +#define DELAY_MULTIPLE 10 +#define BIT_DELAY_MULTIPLE 10 + +/* + * Some global constants + */ +#define TX_INIT_RATE 16 +#define TX_INIT_MAX_RATE 64 +#define RX_INIT_LATENCY 64 +#define RX_INIT_EARLY_THRESH 208 /* not less than MINCLSIZE */ +#define RX_NEXT_EARLY_THRESH 500 + +#define EEPROMSIZE 0x40 +#define MAX_EEPROMBUSY 1000 +#define EP_LAST_TAG 0xd7 +#define EP_MAX_BOARDS 16 +/* + * This `ID' port is a mere hack. There's currently no chance to register + * it with config's idea of the ports that are in use. + * + * "After the automatic configuration is completed, the IDS is in its initial + * state (ID-WAIT), and it monitors all write access to I/O port 01x0h, where + * 'x' is any hex digit. If a zero is written to any one of these ports, then + * that address is remembered and becomes the ID port. A second zero written + * to that port resets the ID sequence to its initial state. The IDS watches + * for the ID sequence to be written to the ID port." + * + * We prefer 0x110 over 0x100 so to not conflict with the Plaque&Pray + * ports. + */ +#define EP_ID_PORT 0x110 +#define EP_IOSIZE 16 /* 16 bytes of I/O space used. */ + +/* + * some macros to acces long named fields + */ +#define BASE (sc->ep_io_addr) + +/* + * Commands to read/write EEPROM trough EEPROM command register (Window 0, + * Offset 0xa) + */ +#define EEPROM_CMD_RD 0x0080 /* Read: Address required (5 bits) */ +#define EEPROM_CMD_WR 0x0040 /* Write: Address required (5 bits) */ +#define EEPROM_CMD_ERASE 0x00c0 /* Erase: Address required (5 bits) */ +#define EEPROM_CMD_EWEN 0x0030 /* Erase/Write Enable: No data required */ + +#define EEPROM_BUSY (1<<15) +#define EEPROM_TST_MODE (1<<14) + +/* + * Some short functions, worth to let them be a macro + */ +#define is_eeprom_busy(b) (inw((b)+EP_W0_EEPROM_COMMAND)&EEPROM_BUSY) +#define GO_WINDOW(x) outw(BASE+EP_COMMAND, WINDOW_SELECT|(x)) + +/************************************************************************** + * * + * These define the EEPROM data structure. They are used in the probe + * function to verify the existence of the adapter after having sent + * the ID_Sequence. + * + **************************************************************************/ + +#define EEPROM_NODE_ADDR_0 0x0 /* Word */ +#define EEPROM_NODE_ADDR_1 0x1 /* Word */ +#define EEPROM_NODE_ADDR_2 0x2 /* Word */ +#define EEPROM_PROD_ID 0x3 /* 0x9[0-f]50 */ +#define EEPROM_MFG_DATE 0x4 /* Manufacturing date */ +#define EEPROM_MFG_DIVSION 0x5 /* Manufacturing division */ +#define EEPROM_MFG_PRODUCT 0x6 /* Product code */ +#define EEPROM_MFG_ID 0x7 /* 0x6d50 */ +#define EEPROM_ADDR_CFG 0x8 /* Base addr */ +# define ADDR_CFG_EISA 0x1f +# define ADDR_CFG_MASK 0x1f +#define EEPROM_RESOURCE_CFG 0x9 /* IRQ. Bits 12-15 */ +#define EEPROM_OEM_ADDR0 0xa +#define EEPROM_OEM_ADDR1 0xb +#define EEPROM_OEM_ADDR2 0xc +#define EEPROM_SOFTINFO 0xd +#define EEPROM_COMPAT 0xe +#define EEPROM_SOFTINFO2 0xf +#define EEPROM_CAP 0x10 +# define CAP_ISA 0x2083 +# define CAP_PCMCIA 0x2082 +#define EEPROM_INT_CONFIG_0 0x12 +#define EEPROM_INT_CONFIG_1 0x13 +/* RAM Partition TX FIFO/RX FIFO */ +# define ICW1_RAM_PART_MASK 0x03 +# define ICW1_RAM_PART_35 0x00 /* 2:5 (only legal if RAM size == 000b default power-up/reset */ +# define ICW1_RAM_PART_13 0x01 /* 1:3 (only legal if RAM size == 000b) */ +# define ICW1_RAM_PART_11 0x10 /* 1:1 */ +# define ICW1_RAM_PART_RESV 0x11 /* Reserved */ +/* ISA Adapter Selection */ +# define ICW1_IAS_MASK 0x0c +# define ICW1_IAS_DIS 0x00 /* Both mechanisms disabled (default) */ +# define ICW1_IAS_ISA 0x04 /* ISA contention only */ +# define ICW1_IAS_PNP 0x08 /* ISA Plug and Play only */ +# define ICW1_IAS_BOTH 0x0c /* Both mechanisms enabled */ + +#define EEPROM_CHECKSUM_EL3 0x17 + +/************************************************************************** + * * + * These are the registers for the 3Com 3c509 and their bit patterns when * + * applicable. They have been taken out the the "EtherLink III Parallel * + * Tasking EISA and ISA Technical Reference" "Beta Draft 10/30/92" manual * + * from 3com. * + * * + **************************************************************************/ + +#define EP_COMMAND 0x0e /* Write. BASE+0x0e is always a + * command reg. */ +#define EP_STATUS 0x0e /* Read. BASE+0x0e is always status + * reg. */ +#define EP_WINDOW 0x0f /* Read. BASE+0x0f is always window + * reg. */ +/* + * Window 0 registers. Setup. + */ +/* Write */ +#define EP_W0_EEPROM_DATA 0x0c +#define EP_W0_EEPROM_COMMAND 0x0a +#define EP_W0_RESOURCE_CFG 0x08 +#define EP_W0_ADDRESS_CFG 0x06 +#define EP_W0_CONFIG_CTRL 0x04 +/* Read */ +#define EP_W0_PRODUCT_ID 0x02 +#define EP_W0_MFG_ID 0x00 + +/* + * Window 1 registers. Operating Set. + */ +/* Write */ +#define EP_W1_TX_PIO_WR_2 0x02 +#define EP_W1_TX_PIO_WR_1 0x00 +/* Read */ +#define EP_W1_FREE_TX 0x0c +#define EP_W1_TX_STATUS 0x0b /* byte */ +#define EP_W1_TIMER 0x0a /* byte */ +#define EP_W1_RX_STATUS 0x08 +#define EP_W1_RX_PIO_RD_2 0x02 +#define EP_W1_RX_PIO_RD_1 0x00 + +/* + * Window 2 registers. Station Address Setup/Read + */ +/* Read/Write */ +#define EP_W2_ADDR_5 0x05 +#define EP_W2_ADDR_4 0x04 +#define EP_W2_ADDR_3 0x03 +#define EP_W2_ADDR_2 0x02 +#define EP_W2_ADDR_1 0x01 +#define EP_W2_ADDR_0 0x00 + +/* + * Window 3 registers. FIFO Management. + */ +/* Read */ +#define EP_W3_FREE_TX 0x0c +#define EP_W3_FREE_RX 0x0a +#define EP_W3_OPTIONS 0x08 + +/* + * Window 4 registers. Diagnostics. + */ +/* Read/Write */ +#define EP_W4_MEDIA_TYPE 0x0a +#define EP_W4_CTRLR_STATUS 0x08 +#define EP_W4_NET_DIAG 0x06 +#define EP_W4_FIFO_DIAG 0x04 +#define EP_W4_HOST_DIAG 0x02 +#define EP_W4_TX_DIAG 0x00 + +/* + * Window 5 Registers. Results and Internal status. + */ +/* Read */ +#define EP_W5_READ_0_MASK 0x0c +#define EP_W5_INTR_MASK 0x0a +#define EP_W5_RX_FILTER 0x08 +#define EP_W5_RX_EARLY_THRESH 0x06 +#define EP_W5_TX_AVAIL_THRESH 0x02 +#define EP_W5_TX_START_THRESH 0x00 + +/* + * Window 6 registers. Statistics. + */ +/* Read/Write */ +#define TX_TOTAL_OK 0x0c +#define RX_TOTAL_OK 0x0a +#define TX_DEFERRALS 0x08 +#define RX_FRAMES_OK 0x07 +#define TX_FRAMES_OK 0x06 +#define RX_OVERRUNS 0x05 +#define TX_COLLISIONS 0x04 +#define TX_AFTER_1_COLLISION 0x03 +#define TX_AFTER_X_COLLISIONS 0x02 +#define TX_NO_SQE 0x01 +#define TX_CD_LOST 0x00 + +/**************************************** + * + * Register definitions. + * + ****************************************/ + +/* + * Command parameter that disables threshold interrupts + * PIO (3c509) cards use 2044. The fifo word-oriented and 2044--2047 work. + * "busmastering" cards need 8188. + * The implicit two-bit upshift done by busmastering cards means + * a value of 2047 disables threshold interrupts on both. + */ +#define EP_THRESH_DISABLE 2047 + +/* + * Command register. All windows. + * + * 16 bit register. + * 15-11: 5-bit code for command to be executed. + * 10-0: 11-bit arg if any. For commands with no args; + * this can be set to anything. + */ +#define GLOBAL_RESET (u_short) 0x0000 /* Wait at least 1ms + * after issuing */ +#define WINDOW_SELECT (u_short) (0x1<<11) +#define START_TRANSCEIVER (u_short) (0x2<<11) /* Read ADDR_CFG reg to + * determine whether + * this is needed. If + * so; wait 800 uSec + * before using trans- + * ceiver. */ +#define RX_DISABLE (u_short) (0x3<<11) /* state disabled on + * power-up */ +#define RX_ENABLE (u_short) (0x4<<11) +#define RX_RESET (u_short) (0x5<<11) +#define RX_DISCARD_TOP_PACK (u_short) (0x8<<11) +#define TX_ENABLE (u_short) (0x9<<11) +#define TX_DISABLE (u_short) (0xa<<11) +#define TX_RESET (u_short) (0xb<<11) +#define REQ_INTR (u_short) (0xc<<11) +#define SET_INTR_MASK (u_short) (0xe<<11) +#define SET_RD_0_MASK (u_short) (0xf<<11) +#define SET_RX_FILTER (u_short) (0x10<<11) +#define FIL_INDIVIDUAL (u_short) (0x1) +#define FIL_GROUP (u_short) (0x2) +#define FIL_BRDCST (u_short) (0x4) +#define FIL_ALL (u_short) (0x8) +#define SET_RX_EARLY_THRESH (u_short) (0x11<<11) +#define SET_TX_AVAIL_THRESH (u_short) (0x12<<11) +#define SET_TX_START_THRESH (u_short) (0x13<<11) +#define STATS_ENABLE (u_short) (0x15<<11) +#define STATS_DISABLE (u_short) (0x16<<11) +#define STOP_TRANSCEIVER (u_short) (0x17<<11) +/* + * The following C_* acknowledge the various interrupts. Some of them don't + * do anything. See the manual. + */ +#define ACK_INTR (u_short) (0x6800) +#define C_INTR_LATCH (u_short) (ACK_INTR|0x1) +#define C_CARD_FAILURE (u_short) (ACK_INTR|0x2) +#define C_TX_COMPLETE (u_short) (ACK_INTR|0x4) +#define C_TX_AVAIL (u_short) (ACK_INTR|0x8) +#define C_RX_COMPLETE (u_short) (ACK_INTR|0x10) +#define C_RX_EARLY (u_short) (ACK_INTR|0x20) +#define C_INT_RQD (u_short) (ACK_INTR|0x40) +#define C_UPD_STATS (u_short) (ACK_INTR|0x80) +#define C_MASK (u_short) 0xFF /* mask of C_* */ + +/* + * Status register. All windows. + * + * 15-13: Window number(0-7). + * 12: Command_in_progress. + * 11: reserved. + * 10: reserved. + * 9: reserved. + * 8: reserved. + * 7: Update Statistics. + * 6: Interrupt Requested. + * 5: RX Early. + * 4: RX Complete. + * 3: TX Available. + * 2: TX Complete. + * 1: Adapter Failure. + * 0: Interrupt Latch. + */ +#define S_INTR_LATCH (u_short) (0x1) +#define S_CARD_FAILURE (u_short) (0x2) +#define S_TX_COMPLETE (u_short) (0x4) +#define S_TX_AVAIL (u_short) (0x8) +#define S_RX_COMPLETE (u_short) (0x10) +#define S_RX_EARLY (u_short) (0x20) +#define S_INT_RQD (u_short) (0x40) +#define S_UPD_STATS (u_short) (0x80) +#define S_MASK (u_short) 0xFF /* mask of S_* */ +#define S_5_INTS (S_CARD_FAILURE|S_TX_COMPLETE|\ + S_TX_AVAIL|S_RX_COMPLETE|S_RX_EARLY) +#define S_COMMAND_IN_PROGRESS (u_short) (0x1000) + +/* Address Config. Register. + * Window 0/Port 06 + */ + +#define ACF_CONNECTOR_BITS 14 +#define ACF_CONNECTOR_UTP 0 +#define ACF_CONNECTOR_AUI 1 +#define ACF_CONNECTOR_BNC 3 + +/* Resource configuration register. + * Window 0/Port 08 + * + */ + +#define SET_IRQ(base,irq) outw((base) + EP_W0_RESOURCE_CFG, \ + ((inw((base) + EP_W0_RESOURCE_CFG) & 0x0fff) | \ + ((u_short)(irq)<<12)) ) /* set IRQ i */ + +/* + * FIFO Registers. + * RX Status. Window 1/Port 08 + * + * 15: Incomplete or FIFO empty. + * 14: 1: Error in RX Packet 0: Incomplete or no error. + * 13-11: Type of error. + * 1000 = Overrun. + * 1011 = Run Packet Error. + * 1100 = Alignment Error. + * 1101 = CRC Error. + * 1001 = Oversize Packet Error (>1514 bytes) + * 0010 = Dribble Bits. + * (all other error codes, no errors.) + * + * 10-0: RX Bytes (0-1514) + */ +#define ERR_RX_INCOMPLETE (u_short) (0x1<<15) +#define ERR_RX (u_short) (0x1<<14) +#define ERR_RX_OVERRUN (u_short) (0x8<<11) +#define ERR_RX_RUN_PKT (u_short) (0xb<<11) +#define ERR_RX_ALIGN (u_short) (0xc<<11) +#define ERR_RX_CRC (u_short) (0xd<<11) +#define ERR_RX_OVERSIZE (u_short) (0x9<<11) +#define ERR_RX_DRIBBLE (u_short) (0x2<<11) + +/* + * FIFO Registers. + * TX Status. Window 1/Port 0B + * + * Reports the transmit status of a completed transmission. Writing this + * register pops the transmit completion stack. + * + * Window 1/Port 0x0b. + * + * 7: Complete + * 6: Interrupt on successful transmission requested. + * 5: Jabber Error (TP Only, TX Reset required. ) + * 4: Underrun (TX Reset required. ) + * 3: Maximum Collisions. + * 2: TX Status Overflow. + * 1-0: Undefined. + * + */ +#define TXS_COMPLETE 0x80 +#define TXS_SUCCES_INTR_REQ 0x40 +#define TXS_JABBER 0x20 +#define TXS_UNDERRUN 0x10 +#define TXS_MAX_COLLISION 0x8 +#define TXS_STATUS_OVERFLOW 0x4 + +/* + * Configuration control register. + * Window 0/Port 04 + */ +/* Read */ +#define IS_AUI (1<<13) +#define IS_BNC (1<<12) +#define IS_UTP (1<<9) +/* Write */ +#define ENABLE_DRQ_IRQ 0x0001 +#define W0_P4_CMD_RESET_ADAPTER 0x4 +#define W0_P4_CMD_ENABLE_ADAPTER 0x1 +/* + * Media type and status. + * Window 4/Port 0A + */ +#define ENABLE_UTP 0xc0 +#define DISABLE_UTP 0x0 + +/* + * Misc defines for various things. + */ +#define ACTIVATE_ADAPTER_TO_CONFIG 0xff /* to the id_port */ +#define MFG_ID 0x6d50 /* in EEPROM and W0 ADDR_CONFIG */ +#define PROD_ID 0x9150 + +#define AUI 0x1 +#define BNC 0x2 +#define UTP 0x4 + +#define RX_BYTES_MASK (u_short) (0x07ff) + +/* + * Config flags + */ +#define EP_FLAGS_100TX 0x1 diff --git a/sys/dev/ep/if_epvar.h b/sys/dev/ep/if_epvar.h new file mode 100644 index 0000000..b9d4107 --- /dev/null +++ b/sys/dev/ep/if_epvar.h @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) 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. + * + * $FreeBSD$ + */ + +struct ep_board { + u_short prod_id; /* product ID */ + int cmd_off; /* command offset (bit shift) */ + int mii_trans; /* activate MII transiever */ + u_short res_cfg; /* resource configuration */ +}; + +/* + * Ethernet software status per interface. + */ +struct ep_softc { + struct arpcom arpcom; /* Ethernet common part */ + struct ifmedia ifmedia; /* media info */ + + device_t dev; + + struct resource * iobase; + struct resource * irq; + + bus_space_handle_t ep_bhandle; + bus_space_tag_t ep_btag; + void * ep_intrhand; + + int ep_io_addr; /* i/o bus address */ + + u_short ep_connectors; /* Connectors on this card. */ + u_char ep_connector; /* Configured connector.*/ + + struct mbuf * top; + struct mbuf * mcur; + short cur_len; + + int stat; /* some flags */ +#define F_RX_FIRST 0x001 +#define F_PROMISC 0x008 +#define F_ACCESS_32_BITS 0x100 + + int gone; /* adapter is not present (for PCCARD) */ + + struct ep_board epb; + + int unit; + +#ifdef EP_LOCAL_STATS + short tx_underrun; + short rx_no_first; + short rx_no_mbuf; + short rx_overrunf; + short rx_overrunl; +#endif +}; + +int ep_alloc (device_t); +void ep_free (device_t); +int ep_detach (device_t); +void ep_get_media (struct ep_softc *); +int ep_attach (struct ep_softc *); +void ep_intr (void *); +int get_e (struct ep_softc *, u_int16_t, u_int16_t *); +int ep_get_macaddr (struct ep_softc *, u_char *); |