diff options
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/sparc/dev/hme.c | 1492 | ||||
-rw-r--r-- | sys/arch/sparc/dev/hmereg.h | 582 | ||||
-rw-r--r-- | sys/arch/sparc/dev/hmevar.h | 68 |
3 files changed, 2142 insertions, 0 deletions
diff --git a/sys/arch/sparc/dev/hme.c b/sys/arch/sparc/dev/hme.c new file mode 100644 index 00000000000..9df40e65026 --- /dev/null +++ b/sys/arch/sparc/dev/hme.c @@ -0,0 +1,1492 @@ +/* $OpenBSD: hme.c,v 1.1 1998/07/10 19:09:08 jason Exp $ */ + +/* + * Copyright (c) 1998 Jason L. Wright (jason@thought.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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. 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. + */ + +/* + * Driver for the Happy Meal (hme) ethernet boards + * Based on the driver from S/Linux by David Miller + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/syslog.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/netisr.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 + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include <net/bpf.h> +#include <net/bpfdesc.h> +#endif + +#include <machine/autoconf.h> +#include <sparc/cpu.h> +#include <sparc/sparc/cpuvar.h> +#include <sparc/dev/sbusvar.h> +#include <sparc/dev/dmareg.h> /* for SBUS_BURST_* */ +#include <sparc/dev/hmereg.h> +#include <sparc/dev/hmevar.h> + +int hmematch __P((struct device *, void *, void *)); +void hmeattach __P((struct device *, struct device *, void *)); +void hmewatchdog __P((struct ifnet *)); +int hmeintr __P((void *)); +int hmeioctl __P((struct ifnet *, u_long, caddr_t)); +void hmereset __P((struct hme_softc *sc)); +void hmestart __P((struct ifnet *)); +void hmestop __P((struct hme_softc *sc)); +void hmeinit __P((struct hme_softc *sc)); + +static void hme_tcvr_write __P((struct hme_softc *sc, int reg, + u_short val)); +static int hme_tcvr_read __P((struct hme_softc *sc, int reg)); +static void hme_tcvr_bb_write __P((struct hme_softc *sc, int reg, + u_short val)); +static int hme_tcvr_bb_read __P((struct hme_softc *sc, int reg)); +static void hme_tcvr_bb_writeb __P((struct hme_softc *sc, int b)); +static int hme_tcvr_bb_readb __P((struct hme_softc *sc)); +static void hme_tcvr_check __P((struct hme_softc *sc)); +static int hme_tcvr_reset __P((struct hme_softc *sc)); + +static void hme_poll_stop __P((struct hme_softc *sc)); + +static int hme_mint __P((struct hme_softc *, u_int32_t)); +static int hme_tint __P((struct hme_softc *, u_int32_t)); +static int hme_rint __P((struct hme_softc *, u_int32_t)); +static int hme_eint __P((struct hme_softc *, u_int32_t)); + +static void hme_auto_negotiate __P((struct hme_softc *sc)); +static void hme_manual_negotiate __P((struct hme_softc *sc)); +static void hme_negotiate_watchdog __P((void *arg)); +static void hme_print_link_mode __P((struct hme_softc *sc)); +static void hme_set_initial_advertisement __P((struct hme_softc *sc)); + +static void hme_reset_rx __P((struct hme_softc *sc)); +static void hme_reset_tx __P((struct hme_softc *sc)); +static void hme_meminit __P((struct hme_softc *sc)); + +static int hme_put __P((struct hme_softc *sc, int idx, struct mbuf *m)); + +static struct mbuf *hme_get __P((struct hme_softc *sc, int idx, int len)); +static void hme_read __P((struct hme_softc *sc, int idx, int len)); + +struct cfattach hme_ca = { + sizeof (struct hme_softc), hmematch, hmeattach +}; + +struct cfdriver hme_cd = { + NULL, "hme", DV_IFNET +}; + +int +hmematch(parent, vcf, aux) + struct device *parent; + void *vcf, *aux; +{ + struct cfdata *cf = vcf; + struct confargs *ca = aux; + register struct romaux *ra = &ca->ca_ra; + + if (strcmp(cf->cf_driver->cd_name, ra->ra_name) && + strcmp("SUNW,hme", ra->ra_name)) { + return (0); + } + return (1); +} + +void +hmeattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + struct hme_softc *sc = (struct hme_softc *)self; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int pri; + +#if 0 + /* + * XXX We should check the prom env var 'local-mac-address?' + * XXX If true, use the MAC address specific to the board, and if + * XXX false, use the MAC address specific to the system. + */ + + /* XXX the following declaration should be elsewhere */ + extern void myetheraddr __P((u_char *)); +#endif + + + if (ca->ca_ra.ra_nintr != 1) { + printf(": expected 1 interrupt, got %d\n", ca->ca_ra.ra_nintr); + return; + } + pri = ca->ca_ra.ra_intr[0].int_pri; + + /* map registers */ + if (ca->ca_ra.ra_nreg != 5) { + printf(": expected 5 registers, got %d\n", ca->ca_ra.ra_nreg); + return; + } + sc->sc_gr = mapiodev(&(ca->ca_ra.ra_reg[0]), 0, + ca->ca_ra.ra_reg[0].rr_len); + sc->sc_txr = mapiodev(&(ca->ca_ra.ra_reg[1]), 0, + ca->ca_ra.ra_reg[1].rr_len); + sc->sc_rxr = mapiodev(&(ca->ca_ra.ra_reg[2]), 0, + ca->ca_ra.ra_reg[2].rr_len); + sc->sc_cr = mapiodev(&(ca->ca_ra.ra_reg[3]), 0, + ca->ca_ra.ra_reg[3].rr_len); + sc->sc_tcvr = mapiodev(&(ca->ca_ra.ra_reg[4]), 0, + ca->ca_ra.ra_reg[4].rr_len); + + sc->sc_node = ca->ca_ra.ra_node; + + sc->sc_rev = getpropint(ca->ca_ra.ra_node, "hm-rev", -1); + if (sc->sc_rev == 0xff) + sc->sc_rev = 0xa0; + if (sc->sc_rev == 0x20 || sc->sc_rev == 0x21) + sc->sc_flags = HME_FLAG_20_21; + else if (sc->sc_rev != 0xa0) + sc->sc_flags = HME_FLAG_NOT_A0; + + sc->sc_burst = ((struct sbus_softc *)parent)->sc_burst; + + sc->sc_desc_dva = (struct hme_desc *) dvma_malloc( + sizeof(struct hme_desc), &(sc->sc_desc), M_NOWAIT); + sc->sc_bufs_dva = (struct hme_bufs *) dvma_malloc( + sizeof(struct hme_bufs) + RX_ALIGN_SIZE, &(sc->sc_bufs), + M_NOWAIT); /* XXX must be aligned on 64 byte boundary */ + + hme_set_initial_advertisement(sc); + + sbus_establish(&sc->sc_sd, &sc->sc_dev); + + sc->sc_ih.ih_fun = hmeintr; + sc->sc_ih.ih_arg = sc; + intr_establish(ca->ca_ra.ra_intr[0].int_pri, &sc->sc_ih); + +#if 1 + /* + * XXX Below should only be used if 'local-mac-address?' == true + */ + getprop(ca->ca_ra.ra_node, "local-mac-address", + sc->sc_arpcom.ac_enaddr, + sizeof(sc->sc_arpcom.ac_enaddr)); +#else + /* + * XXX Below should only be used if 'local-mac-address?' == false + */ + myetheraddr(sc->sc_arpcom.ac_enaddr); +#endif + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_start = hmestart; + ifp->if_ioctl = hmeioctl; + ifp->if_watchdog = hmewatchdog; + ifp->if_flags = + IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; + + /* Attach the interface. */ + if_attach(ifp); + ether_ifattach(ifp); + + sc->sc_an_state = HME_TIMER_DONE; + sc->sc_an_ticks = 0; + + printf(" pri %d: address %s rev %d\n", pri, + ether_sprintf(sc->sc_arpcom.ac_enaddr), sc->sc_rev); + +#if NBPFILTER > 0 + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +} + +/* + * Start output on interface. + * We make two assumptions here: + * 1) that the current priority is set to splnet _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) that the IFF_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ +void +hmestart(ifp) + struct ifnet *ifp; +{ + struct hme_softc *sc = ifp->if_softc; + struct mbuf *m; + int bix, len; + + if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) + return; + + bix = sc->sc_last_td; + + for (;;) { + IF_DEQUEUE(&ifp->if_snd, m); + if (m == 0) + break; +#if NBPFILTER > 0 + /* + * If BPF is listening on this interface, let it see the + * packet before we commit it to the wire. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + + /* + * Copy the mbuf chain into the transmit buffer. + */ + len = hme_put(sc, bix, m); + + /* + * Initialize transmit registers and start transmission + */ + sc->sc_desc->hme_txd[bix].tx_addr = (u_long) sc->sc_bufs_dva + + (((u_long) &(sc->sc_bufs->tx_buf[bix])) - + ((u_long) sc->sc_bufs)); + sc->sc_desc->hme_txd[bix].tx_flags = + TXFLAG_OWN | TXFLAG_SOP | TXFLAG_EOP | + (len & TXFLAG_SIZE); + + sc->sc_txr->tx_pnding = TXR_TP_DMAWAKEUP; + + if (++bix == TX_RING_SIZE) + bix = 0; + + if (++sc->sc_no_td == TX_RING_SIZE) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + } + + sc->sc_last_td = bix; +} + +#define MAX_STOP_TRIES 16 + +void +hmestop(sc) + struct hme_softc *sc; +{ + int tries = 0; + + sc->sc_gr->reset = GR_RESET_ALL; + while (sc->sc_gr->reset && (tries != MAX_STOP_TRIES)) + DELAY(20); + if (tries == MAX_STOP_TRIES) + printf("%s: stop failed\n", sc->sc_dev.dv_xname); +} + +/* + * Reset interface. + */ +void +hmereset(sc) + struct hme_softc *sc; +{ + int s; + + s = splnet(); + hmestop(sc); + hmeinit(sc); + splx(s); +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to generate + * an interrupt after a transmit has been started on it. + */ +void +hmewatchdog(ifp) + struct ifnet *ifp; +{ + struct hme_softc *sc = ifp->if_softc; + + log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname); + ++sc->sc_arpcom.ac_if.if_oerrors; + + hmereset(sc); +} + +int +hmeioctl(ifp, cmd, data) + struct ifnet *ifp; + u_long cmd; + caddr_t data; +{ + struct hme_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + if ((error = ether_ioctl(ifp, &sc->sc_arpcom, cmd, data)) > 0) { + splx(s); + return error; + } + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + hmeinit(sc); + arp_ifinit(&sc->sc_arpcom, ifa); + break; +#endif /* INET */ +#ifdef NS + /* XXX - This code is probably wrong. */ + 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(ina->x_host.c_host, + sc->sc_arpcom.ac_enaddr, + sizeof(sc->sc_arpcom.ac_enaddr)); + /* Set new address. */ + hmeinit(sc); + break; + } +#endif /* NS */ + default: + hmeinit(sc); + break; + } + break; + + case SIOCSIFFLAGS: + sc->sc_promisc = ifp->if_flags & (IFF_PROMISC | IFF_ALLMULTI); + if ((ifp->if_flags & IFF_UP) == 0 && + (ifp->if_flags & IFF_RUNNING) != 0) { + /* + * If interface is marked down and it is running, then + * stop it. + */ + hmestop(sc); + ifp->if_flags &= ~IFF_RUNNING; + } else if ((ifp->if_flags & IFF_UP) != 0 && + (ifp->if_flags & IFF_RUNNING) == 0) { + /* + * If interface is marked up and it is stopped, then + * start it. + */ + hmeinit(sc); + } else { + /* + * Reset the interface to pick up changes in any other + * flags that affect hardware registers. + */ + hmestop(sc); + hmeinit(sc); + } +#ifdef IEDEBUG + if (ifp->if_flags & IFF_DEBUG) + sc->sc_debug = IED_ALL; + else + sc->sc_debug = 0; +#endif + break; + + case SIOCADDMULTI: + case SIOCDELMULTI: + error = (cmd == SIOCADDMULTI) ? + ether_addmulti(ifr, &sc->sc_arpcom): + ether_delmulti(ifr, &sc->sc_arpcom); + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ +#if 0 + mc_reset(sc); +#endif + error = 0; + } + break; + default: + error = EINVAL; + } + splx(s); + return error; +} + +void +hmeinit(sc) + struct hme_softc *sc; +{ + u_int32_t c; + struct hme_tcvr *tcvr = sc->sc_tcvr; + struct hme_cr *cr = sc->sc_cr; + struct hme_gr *gr = sc->sc_gr; + struct hme_txr *txr = sc->sc_txr; + struct hme_rxr *rxr = sc->sc_rxr; + + hme_poll_stop(sc); + hmestop(sc); + + hme_meminit(sc); + + tcvr->int_mask = 0xffff; + + c = tcvr->cfg; + if (sc->sc_flags & HME_FLAG_FENABLE) + tcvr->cfg = c & ~(TCVR_CFG_BENABLE); + else + tcvr->cfg = c | TCVR_CFG_BENABLE; + + hme_tcvr_check(sc); + switch (sc->sc_tcvr_type) { + case HME_TCVR_NONE: + printf("%s: no transceiver type!\n", sc->sc_dev.dv_xname); + return; + case HME_TCVR_INTERNAL: + cr->xif_cfg = 0; + break; + case HME_TCVR_EXTERNAL: + cr->xif_cfg = CR_XCFG_MIIDISAB; + break; + } + hme_tcvr_reset(sc); + + hme_reset_tx(sc); + hme_reset_rx(sc); + + cr->rand_seed = sc->sc_arpcom.ac_enaddr[5] | + ((sc->sc_arpcom.ac_enaddr[4] << 8) & 0x3f00); + cr->mac_addr0 = (sc->sc_arpcom.ac_enaddr[0] << 8) | + sc->sc_arpcom.ac_enaddr[1]; + cr->mac_addr1 = (sc->sc_arpcom.ac_enaddr[2] << 8) | + sc->sc_arpcom.ac_enaddr[3]; + cr->mac_addr2 = (sc->sc_arpcom.ac_enaddr[4] << 8) | + sc->sc_arpcom.ac_enaddr[5]; + + cr->jsize = HME_DEFAULT_JSIZE; + cr->ipkt_gap1 = HME_DEFAULT_IPKT_GAP1; + cr->ipkt_gap2 = HME_DEFAULT_IPKT_GAP2; + cr->htable3 = 0; + cr->htable2 = 0; + cr->htable1 = 0; + cr->htable0 = 0; + + rxr->rx_ring = (u_long) sc->sc_desc_dva + + (((u_long) &sc->sc_desc->hme_rxd[0]) + - ((u_long)sc->sc_desc)); + txr->tx_ring = (u_long) sc->sc_desc_dva + + (((u_long) &sc->sc_desc->hme_txd[0]) + - ((u_long)sc->sc_desc)); + + if (sc->sc_burst & SBUS_BURST_64) + gr->cfg = GR_CFG_BURST64; + else if (sc->sc_burst & SBUS_BURST_32) + gr->cfg = GR_CFG_BURST32; + else if (sc->sc_burst & SBUS_BURST_16) + gr->cfg = GR_CFG_BURST16; + else { + printf("%s: burst size unknown\n", sc->sc_dev.dv_xname); + gr->cfg = 0; + } + + gr->imask = GR_IMASK_SENTFRAME | GR_IMASK_TXPERR | + GR_IMASK_GOTFRAME | GR_IMASK_RCNTEXP; + + txr->tx_rsize = (TX_RING_SIZE >> TXR_RSIZE_SHIFT) - 1; + txr->cfg |= TXR_CFG_DMAENABLE; + + c = RXR_CFG_DMAENABLE | (RX_OFFSET << 3) | (RX_CSUMLOC << 16); +#if RX_RING_SIZE == 32 + c |= RXR_CFG_RINGSIZE32; +#elif RX_RING_SIZE == 64 + c |= RXR_CFG_RINGSIZE64; +#elif RX_RING_SIZE == 128 + c |= RXR_CFG_RINGSIZE128; +#elif RX_RING_SIZE == 256 + c |= RXR_CFG_RINGSIZE256; +#else +#error "RX_RING_SIZE must be 32, 64, 128, or 256." +#endif + rxr->cfg = c; + DELAY(20); + if (c != rxr->cfg) /* the receiver sometimes misses bits */ + printf("%s: setting rxreg->cfg failed.\n", sc->sc_dev.dv_xname); + + cr->rx_cfg = CR_RXCFG_HENABLE; + DELAY(10); + + c = CR_TXCFG_DGIVEUP; + if (sc->sc_flags & HME_FLAG_FULL) + c |= CR_TXCFG_FULLDPLX; + cr->tx_cfg = c; + + c = CR_XCFG_ODENABLE; + if (sc->sc_flags & HME_FLAG_LANCE) + c |= (HME_DEFAULT_IPKT_GAP0 << 5) | CR_XCFG_LANCE; + if (sc->sc_tcvr_type == HME_TCVR_EXTERNAL) + c |= CR_XCFG_MIIDISAB; + cr->xif_cfg = c; + + cr->tx_cfg |= CR_TXCFG_ENABLE; /* enable tx */ + cr->rx_cfg |= CR_RXCFG_ENABLE; /* enable rx */ + + hme_auto_negotiate(sc); +} + +static void +hme_set_initial_advertisement(sc) + struct hme_softc *sc; +{ + hmestop(sc); + sc->sc_tcvr->int_mask = 0xffff; + if (sc->sc_flags & HME_FLAG_FENABLE) + sc->sc_tcvr->cfg &= ~(TCVR_CFG_BENABLE); + else + sc->sc_tcvr->cfg |= TCVR_CFG_BENABLE; + + hme_tcvr_check(sc); + switch (sc->sc_tcvr_type) { + case HME_TCVR_NONE: + return; + case HME_TCVR_INTERNAL: + sc->sc_cr->xif_cfg = 0; + break; + case HME_TCVR_EXTERNAL: + sc->sc_cr->xif_cfg = CR_XCFG_MIIDISAB; + break; + } + if (hme_tcvr_reset(sc)) + return; + + /* grab the supported modes and advertised modes */ + sc->sc_sw.bmsr = hme_tcvr_read(sc, DP83840_BMSR); + sc->sc_sw.anar = hme_tcvr_read(sc, DP83840_ANAR); + + /* If 10BaseT Half duplex supported, advertise it, and so on... */ + if (sc->sc_sw.bmsr & BMSR_10BASET_HALF) + sc->sc_sw.anar |= ANAR_10; + else + sc->sc_sw.anar &= ~(ANAR_10); + + if (sc->sc_sw.bmsr & BMSR_10BASET_FULL) + sc->sc_sw.anar |= ANAR_10_FD; + else + sc->sc_sw.anar &= ~(ANAR_10_FD); + + if (sc->sc_sw.bmsr & BMSR_100BASETX_HALF) + sc->sc_sw.anar |= ANAR_TX; + else + sc->sc_sw.anar &= ~(ANAR_TX); + + if (sc->sc_sw.bmsr & BMSR_100BASETX_FULL) + sc->sc_sw.anar |= ANAR_TX_FD; + else + sc->sc_sw.anar &= ~(ANAR_TX_FD); + + /* Inform card about what it should advertise */ + hme_tcvr_write(sc, DP83840_ANAR, sc->sc_sw.anar); +} + +#define XCVR_RESET_TRIES 16 +#define XCVR_UNISOLATE_TRIES 32 + +static int +hme_tcvr_reset(sc) + struct hme_softc *sc; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + u_int32_t cfg; + int result, tries = XCVR_RESET_TRIES; + + cfg = tcvr->cfg; + if (sc->sc_tcvr_type == HME_TCVR_EXTERNAL) { + tcvr->cfg = cfg & ~(TCVR_CFG_PSELECT); + sc->sc_tcvr_type = HME_TCVR_INTERNAL; + sc->sc_phyaddr = TCVR_PHYADDR_ITX; + hme_tcvr_write(sc, DP83840_BMCR, + (BMCR_LOOPBACK | BMCR_PDOWN | BMCR_ISOLATE)); + result = hme_tcvr_read(sc, DP83840_BMCR); + if (result == TCVR_FAILURE) { + printf("%s: tcvr_reset failed\n", sc->sc_dev.dv_xname); + return -1; + } + tcvr->cfg = cfg | TCVR_CFG_PSELECT; + sc->sc_tcvr_type = HME_TCVR_EXTERNAL; + sc->sc_phyaddr = TCVR_PHYADDR_ETX; + } + else { + if (cfg & TCVR_CFG_MDIO1) { + tcvr->cfg = cfg | TCVR_CFG_PSELECT; + hme_tcvr_write(sc, DP83840_BMCR, + (BMCR_LOOPBACK | BMCR_PDOWN | BMCR_ISOLATE)); + result = hme_tcvr_read(sc, DP83840_BMCR); + if (result == TCVR_FAILURE) { + printf("%s: tcvr_reset failed\n", + sc->sc_dev.dv_xname); + return -1; + } + tcvr->cfg = cfg & ~(TCVR_CFG_PSELECT); + sc->sc_tcvr_type = HME_TCVR_INTERNAL; + sc->sc_phyaddr = TCVR_PHYADDR_ITX; + } + } + + hme_tcvr_write(sc, DP83840_BMCR, BMCR_RESET); + + while (--tries) { + result = hme_tcvr_read(sc, DP83840_BMCR); + if (result == TCVR_FAILURE) + return -1; + sc->sc_sw.bmcr = result; + if (!(result & BMCR_RESET)) + break; + DELAY(200); + } + if (!tries) { + printf("%s: bmcr reset failed\n", sc->sc_dev.dv_xname); + return -1; + } + + sc->sc_sw.bmsr = hme_tcvr_read(sc, DP83840_BMSR); + sc->sc_sw.phyidr1 = hme_tcvr_read(sc, DP83840_PHYIDR1); + sc->sc_sw.phyidr2 = hme_tcvr_read(sc, DP83840_PHYIDR2); + sc->sc_sw.anar = hme_tcvr_read(sc, DP83840_BMSR); + + sc->sc_sw.bmcr &= ~(BMCR_ISOLATE); + hme_tcvr_write(sc, DP83840_BMCR, sc->sc_sw.bmcr); + + tries = XCVR_UNISOLATE_TRIES; + while (--tries) { + result = hme_tcvr_read(sc, DP83840_BMCR); + if (result == TCVR_FAILURE) + return -1; + if (!(result & BMCR_ISOLATE)) + break; + DELAY(200); + } + if (!tries) { + printf("%s: bmcr unisolate failed\n", sc->sc_dev.dv_xname); + return -1; + } + + result = hme_tcvr_read(sc, DP83840_PCR); + hme_tcvr_write(sc, DP83840_PCR, (result | PCR_CIM_DIS)); + return 0; +} + + +/* + * We need to know whether we are using an internal or external transceiver. + */ +static void +hme_tcvr_check(sc) + struct hme_softc *sc; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + u_int32_t cfg = tcvr->cfg; + + /* polling? */ + if (sc->sc_flags & HME_FLAG_POLL) { + if (sc->sc_tcvr_type == HME_TCVR_INTERNAL) { + hme_poll_stop(sc); + sc->sc_phyaddr = TCVR_PHYADDR_ETX; + sc->sc_tcvr_type = HME_TCVR_EXTERNAL; + cfg &= ~(TCVR_CFG_PENABLE); + cfg |= TCVR_CFG_PSELECT; + tcvr->cfg = cfg; + } + else { + if (!(tcvr->status >> 16)) { + hme_poll_stop(sc); + sc->sc_phyaddr = TCVR_PHYADDR_ITX; + sc->sc_tcvr_type = HME_TCVR_INTERNAL; + cfg &= ~(TCVR_CFG_PSELECT); + tcvr->cfg = cfg; + } + } + } + else { + u_int32_t cfg2 = tcvr->cfg; + + if (cfg2 & TCVR_CFG_MDIO1) { + tcvr->cfg = cfg | TCVR_CFG_PSELECT; + sc->sc_phyaddr = TCVR_PHYADDR_ETX; + sc->sc_tcvr_type = HME_TCVR_EXTERNAL; + } + else { + if (cfg2 & TCVR_CFG_MDIO0) { + tcvr->cfg = cfg & ~(TCVR_CFG_PSELECT); + sc->sc_phyaddr = TCVR_PHYADDR_ITX; + sc->sc_tcvr_type = HME_TCVR_INTERNAL; + } + else { + sc->sc_tcvr_type = HME_TCVR_NONE; + } + } + } +} + +static void +hme_poll_stop(sc) + struct hme_softc *sc; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + + /* if not polling, or polling not enabled, we're done. */ + if ((sc->sc_flags & (HME_FLAG_POLLENABLE | HME_FLAG_POLL)) != + (HME_FLAG_POLLENABLE | HME_FLAG_POLL)) + return; + + /* Turn off MIF interrupts, and diable polling */ + tcvr->int_mask = 0xffff; + tcvr->cfg &= ~(TCVR_CFG_PENABLE); + sc->sc_flags &= ~(HME_FLAG_POLL); + DELAY(200); +} + +#define XCVR_WRITE_TRIES 16 + +static void +hme_tcvr_write(sc, reg, val) + struct hme_softc *sc; + int reg; + u_short val; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + int tries = XCVR_WRITE_TRIES; + + /* Use the bitbang? */ + if (! (sc->sc_flags & HME_FLAG_FENABLE)) + return hme_tcvr_bb_write(sc, reg, val); + + /* No, good... we just write to the tcvr frame */ + tcvr->frame = (FRAME_WRITE | sc->sc_phyaddr << 23) | + ((reg & 0xff) << 18) | + (val & 0xffff); + while (!(tcvr->frame & 0x10000) && (tries != 0)) { + tries--; + DELAY(200); + } + + if (!tries) + printf("%s: tcvr_write failed\n", sc->sc_dev.dv_xname); +} + +#define XCVR_READ_TRIES 16 + +static int +hme_tcvr_read(sc, reg) + struct hme_softc *sc; + int reg; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + int tries = XCVR_READ_TRIES; + + if (sc->sc_tcvr_type == HME_TCVR_NONE) { + printf("%s: no transceiver type\n", sc->sc_dev.dv_xname); + return TCVR_FAILURE; + } + + /* Use the bitbang? */ + if (! (sc->sc_flags & HME_FLAG_FENABLE)) + return hme_tcvr_bb_read(sc, reg); + + /* No, good... we just write/read to the tcvr frame */ + tcvr->frame = (FRAME_READ | sc->sc_phyaddr << 23) | + ((reg & 0xff) << 18); + while (!(tcvr->frame & 0x10000) && (tries != 0)) { + tries--; + DELAY(200); + } + + if (!tries) { + printf("%s: tcvr_write failed\n", sc->sc_dev.dv_xname); + return TCVR_FAILURE; + } + return (tcvr->frame & 0xffff); +} + +/* + * Writing to the serial BitBang, is a matter of putting the bit + * into the data register, then strobing the clock. + */ +static void +hme_tcvr_bb_writeb(sc, b) + struct hme_softc *sc; + int b; +{ + sc->sc_tcvr->bb_data = b & 0x1; + sc->sc_tcvr->bb_clock = 0; + sc->sc_tcvr->bb_clock = 1; +} + +static int +hme_tcvr_bb_readb(sc) + struct hme_softc *sc; +{ + int ret; + + sc->sc_tcvr->bb_clock = 0; + DELAY(10); + if (sc->sc_tcvr_type == HME_TCVR_INTERNAL) + ret = sc->sc_tcvr->cfg & TCVR_CFG_MDIO0; + else + ret = sc->sc_tcvr->cfg & TCVR_CFG_MDIO1; + sc->sc_tcvr->bb_clock = 1; + return ((ret) ? 1 : 0); +} + +static void +hme_tcvr_bb_write(sc, reg, val) + struct hme_softc *sc; + int reg; + u_short val; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + int i; + + tcvr->bb_oenab = 1; /* turn on bitbang intrs */ + + for (i = 0; i < 32; i++) /* make bitbang idle */ + hme_tcvr_bb_writeb(sc, 1); + + hme_tcvr_bb_writeb(sc, 0); /* 0101 signals a write */ + hme_tcvr_bb_writeb(sc, 1); + hme_tcvr_bb_writeb(sc, 0); + hme_tcvr_bb_writeb(sc, 1); + + for (i = 4; i >= 0; i--) /* send PHY addr */ + hme_tcvr_bb_writeb(sc, ((sc->sc_phyaddr & 0xff) >> i) & 0x1); + + for (i = 4; i >= 0; i--) /* send register num */ + hme_tcvr_bb_writeb(sc, ((reg & 0xff) >> i) & 0x1); + + hme_tcvr_bb_writeb(sc, 1); /* get ready for data */ + hme_tcvr_bb_writeb(sc, 0); + + for (i = 15; i >= 0; i--) /* send new value */ + hme_tcvr_bb_writeb(sc, (val >> i) & 0x1); + + tcvr->bb_oenab = 0; /* turn off bitbang intrs */ +} + +static int +hme_tcvr_bb_read(sc, reg) + struct hme_softc *sc; + int reg; +{ + struct hme_tcvr *tcvr = sc->sc_tcvr; + int ret = 0, i; + + tcvr->bb_oenab = 1; /* turn on bitbang intrs */ + + for (i = 0; i < 32; i++) /* make bitbang idle */ + hme_tcvr_bb_writeb(sc, 1); + + hme_tcvr_bb_writeb(sc, 0); /* 0110 signals a read */ + hme_tcvr_bb_writeb(sc, 1); + hme_tcvr_bb_writeb(sc, 1); + hme_tcvr_bb_writeb(sc, 0); + + for (i = 4; i >= 0; i--) /* send PHY addr */ + hme_tcvr_bb_writeb(sc, ((sc->sc_phyaddr & 0xff) >> i) & 0x1); + + for (i = 4; i >= 0; i--) /* send register num */ + hme_tcvr_bb_writeb(sc, ((reg & 0xff) >> i) & 0x1); + + tcvr->bb_oenab = 0; /* turn off bitbang intrs */ + + hme_tcvr_bb_readb(sc); /* ignore... */ + + for (i = 15; i >= 15; i--) /* read value */ + ret |= hme_tcvr_bb_readb(sc) << i; + + hme_tcvr_bb_readb(sc); /* ignore... */ + hme_tcvr_bb_readb(sc); /* ignore... */ + hme_tcvr_bb_readb(sc); /* ignore... */ + + return ret; +} + +static void +hme_auto_negotiate(sc) + struct hme_softc *sc; +{ + int tries; + + /* grab all of the registers */ + sc->sc_sw.bmsr = hme_tcvr_read(sc, DP83840_BMSR); + sc->sc_sw.bmcr = hme_tcvr_read(sc, DP83840_BMCR); + sc->sc_sw.phyidr1 = hme_tcvr_read(sc, DP83840_PHYIDR1); + sc->sc_sw.phyidr2 = hme_tcvr_read(sc, DP83840_PHYIDR2); + sc->sc_sw.anar = hme_tcvr_read(sc, DP83840_ANAR); + + /* can this board autonegotiate? No, do it manually */ + if (! (sc->sc_sw.bmsr & BMSR_ANC)) + hme_manual_negotiate(sc); + + /* advertise -everything- supported */ + if (sc->sc_sw.bmsr & BMSR_10BASET_HALF) + sc->sc_sw.anar |= ANAR_10; + else + sc->sc_sw.anar &= ~(ANAR_10); + + if (sc->sc_sw.bmsr & BMSR_10BASET_FULL) + sc->sc_sw.anar |= ANAR_10_FD; + else + sc->sc_sw.anar &= ~(ANAR_10_FD); + + if (sc->sc_sw.bmsr & BMSR_100BASETX_HALF) + sc->sc_sw.anar |= ANAR_TX; + else + sc->sc_sw.anar &= ~(ANAR_TX); + + if (sc->sc_sw.bmsr & BMSR_100BASETX_FULL) + sc->sc_sw.anar |= ANAR_TX_FD; + else + sc->sc_sw.anar &= ~(ANAR_TX_FD); + + hme_tcvr_write(sc, DP83840_ANAR, sc->sc_sw.anar); + + /* Start autonegoiation */ + sc->sc_sw.bmcr |= BMCR_ANE; /* enable auto-neg */ + hme_tcvr_write(sc, DP83840_BMCR, sc->sc_sw.bmcr); + sc->sc_sw.bmcr |= BMCR_RAN; /* force a restart */ + hme_tcvr_write(sc, DP83840_BMCR, sc->sc_sw.bmcr); + + /* BMCR_RAN clears itself when it has started negotiation... */ + tries = 64; + while (--tries) { + int r = hme_tcvr_read(sc, DP83840_BMCR); + if (r == TCVR_FAILURE) + return; + sc->sc_sw.bmcr = r; + if (! (sc->sc_sw.bmcr & BMCR_RAN)) + break; + DELAY(100); + } + if (!tries) { + printf("%s: failed to start auto-negotiation\n", + sc->sc_dev.dv_xname); + hme_manual_negotiate(sc); + return; + } + sc->sc_an_state = HME_TIMER_AUTONEG; + sc->sc_an_ticks = 0; + timeout(hme_negotiate_watchdog, sc, (12 * hz)/10); +} + +static void +hme_manual_negotiate(sc) + struct hme_softc *sc; +{ + printf("%s: Starting manual negotiation... not yet!\n", + sc->sc_dev.dv_xname); +} + +/* + * If auto-negotiating, check to see if it has completed successfully. If so, + * wait for a link up. If it completed unsucessfully, try the manual process. + */ +static void +hme_negotiate_watchdog(arg) + void *arg; +{ + struct hme_softc *sc = (struct hme_softc *)arg; + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + + sc->sc_an_ticks++; + switch (sc->sc_an_state) { + case HME_TIMER_DONE: + return; + case HME_TIMER_AUTONEG: + printf("%s: tick: autoneg...\n", sc->sc_dev.dv_xname); + sc->sc_sw.bmsr = hme_tcvr_read(sc, DP83840_BMSR); + if (sc->sc_sw.bmsr & BMSR_ANCOMPLETE) { + sc->sc_an_state = HME_TIMER_LINKUP; + sc->sc_an_ticks = 0; + timeout(hme_negotiate_watchdog, sc, (12 * hz)/10); + return; + } + if (sc->sc_an_ticks > 10) { + printf("%s: auto-negotiation failed.\n", + sc->sc_dev.dv_xname); + return; + } + timeout(hme_negotiate_watchdog, sc, (12 * hz)/10); + break; + case HME_TIMER_LINKUP: + printf("%s: tick: linkup..\n", sc->sc_dev.dv_xname); + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_timer = 0; + hmestart(ifp); + sc->sc_sw.bmsr = hme_tcvr_read(sc, DP83840_BMSR); + if (sc->sc_sw.bmsr & BMSR_LINKSTATUS) { + sc->sc_an_state = HME_TIMER_DONE; + sc->sc_an_ticks = 0; + hme_print_link_mode(sc); + return; + } + if ((sc->sc_an_ticks % 10) == 0) { + printf("%s: link down...\n", sc->sc_dev.dv_xname); + timeout(hme_negotiate_watchdog, sc, (12 * hz)/10); + return; + } + } +} + +static void +hme_print_link_mode(sc) + struct hme_softc *sc; +{ + sc->sc_sw.anlpar = hme_tcvr_read(sc, DP83840_ANLPAR); + printf("%s: %s transceiver up %dMb/s %s duplex\n", + sc->sc_dev.dv_xname, + (sc->sc_tcvr_type == HME_TCVR_EXTERNAL) ? "external" : "internal", + (sc->sc_sw.anlpar & (ANLPAR_TX_FD | ANLPAR_TX)) ? 100 : 10, + (sc->sc_sw.anlpar & (ANLPAR_TX_FD | ANLPAR_10_FD)) ? "full" : "half"); +} + +#define RESET_TRIES 32 + +static void +hme_reset_tx(sc) + struct hme_softc *sc; +{ + int tries = RESET_TRIES; + struct hme_cr *cr = sc->sc_cr; + + cr->tx_swreset = 0; + while (tries-- && (cr->tx_swreset & 1)) + DELAY(20); + + if (!tries) + printf("%s: reset tx failed\n", sc->sc_dev.dv_xname); +} + +static void +hme_reset_rx(sc) + struct hme_softc *sc; +{ + int tries = RESET_TRIES; + struct hme_cr *cr = sc->sc_cr; + + cr->rx_swreset = 0; + while (tries-- && (cr->rx_swreset & 1)) + DELAY(20); + + if (!tries) + printf("%s: reset rx failed\n", sc->sc_dev.dv_xname); +} + +static void +hme_meminit(sc) + struct hme_softc *sc; +{ + struct hme_desc *desc = sc->sc_desc; + int i; + + /* setup tx descriptors */ + sc->sc_first_td = sc->sc_last_td = sc->sc_no_td = 0; + for (i = 0; i < TX_RING_SIZE; i++) + desc->hme_txd[i].tx_flags = 0; + + /* setup rx descriptors */ + sc->sc_last_rd = 0; + for (i = 0; i < RX_RING_SIZE; i++) { + + desc->hme_rxd[i].rx_addr = (u_long) sc->sc_bufs_dva + + (((u_long) &(sc->sc_bufs->rx_buf[i])) - + ((u_long) sc->sc_bufs)); + + desc->hme_rxd[i].rx_flags = + RXFLAG_OWN | ((RX_PKT_BUF_SZ - RX_OFFSET) << 16); + } + +} + +/* + * Pull data off an interface. + * Len is the length of data, with local net header stripped. + * We copy the data into mbufs. When full cluster sized units are present, + * we copy into clusters. + */ +static struct mbuf * +hme_get(sc, idx, totlen) + struct hme_softc *sc; + int idx, totlen; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct mbuf *m; + struct mbuf *top, **mp; + int len, pad, boff = 0; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + pad = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header); + m->m_data += pad; + len = MHLEN - pad; + top = NULL; + mp = ⊤ + + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + m_freem(top); + return NULL; + } + len = MLEN; + } + if (top && totlen >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + len = MCLBYTES; + } + m->m_len = len = min(totlen, len); + bcopy(&(sc->sc_bufs->rx_buf[idx][boff + RX_OFFSET]), + mtod(m, caddr_t), len); + boff += len; + totlen -= len; + *mp = m; + mp = &m->m_next; + } + + return (top); +} + +static int +hme_put(sc, idx, m) + struct hme_softc *sc; + int idx; + struct mbuf *m; +{ + struct mbuf *n; + int len, tlen = 0, boff = 0; + + for (; m; m = n) { + len = m->m_len; + if (len == 0) { + MFREE(m, n); + continue; + } + bcopy(mtod(m, caddr_t), &(sc->sc_bufs->tx_buf[idx][boff]), len); + boff += len; + tlen += len; + MFREE(m, n); + } + return tlen; +} + +/* + * mif interrupt + */ +static int +hme_mint(sc, why) + struct hme_softc *sc; + u_int32_t why; +{ + sc->sc_sw.bmcr = hme_tcvr_read(sc, DP83840_BMCR); + sc->sc_sw.anlpar = hme_tcvr_read(sc, DP83840_ANLPAR); + + printf("%s: link status changed\n", sc->sc_dev.dv_xname); + if (sc->sc_sw.anlpar & ANLPAR_TX_FD) { + sc->sc_sw.bmcr |= (BMCR_SPEED | BMCR_DUPLEX); + } else if (sc->sc_sw.anlpar & ANLPAR_TX) { + sc->sc_sw.bmcr |= BMCR_SPEED; + } else if (sc->sc_sw.anlpar & ANLPAR_10_FD) { + sc->sc_sw.bmcr |= BMCR_DUPLEX; + } /* else 10Mb half duplex... */ + hme_tcvr_write(sc, DP83840_BMCR, sc->sc_sw.bmcr); + hme_print_link_mode(sc); + hme_poll_stop(sc); + return 1; +} + +/* + * receive interrupt + */ +static int +hme_rint(sc, why) + struct hme_softc *sc; + u_int32_t why; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int bix, len; + struct hme_rxd rxd; + + bix = sc->sc_last_rd; + + /* Process all buffers with valid data. */ + for (;;) { + bcopy(&(sc->sc_desc->hme_rxd[bix]), &rxd, sizeof(rxd)); + len = rxd.rx_flags >> 16; + + if (rxd.rx_flags & RXFLAG_OWN) + break; + + if (rxd.rx_flags & RXFLAG_OVERFLOW) + ifp->if_ierrors++; + else + hme_read(sc, bix, len); + + rxd.rx_flags = RXFLAG_OWN|((RX_PKT_BUF_SZ - RX_OFFSET) << 16); + bcopy(&rxd, &(sc->sc_desc->hme_rxd[bix]), sizeof(rxd)); + + if (++bix == RX_RING_SIZE) + bix = 0; + } + + sc->sc_last_rd = bix; + + return 1; +} + +/* + * error interrupt + */ +static int +hme_eint(sc, why) + struct hme_softc *sc; + u_int32_t why; +{ + if (why & GR_STAT_RFIFOVF) { /* probably dma error */ + printf("%s: receive fifo overflow\n", sc->sc_dev.dv_xname); + hmereset(sc); + } + + if (why & GR_STAT_STSTERR) { + printf("%s: SQE test failed: resetting\n", sc->sc_dev.dv_xname); + hmereset(sc); + } + + if (why & GR_STAT_TFIFO_UND) { /* probably dma error */ + printf("%s: tx fifo underrun\n", sc->sc_dev.dv_xname); + hmereset(sc); + } + + if (why & GR_STAT_MAXPKTERR) { /* driver bug */ + printf("%s: tx max packet size error\n", sc->sc_dev.dv_xname); + hmereset(sc); + } + + if (why & GR_STAT_NORXD) { /* driver bug */ + printf("%s: out of receive descriptors\n", sc->sc_dev.dv_xname); + hmereset(sc); + } + + if (why & GR_STAT_EOPERR) { + printf("%s: eop not set in tx descriptor\n", + sc->sc_dev.dv_xname); + hmereset(sc); + } + + if (why & (GR_STAT_RXERR | GR_STAT_RXPERR | GR_STAT_RXTERR)) { + printf("%s: rx dma error < ", sc->sc_dev.dv_xname); + if (why & GR_STAT_RXERR) + printf("Generic "); + if (why & GR_STAT_RXPERR); + printf("Parity "); + if (why & GR_STAT_RXTERR) + printf("RxTag "); + printf(" >\n"); + hmereset(sc); + } + + if (why & + (GR_STAT_TXEACK|GR_STAT_TXLERR|GR_STAT_TXPERR|GR_STAT_TXTERR)) { + printf("%s: rx dma error < ", sc->sc_dev.dv_xname); + if (why & GR_STAT_TXEACK) + printf("Generic "); + if (why & GR_STAT_TXLERR); + printf("Late "); + if (why & GR_STAT_TXPERR) + printf("Parity "); + if (why & GR_STAT_TXTERR); + printf("TxTag "); + printf(" >\n"); + hmereset(sc); + } + + if (why & (GR_STAT_SLVERR | GR_STAT_SLVPERR)) { + printf("%s: sbus %s error accessing registers\n", + sc->sc_dev.dv_xname, + (why & GR_STAT_SLVPERR) ? "parity" : "generic"); + hmereset(sc); + } + + return 1; +} + +/* + * transmit interrupt + */ +static int +hme_tint(sc, why) + struct hme_softc *sc; + u_int32_t why; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + int bix; + struct hme_txd txd; + + bix = sc->sc_first_td; + + for (;;) { + if (sc->sc_no_td <= 0) + break; + + bcopy(&(sc->sc_desc->hme_txd[bix]), &txd, sizeof(txd)); + + if (txd.tx_flags & TXFLAG_OWN) + break; + + ifp->if_flags &= ~IFF_OACTIVE; + ifp->if_opackets++; + + if (++bix == TX_RING_SIZE) + bix = 0; + + --sc->sc_no_td; + } + + sc->sc_first_td = bix; + + hmestart(ifp); + + if (sc->sc_no_td == 0) + ifp->if_timer = 0; + + return 1; +} + +/* + * Interrup handler + */ +int +hmeintr(v) + void *v; +{ + struct hme_softc *sc = (struct hme_softc *)v; + struct hme_gr *gr = sc->sc_gr; + u_int32_t why; + int r = 0; + + why = gr->stat; + + if (why & GR_STAT_ALL_ERRORS) + r |= hme_eint(sc, why); + + if (why & GR_STAT_MIFIRQ) + r |= hme_mint(sc, why); + + if (why & (GR_STAT_TXALL | GR_STAT_HOSTTOTX)) + r |= hme_tint(sc, why); + + if (why & GR_STAT_RXTOHOST) + r |= hme_rint(sc, why); + + return (r); +} + +static void +hme_read(sc, idx, len) + struct hme_softc *sc; + int idx, len; +{ + struct ifnet *ifp = &sc->sc_arpcom.ac_if; + struct ether_header *eh; + struct mbuf *m; + + if (len <= sizeof(struct ether_header) || + len > ETHERMTU + sizeof(struct ether_header)) { + + printf("%s: invalid packet size %d; dropping\n", + sc->sc_dev.dv_xname, len); + + ifp->if_ierrors++; + return; + } + + /* Pull packet off interface. */ + m = hme_get(sc, idx, len); + if (m == NULL) { + ifp->if_ierrors++; + return; + } + + ifp->if_ipackets++; + + /* We assume that the header fit entirely in one mbuf. */ + eh = mtod(m, struct ether_header *); + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to BPF. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m); +#endif + /* Pass the packet up, with the ether header sort-of removed. */ + m_adj(m, sizeof(struct ether_header)); + ether_input(ifp, eh, m); +} diff --git a/sys/arch/sparc/dev/hmereg.h b/sys/arch/sparc/dev/hmereg.h new file mode 100644 index 00000000000..98d481a8210 --- /dev/null +++ b/sys/arch/sparc/dev/hmereg.h @@ -0,0 +1,582 @@ +/* $OpenBSD: hmereg.h,v 1.1 1998/07/10 19:09:12 jason Exp $ */ + +/* + * Copyright (c) 1998 Jason L. Wright (jason@thought.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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. 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. + */ + +#define HME_DEFAULT_JSIZE 4 +#define HME_DEFAULT_IPKT_GAP0 16 +#define HME_DEFAULT_IPKT_GAP1 8 +#define HME_DEFAULT_IPKT_GAP2 4 +#define MEMSIZE 4096 + +/* global registers */ +struct hme_gr { + volatile u_int32_t reset; /* reset tx/rx */ + volatile u_int32_t cfg; /* config */ + volatile u_int32_t _padding[62]; /* unused */ + volatile u_int32_t stat; /* intr status */ + volatile u_int32_t imask; /* intr mask */ +}; + +/* hme_gr.reset (software reset register) */ +#define GR_RESET_ETX 0x01 /* reset external tx */ +#define GR_RESET_ERX 0x02 /* reset external rx */ +#define GR_RESET_ALL (GR_RESET_ETX | GR_RESET_ERX) + +/* hme_gr.cfg (configuration register) */ +#define GR_CFG_BURSTMSK 0x03 /* burst mask */ +#define GR_CFG_BURST16 0x00 /* 16 byte bursts */ +#define GR_CFG_BURST32 0x01 /* 32 byte bursts */ +#define GR_CFG_BURST64 0x02 /* 32 byte bursts */ +#define GR_CFG_64BIT 0x04 +#define GR_CFG_PARITY 0x08 +#define GR_CFG_RESV 0x10 + +/* hme_gr.stat (interrupt status register) */ +#define GR_STAT_GOTFRAME 0x00000001 /* frame received */ +#define GR_STAT_RCNTEXP 0x00000002 /* rx frame count expired */ +#define GR_STAT_ACNTEXP 0x00000004 /* align error count expired */ +#define GR_STAT_CCNTEXP 0x00000008 /* crc error count expired */ +#define GR_STAT_LCNTEXP 0x00000010 /* length error count expired */ +#define GR_STAT_RFIFOVF 0x00000020 /* rx fifo overflow */ +#define GR_STAT_CVCNTEXP 0x00000040 /* code violation counter expired */ +#define GR_STAT_STSTERR 0x00000080 /* xif sqe test failed */ +#define GR_STAT_SENTFRAME 0x00000100 /* frame sent */ +#define GR_STAT_TFIFO_UND 0x00000200 /* tx fifo underrun */ +#define GR_STAT_MAXPKTERR 0x00000400 /* max-packet size error */ +#define GR_STAT_NCNTEXP 0x00000800 /* normal collision count expired */ +#define GR_STAT_ECNTEXP 0x00001000 /* excess collision count expired */ +#define GR_STAT_LCCNTEXP 0x00002000 /* late collision count expired */ +#define GR_STAT_FCNTEXP 0x00004000 /* first collision count expired */ +#define GR_STAT_DTIMEXP 0x00008000 /* defer timer expired */ +#define GR_STAT_RXTOHOST 0x00010000 /* pkt moved from rx fifo->memory */ +#define GR_STAT_NORXD 0x00020000 /* out of receive descriptors */ +#define GR_STAT_RXERR 0x00040000 /* rx dma error */ +#define GR_STAT_RXLATERR 0x00080000 /* late error during rx dma */ +#define GR_STAT_RXPERR 0x00100000 /* parity error during rx dma */ +#define GR_STAT_RXTERR 0x00200000 /* tag error during rx dma */ +#define GR_STAT_EOPERR 0x00400000 /* tx descriptor did not set EOP */ +#define GR_STAT_MIFIRQ 0x00800000 /* mif needs attention */ +#define GR_STAT_HOSTTOTX 0x01000000 /* pkt moved from memory->tx fifo */ +#define GR_STAT_TXALL 0x02000000 /* all pkts in fifo transmitted */ +#define GR_STAT_TXEACK 0x04000000 /* error during tx dma */ +#define GR_STAT_TXLERR 0x08000000 /* late error during tx dma */ +#define GR_STAT_TXPERR 0x10000000 /* parity error during tx dma */ +#define GR_STAT_TXTERR 0x20000000 /* tag error durig tx dma */ +#define GR_STAT_SLVERR 0x40000000 /* pio access error */ +#define GR_STAT_SLVPERR 0x80000000 /* pio access parity error */ + +/* all the errors to worry about */ +#define GR_STAT_ALL_ERRORS \ + (GR_STAT_SLVPERR | GR_STAT_SLVERR | GR_STAT_TXTERR | \ + GR_STAT_TXPERR | GR_STAT_TXLERR | GR_STAT_TXEACK | \ + GR_STAT_EOPERR | GR_STAT_RXTERR | GR_STAT_RXPERR | \ + GR_STAT_RXLATERR | GR_STAT_RXERR | GR_STAT_NORXD | \ + GR_STAT_DTIMEXP | GR_STAT_FCNTEXP | GR_STAT_LCCNTEXP | \ + GR_STAT_ECNTEXP | GR_STAT_NCNTEXP | GR_STAT_MAXPKTERR | \ + GR_STAT_TFIFO_UND | GR_STAT_STSTERR | GR_STAT_CVCNTEXP | \ + GR_STAT_RFIFOVF | GR_STAT_LCNTEXP | GR_STAT_CCNTEXP | \ + GR_STAT_ACNTEXP) + + +/* hme_gr.stat (interrupt status register) */ +#define GR_IMASK_GOTFRAME 0x00000001 /* frame received */ +#define GR_IMASK_RCNTEXP 0x00000002 /* rx frame count expired */ +#define GR_IMASK_ACNTEXP 0x00000004 /* align error count expired */ +#define GR_IMASK_CCNTEXP 0x00000008 /* crc error count expired */ +#define GR_IMASK_LCNTEXP 0x00000010 /* length error count expired */ +#define GR_IMASK_RFIFOVF 0x00000020 /* rx fifo overflow */ +#define GR_IMASK_CVCNTEXP 0x00000040 /* code violation count expired */ +#define GR_IMASK_STSTERR 0x00000080 /* xif sqe test failed */ +#define GR_IMASK_SENTFRAME 0x00000100 /* frame sent */ +#define GR_IMASK_TFIFO_UND 0x00000200 /* tx fifo underrun */ +#define GR_IMASK_MAXPKTERR 0x00000400 /* max-packet size error */ +#define GR_IMASK_NCNTEXP 0x00000800 /* normal collision count expired */ +#define GR_IMASK_ECNTEXP 0x00001000 /* excess collision count expired */ +#define GR_IMASK_LCCNTEXP 0x00002000 /* late collision count expired */ +#define GR_IMASK_FCNTEXP 0x00004000 /* first collision count expired */ +#define GR_IMASK_DTIMEXP 0x00008000 /* defer timer expired */ +#define GR_IMASK_RXTOHOST 0x00010000 /* pkt moved from rx fifo->memory */ +#define GR_IMASK_NORXD 0x00020000 /* out of receive descriptors */ +#define GR_IMASK_RXERR 0x00040000 /* rx dma error */ +#define GR_IMASK_RXLATERR 0x00080000 /* late error during rx dma */ +#define GR_IMASK_RXPERR 0x00100000 /* parity error during rx dma */ +#define GR_IMASK_RXTERR 0x00200000 /* tag error during rx dma */ +#define GR_IMASK_EOPERR 0x00400000 /* tx descriptor did not set EOP */ +#define GR_IMASK_MIFIRQ 0x00800000 /* mif needs attention */ +#define GR_IMASK_HOSTTOTX 0x01000000 /* pkt moved from memory->tx fifo */ +#define GR_IMASK_TXALL 0x02000000 /* all pkts in fifo transmitted */ +#define GR_IMASK_TXEACK 0x04000000 /* error during tx dma */ +#define GR_IMASK_TXLERR 0x08000000 /* late error during tx dma */ +#define GR_IMASK_TXPERR 0x10000000 /* parity error during tx dma */ +#define GR_IMASK_TXTERR 0x20000000 /* tag error during tx dma */ +#define GR_IMASK_SLVERR 0x40000000 /* pio access error */ +#define GR_IMASK_SLVPERR 0x80000000 /* PIO access parity error */ + +/* + * external transmitter registers + */ +struct hme_txr { + volatile u_int32_t tx_pnding; /* tx pending/wakeup */ + volatile u_int32_t cfg; /* tx cfg */ + volatile u_int32_t tx_ring; /* tx ring ptr */ + volatile u_int32_t tx_bbase; /* tx buffer base */ + volatile u_int32_t tx_bdisp; /* tx buffer displacement */ + volatile u_int32_t tx_fifo_wptr; /* tx fifo write pointer */ + volatile u_int32_t tx_fifo_swptr; /* tx fifo write ptr (shadow) */ + volatile u_int32_t tx_fifo_rptr; /* tx fifo read pointer */ + volatile u_int32_t tx_fifo_srptr; /* tx fifo read ptr (shadow) */ + volatile u_int32_t tx_fifo_pcnt; /* tx fifo packet counter */ + volatile u_int32_t smachine; /* tx state machine */ + volatile u_int32_t tx_rsize; /* tx ring size */ + volatile u_int32_t tx_bptr; /* tx buffer pointer */ +}; + +/* hme_txr.tx_pnding (tx pending/wakeup) */ +#define TXR_TP_DMAWAKEUP 0x00000001 /* Restart transmit dma */ + +/* hme_txr.tx_cfg (tx configuration) */ +#define TXR_CFG_DMAENABLE 0x00000001 /* enable tx dma */ +#define TXR_CFG_FIFOTHRESH 0x000003fe /* tx fifo threshold */ +#define TXR_CFG_IRQDAFTER 0x00000400 /* intr after tx-fifo empty */ +#define TXR_CFG_IRQDBEFORE 0x00000000 /* intr before tx-fifo empty */ +#define TXR_RSIZE_SHIFT 4 + +/* + * external receiver registers + */ +struct hme_rxr { + volatile u_int32_t cfg; /* rx cfg */ + volatile u_int32_t rx_ring; /* rx ring pointer */ + volatile u_int32_t rx_bptr; /* rx buffer ptr */ + volatile u_int32_t rx_fifo_wptr; /* rx fifo write ptr */ + volatile u_int32_t rx_fifo_swptr; /* rx fifo write ptr (shadow) */ + volatile u_int32_t rx_fifo_rptr; /* rx fifo read ptr */ + volatile u_int32_t rx_fifo_srptr; /* rx fifo read ptr (shadow) */ + volatile u_int32_t smachine; /* rx state machine */ +}; + +/* hme_rxr.rx_cfg (rx configuration) */ +#define RXR_CFG_DMAENABLE 0x00000001 /* rx dma enable */ +#define RXR_CFG_reserved1 0x00000006 /* reserved bits */ +#define RXR_CFG_BYTEOFFSET 0x00000038 /* rx first byte offset */ +#define RXR_CFG_reserved2 0x000001c0 /* reserved bits */ +#define RXR_CFG_RINGSIZE32 0x00000000 /* rx descptr ring size: 32 */ +#define RXR_CFG_RINGSIZE64 0x00000200 /* rx descptr ring size: 64 */ +#define RXR_CFG_RINGSIZE128 0x00000400 /* rx descptr ring size: 128 */ +#define RXR_CFG_RINGSIZE256 0x00000600 /* rx descptr ring size: 128 */ +#define RXR_CFG_reserved3 0x0000f800 /* reserved bits */ +#define RXR_CFG_CSUMSTART 0x007f0000 /* rx offset of checksum */ + +/* + * configuration registers + */ +struct hme_cr { + volatile u_int32_t xif_cfg; /* xif configuration reg */ + volatile u_int32_t _padding[129]; /* reserved */ + volatile u_int32_t tx_swreset; /* tx software reset */ + volatile u_int32_t tx_cfg; /* tx configuration reg */ + volatile u_int32_t ipkt_gap1; /* interpacket gap 1 */ + volatile u_int32_t ipkt_gap2; /* interpacket gap 2 */ + volatile u_int32_t attempt_limit; /* tx attempt limit */ + volatile u_int32_t stime; /* tx slot time */ + volatile u_int32_t preamble_len; /* len of tx preamble */ + volatile u_int32_t preamble_patt; /* tx preamble pattern */ + volatile u_int32_t tx_sframedelim; /* tx frame delimiter */ + volatile u_int32_t jsize; /* tx jam size */ + volatile u_int32_t tx_pkt_max; /* tx maximum pkt size */ + volatile u_int32_t tx_pkt_min; /* tx minimum pkt size */ + volatile u_int32_t peak_attempt; /* tx peak counter */ + volatile u_int32_t dt_ctr; /* tx defer counter */ + volatile u_int32_t nc_ctr; /* tx normal collision cntr */ + volatile u_int32_t fc_ctr; /* tx first collision cntr */ + volatile u_int32_t ex_ctr; /* tx execess collision cntr */ + volatile u_int32_t lt_ctr; /* tx late collision cntr */ + volatile u_int32_t rand_seed; /* tx random seed */ + volatile u_int32_t tx_smachine; /* tx state machine */ + volatile u_int32_t _padding2[44]; /* reserved */ + volatile u_int32_t rx_swreset; /* rx software reset */ + volatile u_int32_t rx_cfg; /* rx configuration */ + volatile u_int32_t rx_pkt_max; /* rx maximum pkt size */ + volatile u_int32_t rx_pkt_min; /* rx minimum pkt size */ + volatile u_int32_t mac_addr2; /* macaddress register2 (MSB) */ + volatile u_int32_t mac_addr1; /* macaddress register1 */ + volatile u_int32_t mac_addr0; /* macaddress register0 (LSB) */ + volatile u_int32_t fr_ctr; /* rx frame counter */ + volatile u_int32_t gle_ctr; /* rx giant counter */ + volatile u_int32_t unale_ctr; /* rx unaligned error cntr */ + volatile u_int32_t rcrce_ctr; /* rx crc error cntr */ + volatile u_int32_t rx_smachine; /* rx state machine */ + volatile u_int32_t rx_cvalid; /* rx code violation */ + volatile u_int32_t _padding3; /* reserved */ + volatile u_int32_t htable3; /* hash table 3 */ + volatile u_int32_t htable2; /* hash table 2 */ + volatile u_int32_t htable1; /* hash table 1 */ + volatile u_int32_t htable0; /* hash table 0 */ + volatile u_int32_t afilter2; /* address filter 2 */ + volatile u_int32_t afilter1; /* address filter 1 */ + volatile u_int32_t afilter0; /* address filter 0 */ + volatile u_int32_t afilter_mask; /* address filter mask */ +}; + +/* BigMac XIF config register. */ +#define CR_XCFG_ODENABLE 0x00000001 /* Output driver enable */ +#define CR_XCFG_XLBACK 0x00000002 /* Loopback-mode XIF enable */ +#define CR_XCFG_MLBACK 0x00000004 /* Loopback-mode MII enable */ +#define CR_XCFG_MIIDISAB 0x00000008 /* MII receive buffer disable */ +#define CR_XCFG_SQENABLE 0x00000010 /* SQE test enable */ +#define CR_XCFG_SQETWIN 0x000003e0 /* SQE time window */ +#define CR_XCFG_LANCE 0x00000010 /* Lance mode enable */ +#define CR_XCFG_LIPG0 0x000003e0 /* Lance mode IPG0 */ + +/* BigMac transmit config register. */ +#define CR_TXCFG_ENABLE 0x00000001 /* Enable the transmitter */ +#define CR_TXCFG_SMODE 0x00000020 /* Enable slow transmit mode */ +#define CR_TXCFG_CIGN 0x00000040 /* Ignore transmit collisions */ +#define CR_TXCFG_FCSOFF 0x00000080 /* Do not emit FCS */ +#define CR_TXCFG_DBACKOFF 0x00000100 /* Disable backoff */ +#define CR_TXCFG_FULLDPLX 0x00000200 /* Enable full-duplex */ +#define CR_TXCFG_DGIVEUP 0x00000400 /* Don't give up on transmits */ + +/* BigMac receive config register. */ +#define CR_RXCFG_ENABLE 0x00000001 /* Enable the receiver */ +#define CR_RXCFG_PSTRIP 0x00000020 /* Pad byte strip enable */ +#define CR_RXCFG_PMISC 0x00000040 /* Enable promiscous mode */ +#define CR_RXCFG_DERR 0x00000080 /* Disable error checking */ +#define CR_RXCFG_DCRCS 0x00000100 /* Disable CRC stripping */ +#define CR_RXCFG_ME 0x00000200 /* Receive packets addressed to me */ +#define CR_RXCFG_PGRP 0x00000400 /* Enable promisc group mode */ +#define CR_RXCFG_HENABLE 0x00000800 /* Enable the hash filter */ +#define CR_RXCFG_AENABLE 0x00001000 /* Enable the address filter */ + +struct hme_tcvr { + volatile u_int32_t bb_clock; /* bit bang clock */ + volatile u_int32_t bb_data; /* bit bang data */ + volatile u_int32_t bb_oenab; /* bit bang output enable */ + volatile u_int32_t frame; /* frame control & data */ + volatile u_int32_t cfg; /* MIF configuration */ + volatile u_int32_t int_mask; /* MIF interrupt mask */ + volatile u_int32_t status; /* MIF status */ + volatile u_int32_t smachine; /* MIF state machine */ +}; + +#define FRAME_WRITE 0x50020000 /* start a frame write */ +#define FRAME_READ 0x60020000 /* start a frame read */ +#define TCVR_FAILURE 0x80000000 /* impossible value */ + +/* Transceiver config register */ +#define TCVR_CFG_PSELECT 0x00000001 /* select PHY */ +#define TCVR_CFG_PENABLE 0x00000002 /* enable MIF polling */ +#define TCVR_CFG_BENABLE 0x00000004 /* enable bit bang */ +#define TCVR_CFG_PREGADDR 0x000000f8 /* poll register addr */ +#define TCVR_CFG_MDIO0 0x00000100 /* MDIO zero, data/attached */ +#define TCVR_CFG_MDIO1 0x00000200 /* MDIO one, data/attached */ +#define TCVR_CFG_PDADDR 0x00007c00 /* device phy addr polling */ + +/* Here are some PHY addresses. */ +#define TCVR_PHYADDR_ETX 0 /* external transceiver */ +#define TCVR_PHYADDR_ITX 1 /* internal transceiver */ + +/* Transceiver status register */ +#define TCVR_STAT_BASIC 0xffff0000 /* The "basic" part */ +#define TCVR_STAT_NORMAL 0x0000ffff /* The "non-basic" part */ + +struct hme_rxd { + volatile u_int32_t rx_flags; + volatile u_int32_t rx_addr; +}; +#define RXFLAG_OWN 0x80000000 /* desc owner: 1=hw,0=sw */ +#define RXFLAG_OVERFLOW 0x40000000 /* 1 = buffer over flow */ +#define RXFLAG_SIZE 0x3fff0000 /* desciptor size */ +#define RXFLAG_CSUM 0x0000ffff /* checksum mask */ + +struct hme_txd { + volatile u_int32_t tx_flags; + volatile u_int32_t tx_addr; +}; +#define TXFLAG_OWN 0x80000000 /* desc owner: 1=hw,0=sw */ +#define TXFLAG_SOP 0x40000000 /* 1 = start of pkt */ +#define TXFLAG_EOP 0x20000000 /* 1 = end of pkt */ +#define TXFLAG_CSENABLE 0x10000000 /* 1 = use hw checksums */ +#define TXFLAG_CSLOCATION 0x0ff00000 /* checksum location mask */ +#define TXFLAG_CSBUFBEGIN 0x000fc000 /* checksum begin mask */ +#define TXFLAG_SIZE 0x00003fff /* pkt size mask */ + +#define RX_RING_SIZE 32 /* Must be 32, 64, 128, or 256 */ +#define TX_RING_SIZE 32 /* Must be 16<=x<=255, and divisible by 16 */ +#define RX_RING_MAX 256 /* maximum ring size: rx */ +#define TX_RING_MAX 256 /* maximum ring size: tx */ +#define RX_PKT_BUF_SZ 1600 /* size of a rx buffer */ +#define RX_OFFSET 2 /* packet offset */ +#define RX_CSUMLOC 0x00 /* checksum location */ +#define TX_PKT_BUF_SZ 1546 /* size of a tx buffer */ +#define RX_ALIGN_SIZE 64 /* rx packet buffers must align on 64 bytes */ +#define RX_ALIGN_MASK (~(RX_ALIGN_SIZE - 1)) + +struct hme_desc { + struct hme_rxd hme_rxd[RX_RING_MAX]; + struct hme_txd hme_txd[TX_RING_MAX]; +}; + +struct hme_bufs { + char rx_buf[RX_RING_SIZE][RX_PKT_BUF_SZ]; + char tx_buf[TX_RING_SIZE][TX_PKT_BUF_SZ]; +}; + +/* hme flags */ +#define HME_FLAG_POLL 0x00000001 /* polling mif? */ +#define HME_FLAG_FENABLE 0x00000002 /* MII frame enabled? */ +#define HME_FLAG_LANCE 0x00000004 /* Lance mode IPG0? */ +#define HME_FLAG_RXENABLE 0x00000008 /* Receiver enabled? */ +#define HME_FLAG_AUTO 0x00000010 /* Auto-Neg? 0 = force */ +#define HME_FLAG_FULL 0x00000020 /* Full duplex enabled? */ +#define HME_FLAG_MACFULL 0x00000040 /* Full duplex in the MAC? */ +#define HME_FLAG_POLLENABLE 0x00000080 /* Try MIF polling? */ +#define HME_FLAG_RXCV 0x00000100 /* RXCV enable - XXX */ +#define HME_FLAG_INIT 0x00000200 /* Initialized once? */ +#define HME_FLAG_LINKUP 0x00000400 /* Is link up? */ + +#define HME_FLAG_20_21 \ + (HME_FLAG_POLLENABLE | HME_FLAG_FENABLE) +#define HME_FLAG_NOT_A0 \ + (HME_FLAG_POLLENABLE | HME_FLAG_FENABLE | HME_FLAG_LANCE | HME_FLAG_RXCV) + +/* + * Copies of the hardware registers + */ +struct hme_swr { + u_int32_t bmcr; + u_int32_t bmsr; + u_int32_t phyidr1; + u_int32_t phyidr2; + u_int32_t anar; + u_int32_t anlpar; + u_int32_t aner; +}; + +/* + * Transceiver type + */ +#define HME_TCVR_EXTERNAL 0 +#define HME_TCVR_INTERNAL 1 +#define HME_TCVR_NONE 2 + +/* + * Autonegotiation time states + */ +#define HME_TIMER_DONE 0 /* not doing/waiting for anything */ +#define HME_TIMER_AUTONEG 1 /* autonegotiating */ +#define HME_TIMER_LINKUP 2 /* waiting for link up */ + +/* + * National Semiconductor DP83840A chip definitions + * Documentation available from National at: + * http://www.national.com/pf/DP/DP83840A.html + */ + +/* + * Register offsets + */ +#define DP83840_BMCR 0x00 /* Basic Mode Control Register */ +#define DP83840_BMSR 0x01 /* Basic Mode Status Register */ +#define DP83840_PHYIDR1 0x02 /* PHY Identifier Register #1 */ +#define DP83840_PHYIDR2 0x03 /* PHY Identifier Register #2 */ +#define DP83840_ANAR 0x04 /* Auto-Negotiation Advertisement Reg */ +#define DP83840_ANLPAR 0x05 /* Auto-Negotiation Partner Ability Reg */ +#define DP83840_ANER 0x06 /* Auto-Negotiation Expansion Register */ + /* 0x07 - 0x11 Reserved */ +#define DP83840_DCR 0x12 /* Disconnect Counter Register */ +#define DP83840_FCSCR 0x13 /* False Carrier Sense Counter Register */ + /* 0x14 Reserved */ +#define DP83840_RECR 0x15 /* Receive Error Counter Register */ +#define DP83840_SRR 0x16 /* Silicon Revision Register */ +#define DP83840_PCR 0x17 /* PCS Sub-Layer Configuration Register */ +#define DP83840_LBREMR 0x18 /* Loopback,Bypass,& Receiver Error Reg */ +#define DP83840_PAR 0x19 /* PHY Address Register */ + /* 0x1a Reserved */ +#define DP83840_10BTSR 0x1b /* 10BaseT Status Register */ +#define DP83840_10BTCR 0x1c /* 10BaseT Configuration Register */ + /* 0x1d - 0x1f Reserved */ + +/* + * Basic Mode Control Register (BMCR) + */ +#define BMCR_RESET 0x8000 /* Software reset */ +#define BMCR_LOOPBACK 0x4000 /* Lookback enable */ +#define BMCR_SPEED 0x2000 /* 1=100Mb, 0=10Mb */ +#define BMCR_ANE 0x1000 /* Auto-Negiation enable */ +#define BMCR_PDOWN 0x0800 /* power down the chip */ +#define BMCR_ISOLATE 0x0400 /* Isolate the chip */ +#define BMCR_RAN 0x0200 /* Restart autonegotiation */ +#define BMCR_DUPLEX 0x0100 /* 1=full, 0=half */ +#define BMCR_COLLISONTEST 0x0080 /* Create collisions on TX */ + +/* + * Basic Mode Status Register (BMSR) + */ +#define BMSR_100BASET4 0x8000 /* 100BaseT4 capable? */ +#define BMSR_100BASETX_FULL 0x4000 /* 100BaseTX full duplex? */ +#define BMSR_100BASETX_HALF 0x2000 /* 100BaseTX half duplex? */ +#define BMSR_10BASET_FULL 0x1000 /* 10BaseT full duplex? */ +#define BMSR_10BASET_HALF 0x0800 /* 10BaseT half duplex? */ +#define BMSR_ANCOMPLETE 0x0020 /* auto-negotiation complete? */ +#define BMSR_REMOTEFAULT 0x0010 /* Fault condition seen? */ +#define BMSR_ANC 0x0008 /* Can auto-negotiate? */ +#define BMSR_LINKSTATUS 0x0004 /* Link established? */ +#define BMSR_JABBER 0x0002 /* Jabber detected? */ +#define BMSR_EXTENDED 0x0001 /* Extended registers? */ + +/* + * Auto-Negotiation Advertisement Register (ANAR) + */ +#define ANAR_NP 0x8000 /* Next page indicator */ +#define ANAR_ACK 0x4000 /* Acknowledge */ +#define ANAR_RF 0x2000 /* Remote Fault */ +#define ANAR_RSRV12 0x1000 /* reserved */ +#define ANAR_RSRV11 0x0800 /* reserved */ +#define ANAR_RSRV10 0x0400 /* reserved */ +#define ANAR_T4 0x0200 /* 100BaseT4 support? */ +#define ANAR_TX_FD 0x0100 /* 100BaseTX full duplex? */ +#define ANAR_TX 0x0080 /* 100BaseTX half duplex? */ +#define ANAR_10_FD 0x0040 /* 10BaseT full duplex? */ +#define ANAR_10 0x0020 /* 10BaseT full duplex? */ +#define ANAR_SELECTOR 0x001f /* protocol selector */ + +/* + * Auto-Negotiation Link Partner Ability Register (ANLPAR) + */ +#define ANLPAR_NP 0x8000 /* Next page indictaion */ +#define ANLPAR_ACK 0x4000 /* Acknowledge */ +#define ANLPAR_RF 0x2000 /* Remote Fault */ +#define ANLPAR_RSRV12 0x1000 /* reserved */ +#define ANLPAR_RSRV11 0x0800 /* reserved */ +#define ANLPAR_RSRV10 0x0400 /* reserved */ +#define ANLPAR_T4 0x0200 /* 100BaseT4 support? */ +#define ANLPAR_TX_FD 0x0100 /* 100BaseTX full duplex? */ +#define ANLPAR_TX 0x0080 /* 100BaseTX half duplex? */ +#define ANLPAR_10_FD 0x0040 /* 10BaseT full duplex? */ +#define ANLPAR_10 0x0020 /* 10BaseT full duplex? */ +#define ANLPAR_SELECTOR 0x001f /* protocol selector */ + +/* + * Auto-Negotiation Expansion Register (ANER) + */ +#define ANER_RSRV15 0x8000 /* reserved */ +#define ANER_RSRV14 0x4000 /* reserved */ +#define ANER_RSRV13 0x2000 /* reserved */ +#define ANER_RSRV12 0x1000 /* reserved */ +#define ANER_RSRV11 0x0800 /* reserved */ +#define ANER_RSRV10 0x0400 /* reserved */ +#define ANER_RSRV09 0x0200 /* reserved */ +#define ANER_RSRV08 0x0100 /* reserved */ +#define ANER_RSRV07 0x0080 /* reserved */ +#define ANER_RSRV06 0x0040 /* reserved */ +#define ANER_RSRV05 0x0020 /* reserved */ +#define ANER_MLF 0x0010 /* Multiple link fault */ +#define ANER_LP_NP_ABLE 0x0008 /* Link partner next page-able */ +#define ANER_NP_ABLE 0x0004 /* Next page-able */ +#define ANER_PAGE_RX 0x0002 /* Link Code Word Page Received */ +#define ANER_LP_AN_ABLE 0x0001 /* Link partner auto-neg-able */ + +/* + * PCS Configuration Register (PCR) + */ +#define PCR_NRZI_EN 0x8000 /* NRZI coding enable */ +#define PCR_DESCR_TO_SEL 0x4000 /* Descrambler timeout select */ +#define PCR_DESCR_TO_DIS 0x2000 /* Descrambler timeout disable */ +#define PCR_REPEATER 0x1000 /* Repeater/Node Mode */ +#define PCR_ENCSEL 0x0800 /* Encoder Mode select */ +#define PCR_RSRV10 0x0400 /* reserved */ +#define PCR_RSRV09 0x0200 /* reserved */ +#define PCR_RSRV08 0x0100 /* reserved */ +#define PCR_CLK25MDIS 0x0080 /* CLK25M disbable */ +#define PCR_F_LINK_100 0x0040 /* Force good link in 100Mb/s */ +#define PCR_CIM_DIS 0x0020 /* Carrier integ. mon. disable */ +#define PCR_TX_OFF 0x0010 /* Force transmit off */ +#define PCR_RSRV03 0x0008 /* reserved */ +#define PCR_LED1_MODE 0x0004 /* Led1 mode select */ +#define PCR_LED4_MODE 0x0002 /* Led4 mode select */ +#define PCR_RSRV00 0x0001 /* reserved */ + +/* + * Loopback, bypass, and receiver error mask register (LBREMR) + */ +#define LBREMR_BAD_SSD_EN 0x8000 /* BAD SSD enable */ +#define LBREMR_BP_4B5B 0x4000 /* Bypass 4B5B and 5B4B enc/dec */ +#define LBREMR_BP_SCR 0x2000 /* Bypass scramble/descramble */ +#define LBREMR_BP_ALIGN 0x1000 /* Bypass symbol alignment func */ +#define LBREMR_10BT_LPBK 0x0800 /* 10BaseT enc/dec loopback */ +#define LBREMR_RSRV10 0x0400 /* reserved */ +#define LBREMR_LB1 0x0200 /* Loopback control bit #2 */ +#define LBREMR_LB0 0x0100 /* Loopback control bit #1 */ +#define LBREMR_RSRV07 0x0080 /* reserved */ +#define LBREMR_ALT_CRS 0x0040 /* Alternate CRS operation */ +#define LBREMR_LBK_XMT_DIS 0x0020 /* 100Mb/s tx disable in loop */ +#define LBREMR_CODE_ERR 0x0010 /* Code errors */ +#define LBREMR_PE_ERR 0x0008 /* Premature end errors */ +#define LBREMR_LINK_ERR 0x0004 /* Link errors */ +#define LBREMR_PKT_ERR 0x0002 /* Packet errors */ +#define LBREMR_RSRV00 0x0001 /* reserved */ + +/* + * PHY Address Register (PAR) + */ +#define PAR_RSRV15 0x8000 /* reserved */ +#define PAR_RSRV14 0x4000 /* reserved */ +#define PAR_RSRV13 0x2000 /* reserved */ +#define PAR_RSRV12 0x1000 /* reserved */ +#define PAR_DIS_CRS_JAB 0x0800 /* Disable CS during jabber */ +#define PAR_AN_EN_STAT 0x0400 /* Auto-Neg. mode status */ +#define PAR_RSRV09 0x0200 /* reserved */ +#define PAR_FEFI_EN 0x0100 /* Far end fault status enable */ +#define PAR_DUPLEX_STAT 0x0080 /* 1=full duplex, 0=half duplex */ +#define PAR_SPEED_10 0x0040 /* 1=10Mb/s, 0=100Mb/s */ +#define PAR_CIM_STATUS 0x0020 /* Carrier integrity mon. stat */ +#define PAR_PHYADDR4 0x0010 /* physical address bit 4 */ +#define PAR_PHYADDR3 0x0008 /* physical address bit 3 */ +#define PAR_PHYADDR2 0x0004 /* physical address bit 2 */ +#define PAR_PHYADDR1 0x0002 /* physical address bit 1 */ +#define PAR_PHYADDR0 0x0001 /* physical address bit 0 */ + +/* + * 10BaseT status register (TENBTSR) + */ +#define TENBTSR10BT_SER 0x0200 /* 10BaseT Serial mode? */ + +/* + * 10BaseT configuration register (TENBTCR) + */ +#define TENBTCR_LP_EN 0x0020 /* Link pulse enable */ +#define TENBTCR_HBE 0x0010 /* Heartbeat enable */ +#define TENBTCR_UTP_STP 0x0008 /* 1=UTP, 0=STP */ +#define TENBTCR_LSS 0x0004 /* Low squelch select */ +#define TENBTCR_RSRV01 0x0002 /* reserved */ +#define TENBTCR_JABEN 0x0001 /* Jabber enable */ diff --git a/sys/arch/sparc/dev/hmevar.h b/sys/arch/sparc/dev/hmevar.h new file mode 100644 index 00000000000..cfc44033062 --- /dev/null +++ b/sys/arch/sparc/dev/hmevar.h @@ -0,0 +1,68 @@ +/* $OpenBSD: hmevar.h,v 1.1 1998/07/10 19:09:12 jason Exp $ */ + +/* + * Copyright (c) 1998 Jason L. Wright (jason@thought.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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Jason L. Wright + * 4. 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. + */ + +struct hme_softc { + struct device sc_dev; /* base device */ + struct arpcom sc_arpcom; /* ethernet common */ + struct sbusdev sc_sd; /* sbus device */ + struct intrhand sc_ih; /* interrupt vectoring */ + int sc_node; /* which sbus node */ + + /* + * Register sets + */ + struct hme_gr *sc_gr; /* global registers */ + struct hme_txr *sc_txr; /* transmitter regs */ + struct hme_rxr *sc_rxr; /* receiver registers */ + struct hme_cr *sc_cr; /* configuration registers */ + struct hme_tcvr *sc_tcvr; /* MIF registers */ + + struct hme_swr sc_sw; /* software copy registers */ + int sc_burst; /* DMA burst size in effect */ + int sc_rev; /* Card revision */ + + u_int32_t sc_flags; /* status flags */ + u_int32_t sc_promisc; /* are we promiscuous? */ + u_int32_t sc_phyaddr; /* PHY addr */ + int sc_an_state; /* state of negotiation */ + int sc_an_ticks; /* how long has passed? */ + int sc_tcvr_type; /* transceiver type */ + + /* + * RX/TX ring buffers, descriptors, and counters + */ + struct hme_desc *sc_desc, *sc_desc_dva; /* descriptors */ + struct hme_bufs *sc_bufs, *sc_bufs_dva; /* buffers */ + int sc_first_td, sc_last_td, sc_no_td; /* tx counters */ + int sc_last_rd; /* rx counter */ +}; |