/* $OpenBSD: if_mc.c,v 1.10 2007/04/22 22:31:14 deraadt Exp $ */ /* $NetBSD: if_mc.c,v 1.9.16.1 2006/06/21 14:53:13 yamt Exp $ */ /*- * Copyright (c) 1997 David Huang * All rights reserved. * * Portions of this code are based on code by Denton Gentry * and Yanagisawa Takeshi . * * 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. * */ /* * AMD AM79C940 (MACE) driver with DBDMA bus attachment and DMA routines * for onboard ethernet found on most old world macs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef INET #include #include #include #include #include #endif #include "bpfilter.h" #if NBPFILTER > 0 #include #include #endif #include #include #include #include #include #define MC_REGSPACING 16 #define MC_REGSIZE MACE_NREGS * MC_REGSPACING #define MACE_REG(x) ((x)*MC_REGSPACING) #define MACE_BUFLEN 2048 #define MACE_TXBUFS 2 #define MACE_RXBUFS 8 #define MC_RXDMABUFS 4 #define MACE_BUFSZ ((MACE_RXBUFS + MACE_TXBUFS + 2) * MACE_BUFLEN) #define NIC_GET(sc, reg) (in8rb(sc->sc_reg + MACE_REG(reg))) #define NIC_PUT(sc, reg, val) (out8rb(sc->sc_reg + MACE_REG(reg), (val))) /* * AMD MACE (Am79C940) register definitions */ #define MACE_RCVFIFO 0 /* Receive FIFO [15-00] (read only) */ #define MACE_XMTFIFO 1 /* Transmit FIFO [15-00] (write only) */ #define MACE_XMTFC 2 /* Transmit Frame Control (read/write) */ #define MACE_XMTFS 3 /* Transmit Frame Status (read only) */ #define MACE_XMTRC 4 /* Transmit Retry Count (read only) */ #define MACE_RCVFC 5 /* Receive Frame Control (read/write) */ #define MACE_RCVFS 6 /* Receive Frame Status (4 bytes) (read only) */ #define MACE_FIFOFC 7 /* FIFO Frame Count (read only) */ #define MACE_IR 8 /* Interrupt Register (read only) */ #define MACE_IMR 9 /* Interrupt Mask Register (read/write) */ #define MACE_PR 10 /* Poll Register (read only) */ #define MACE_BIUCC 11 /* BIU Configuration Control (read/write) */ #define MACE_FIFOCC 12 /* FIFO Configuration Control (read/write) */ #define MACE_MACCC 13 /* MAC Configuration Control (read/write) */ #define MACE_PLSCC 14 /* PLS Configuration Control (read/write) */ #define MACE_PHYCC 15 /* PHY Confiuration Control (read/write) */ #define MACE_CHIPIDL 16 /* Chip ID Register [07-00] (read only) */ #define MACE_CHIPIDH 17 /* Chip ID Register [15-08] (read only) */ #define MACE_IAC 18 /* Internal Address Configuration (read/write) */ /* RESERVED 19 Reserved (read/write as 0) */ #define MACE_LADRF 20 /* Logical Address Filter (8 bytes) (read/write) */ #define MACE_PADR 21 /* Physical Address (6 bytes) (read/write) */ /* RESERVED 22 Reserved (read/write as 0) */ /* RESERVED 23 Reserved (read/write as 0) */ #define MACE_MPC 24 /* Missed Packet Count (read only) */ /* RESERVED 25 Reserved (read/write as 0) */ #define MACE_RNTPC 26 /* Runt Packet Count (read only) */ #define MACE_RCVCC 27 /* Receive Collision Count (read only) */ /* RESERVED 28 Reserved (read/write as 0) */ #define MACE_UTR 29 /* User Test Register (read/write) */ #define MACE_RTR1 30 /* Reserved Test Register 1 (read/write as 0) */ #define MACE_RTR2 31 /* Reserved Test Register 2 (read/write as 0) */ #define MACE_NREGS 32 /* 2: Transmit Frame Control (XMTFC) */ #define DRTRY 0x80 /* Disable Retry */ #define DXMTFCS 0x08 /* Disable Transmit FCS */ #define APADXMT 0x01 /* Auto Pad Transmit */ /* 3: Transmit Frame Status (XMTFS) */ #define XMTSV 0x80 /* Transmit Status Valid */ #define UFLO 0x40 /* Underflow */ #define LCOL 0x20 /* Late Collision */ #define MORE 0x10 /* More than one retry needed */ #define ONE 0x08 /* Exactly one retry needed */ #define DEFER 0x04 /* Transmission deferred */ #define LCAR 0x02 /* Loss of Carrier */ #define RTRY 0x01 /* Retry Error */ /* 4: Transmit Retry Count (XMTRC) */ #define EXDEF 0x80 /* Excessive Defer */ #define XMTRC 0x0f /* Transmit Retry Count */ /* 5: Receive Frame Control (RCVFC) */ #define LLRCV 0x08 /* Low Latency Receive */ #define MR 0x04 /* Match/Reject */ #define ASTRPRCV 0x01 /* Auto Strip Receive */ /* 6: Receive Frame Status (RCVFS) */ /* 4 byte register; read 4 times to get all of the bytes */ /* Read 1: RFS0 - Receive Message Byte Count [7-0] (RCVCNT) */ /* Read 2: RFS1 - Receive Status (RCVSTS) */ #define OFLO 0x80 /* Overflow flag */ #define CLSN 0x40 /* Collision flag */ #define FRAM 0x20 /* Framing Error flag */ #define FCS 0x10 /* FCS Error flag */ #define RCVCNT 0x0f /* Receive Message Byte Count [11-8] */ /* Read 3: RFS2 - Runt Packet Count (RNTPC) [7-0] */ /* Read 4: RFS3 - Receive Collision Count (RCVCC) [7-0] */ /* 7: FIFO Frame Count (FIFOFC) */ #define RCVFC 0xf0 /* Receive Frame Count */ #define XMTFC 0x0f /* Transmit Frame Count */ /* 8: Interrupt Register (IR) */ #define JAB 0x80 /* Jabber Error */ #define BABL 0x40 /* Babble Error */ #define CERR 0x20 /* Collision Error */ #define RCVCCO 0x10 /* Receive Collision Count Overflow */ #define RNTPCO 0x08 /* Runt Packet Count Overflow */ #define MPCO 0x04 /* Missed Packet Count Overflow */ #define RCVINT 0x02 /* Receive Interrupt */ #define XMTINT 0x01 /* Transmit Interrupt */ /* 9: Interrut Mask Register (IMR) */ #define JABM 0x80 /* Jabber Error Mask */ #define BABLM 0x40 /* Babble Error Mask */ #define CERRM 0x20 /* Collision Error Mask */ #define RCVCCOM 0x10 /* Receive Collision Count Overflow Mask */ #define RNTPCOM 0x08 /* Runt Packet Count Overflow Mask */ #define MPCOM 0x04 /* Missed Packet Count Overflow Mask */ #define RCVINTM 0x02 /* Receive Interrupt Mask */ #define XMTINTM 0x01 /* Transmit Interrupt Mask */ /* 10: Poll Register (PR) */ #define XMTSV 0x80 /* Transmit Status Valid */ #define TDTREQ 0x40 /* Transmit Data Transfer Request */ #define RDTREQ 0x20 /* Receive Data Transfer Request */ /* 11: BIU Configuration Control (BIUCC) */ #define BSWP 0x40 /* Byte Swap */ #define XMTSP 0x30 /* Transmit Start Point */ #define XMTSP_4 0x00 /* 4 bytes */ #define XMTSP_16 0x10 /* 16 bytes */ #define XMTSP_64 0x20 /* 64 bytes */ #define XMTSP_112 0x30 /* 112 bytes */ #define SWRST 0x01 /* Software Reset */ /* 12: FIFO Configuration Control (FIFOCC) */ #define XMTFW 0xc0 /* Transmit FIFO Watermark */ #define XMTFW_8 0x00 /* 8 write cycles */ #define XMTFW_16 0x40 /* 16 write cycles */ #define XMTFW_32 0x80 /* 32 write cycles */ #define RCVFW 0x30 /* Receive FIFO Watermark */ #define RCVFW_16 0x00 /* 16 bytes */ #define RCVFW_32 0x10 /* 32 bytes */ #define RCVFW_64 0x20 /* 64 bytes */ #define XMTFWU 0x08 /* Transmit FIFO Watermark Update */ #define RCVFWU 0x04 /* Receive FIFO Watermark Update */ #define XMTBRST 0x02 /* Transmit Burst */ #define RCVBRST 0x01 /* Receive Burst */ /* 13: MAC Configuration (MACCC) */ #define PROM 0x80 /* Promiscuous */ #define DXMT2PD 0x40 /* Disable Transmit Two Part Deferral */ #define EMBA 0x20 /* Enable Modified Back-off Algorithm */ #define DRCVPA 0x08 /* Disable Receive Physical Address */ #define DRCVBC 0x04 /* Disable Receive Broadcast */ #define ENXMT 0x02 /* Enable Transmit */ #define ENRCV 0x01 /* Enable Receive */ /* 14: PLS Configuration Control (PLSCC) */ #define XMTSEL 0x08 /* Transmit Mode Select */ #define PORTSEL 0x06 /* Port Select */ #define PORTSEL_AUI 0x00 /* Select AUI */ #define PORTSEL_10BT 0x02 /* Select 10BASE-T */ #define PORTSEL_DAI 0x04 /* Select DAI port */ #define PORTSEL_GPSI 0x06 /* Select GPSI */ #define ENPLSIO 0x01 /* Enable PLS I/O */ /* 15: PHY Configuration (PHYCC) */ #define LNKFL 0x80 /* Link Fail */ #define DLNKTST 0x40 /* Disable Link Test */ #define REVPOL 0x20 /* Reversed Polarity */ #define DAPC 0x10 /* Disable Auto Polarity Correction */ #define LRT 0x08 /* Low Receive Threshold */ #define ASEL 0x04 /* Auto Select */ #define RWAKE 0x02 /* Remote Wake */ #define AWAKE 0x01 /* Auto Wake */ /* 18: Internal Address Configuration (IAC) */ #define ADDRCHG 0x80 /* Address Change */ #define PHYADDR 0x04 /* Physical Address Reset */ #define LOGADDR 0x02 /* Logical Address Reset */ /* 28: User Test Register (UTR) */ #define RTRE 0x80 /* Reserved Test Register Enable */ #define RTRD 0x40 /* Reserved Test Register Disable */ #define RPA 0x20 /* Run Packet Accept */ #define FCOLL 0x10 /* Force Collision */ #define RCVFCSE 0x08 /* Receive FCS Enable */ #define LOOP 0x06 /* Loopback Control */ #define LOOP_NONE 0x00 /* No Loopback */ #define LOOP_EXT 0x02 /* External Loopback */ #define LOOP_INT 0x04 /* Internal Loopback, excludes MENDEC */ #define LOOP_INT_MENDEC 0x06 /* Internal Loopback, includes MENDEC */ struct mc_rxframe { u_int8_t rx_rcvcnt; u_int8_t rx_rcvsts; u_int8_t rx_rntpc; u_int8_t rx_rcvcc; u_char *rx_frame; }; struct mc_softc { struct device sc_dev; /* base device glue */ struct arpcom sc_arpcom; /* Ethernet common part */ struct timeout sc_tick_ch; struct mc_rxframe sc_rxframe; u_int8_t sc_biucc; u_int8_t sc_fifocc; u_int8_t sc_plscc; u_int8_t sc_enaddr[6]; u_int8_t sc_pad[2]; int sc_havecarrier; /* carrier status */ char *sc_reg; bus_dma_tag_t sc_dmat; bus_dmamap_t sc_bufmap; bus_dma_segment_t sc_bufseg[1]; dbdma_regmap_t *sc_txdma; dbdma_regmap_t *sc_rxdma; dbdma_command_t *sc_txdmacmd; dbdma_command_t *sc_rxdmacmd; dbdma_t sc_txdbdma; dbdma_t sc_rxdbdma; caddr_t sc_txbuf; caddr_t sc_rxbuf; paddr_t sc_txbuf_pa; paddr_t sc_rxbuf_pa; int sc_tail; int sc_rxset; int sc_txset; int sc_txseti; }; int mc_match(struct device *, void *, void *); void mc_attach(struct device *, struct device *, void *); struct cfattach mc_ca = { sizeof(struct mc_softc), mc_match, mc_attach }; struct cfdriver mc_cd = { NULL, "mc", DV_IFNET }; void mc_init(struct mc_softc *sc); void mc_put(struct mc_softc *sc, u_int len); int mc_dmaintr(void *arg); void mc_reset_rxdma(struct mc_softc *sc); void mc_reset_txdma(struct mc_softc *sc); int mc_stop(struct mc_softc *sc); int mc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data); void mc_start(struct ifnet *ifp); void mc_reset(struct mc_softc *sc); void mc_tint(struct mc_softc *sc); void mc_rint(struct mc_softc *sc); int mc_intr(void *); void mc_watchdog(struct ifnet *ifp); u_int maceput(struct mc_softc *sc, struct mbuf *); void mace_read(struct mc_softc *, caddr_t, int); struct mbuf *mace_get(struct mc_softc *, caddr_t, int); static void mace_calcladrf(struct mc_softc *, u_int8_t *); void mc_putpacket(struct mc_softc *, u_int); int mc_match(struct device *parent, void *arg, void *aux) { struct confargs *ca = aux; if (strcmp(ca->ca_name, "mace") != 0) return 0; /* requires 6 regs */ if (ca->ca_nreg / sizeof(int) != 6) return 0; /* requires 3 intrs */ if (ca->ca_nintr / sizeof(int) != 3) return 0; return 1; } void mc_attach(struct device *parent, struct device *self, void *aux) { struct confargs *ca = aux; struct mc_softc *sc = (struct mc_softc *)self; struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_int8_t lladdr[ETHER_ADDR_LEN]; int nseg, error; if (OF_getprop(ca->ca_node, "local-mac-address", lladdr, ETHER_ADDR_LEN) != ETHER_ADDR_LEN) { printf(": failed to get MAC address.\n"); return; } ca->ca_reg[0] += ca->ca_baseaddr; ca->ca_reg[2] += ca->ca_baseaddr; ca->ca_reg[4] += ca->ca_baseaddr; if ((sc->sc_reg = mapiodev(ca->ca_reg[0], ca->ca_reg[1])) == NULL) { printf(": cannot map registers\n"); return; } sc->sc_dmat = ca->ca_dmat; sc->sc_tail = 0; if ((sc->sc_txdma = mapiodev(ca->ca_reg[2], ca->ca_reg[3])) == NULL) { printf(": cannot map TX DMA registers\n"); goto notxdma; } if ((sc->sc_rxdma = mapiodev(ca->ca_reg[4], ca->ca_reg[5])) == NULL) { printf(": cannot map RX DMA registers\n"); goto norxdma; } if ((sc->sc_txdbdma = dbdma_alloc(sc->sc_dmat, 2)) == NULL) { printf(": cannot alloc TX DMA descriptors\n"); goto notxdbdma; } sc->sc_txdmacmd = sc->sc_txdbdma->d_addr; if ((sc->sc_rxdbdma = dbdma_alloc(sc->sc_dmat, 8 + 1)) == NULL) { printf(": cannot alloc RX DMA descriptors\n"); goto norxdbdma; } sc->sc_rxdmacmd = sc->sc_rxdbdma->d_addr; if ((error = bus_dmamem_alloc(sc->sc_dmat, MACE_BUFSZ, PAGE_SIZE, 0, sc->sc_bufseg, 1, &nseg, BUS_DMA_NOWAIT))) { printf(": cannot allocate DMA mem (%d)\n", error); goto nodmamem; } if ((error = bus_dmamem_map(sc->sc_dmat, sc->sc_bufseg, nseg, MACE_BUFSZ, &sc->sc_txbuf, BUS_DMA_NOWAIT))) { printf(": cannot map DMA mem (%d)\n", error); goto nodmamap; } if ((error = bus_dmamap_create(sc->sc_dmat, MACE_BUFSZ, 1, MACE_BUFSZ, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &sc->sc_bufmap))) { printf(": cannot create DMA map (%d)\n", error); goto nodmacreate; } if ((error = bus_dmamap_load(sc->sc_dmat, sc->sc_bufmap, sc->sc_txbuf, MACE_BUFSZ, NULL, BUS_DMA_NOWAIT))) { printf(": cannot load DMA map (%d)\n", error); goto nodmaload; } sc->sc_txbuf_pa = sc->sc_bufmap->dm_segs->ds_addr; sc->sc_rxbuf = sc->sc_txbuf + MACE_BUFLEN * MACE_TXBUFS; sc->sc_rxbuf_pa = sc->sc_txbuf_pa + MACE_BUFLEN * MACE_TXBUFS; printf(": irq %d,%d,%d", ca->ca_intr[0], ca->ca_intr[1], ca->ca_intr[2]); /* disable receive DMA */ dbdma_reset(sc->sc_rxdma); /* disable transmit DMA */ dbdma_reset(sc->sc_txdma); /* install interrupt handlers */ mac_intr_establish(parent, ca->ca_intr[2], IST_LEVEL, IPL_NET, mc_dmaintr, sc, sc->sc_dev.dv_xname); mac_intr_establish(parent, ca->ca_intr[0], IST_LEVEL, IPL_NET, mc_intr, sc, sc->sc_dev.dv_xname); sc->sc_biucc = XMTSP_64; sc->sc_fifocc = XMTFW_16 | RCVFW_64 | XMTFWU | RCVFWU | XMTBRST | RCVBRST; sc->sc_plscc = PORTSEL_GPSI | ENPLSIO; /* reset the chip and disable all interrupts */ NIC_PUT(sc, MACE_BIUCC, SWRST); DELAY(100); NIC_PUT(sc, MACE_IMR, ~0); bcopy(lladdr, sc->sc_enaddr, ETHER_ADDR_LEN); bcopy(sc->sc_enaddr, sc->sc_arpcom.ac_enaddr, ETHER_ADDR_LEN); printf(": address %s\n", ether_sprintf(lladdr)); bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); ifp->if_softc = sc; ifp->if_ioctl = mc_ioctl; ifp->if_start = mc_start; ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_MULTICAST; ifp->if_watchdog = mc_watchdog; ifp->if_timer = 0; IFQ_SET_READY(&ifp->if_snd); if_attach(ifp); ether_ifattach(ifp); return; nodmaload: bus_dmamap_destroy(sc->sc_dmat, sc->sc_bufmap); nodmacreate: bus_dmamem_unmap(sc->sc_dmat, sc->sc_txbuf, MACE_BUFSZ); nodmamap: bus_dmamem_free(sc->sc_dmat, sc->sc_bufseg, 1); nodmamem: dbdma_free(sc->sc_rxdbdma); norxdbdma: dbdma_free(sc->sc_txdbdma); notxdbdma: unmapiodev((void *)sc->sc_rxdma, ca->ca_reg[5]); norxdma: unmapiodev((void *)sc->sc_txdma, ca->ca_reg[3]); notxdma: unmapiodev(sc->sc_reg, ca->ca_reg[1]); } int mc_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) { struct mc_softc *sc = ifp->if_softc; struct ifaddr *ifa = (struct ifaddr *)data; struct ifreq *ifr; int s = splnet(), err = 0; switch (cmd) { case SIOCSIFADDR: ifp->if_flags |= IFF_UP; if (!(ifp->if_flags & IFF_RUNNING)) mc_init(sc); #ifdef INET if (ifa->ifa_addr->sa_family == AF_INET) arp_ifinit(&sc->sc_arpcom, ifa); #endif break; case SIOCSIFFLAGS: 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. */ mc_stop(sc); } 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. */ mc_init(sc); } else { /* * reset the interface to pick up any other changes * in flags */ mc_reset(sc); mc_start(ifp); } break; case SIOCADDMULTI: case SIOCDELMULTI: ifr = (struct ifreq *) data; err = (cmd == SIOCADDMULTI) ? ether_addmulti(ifr, &sc->sc_arpcom) : ether_delmulti(ifr, &sc->sc_arpcom); if (err == ENETRESET) { /* * Multicast list has changed; set the hardware * filter accordingly. But remember UP flag! */ if (ifp->if_flags & IFF_RUNNING) mc_reset(sc); err = 0; } break; default: err = EINVAL; } splx(s); return (err); } /* * Encapsulate a packet of type family for the local net. */ void mc_start(struct ifnet *ifp) { struct mc_softc *sc = ifp->if_softc; struct mbuf *m; if ((ifp->if_flags & (IFF_RUNNING | IFF_OACTIVE)) != IFF_RUNNING) return; while (1) { if (ifp->if_flags & IFF_OACTIVE) return; IFQ_DEQUEUE(&ifp->if_snd, m); if (m == NULL) return; #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, BPF_DIRECTION_OUT); #endif /* * Copy the mbuf chain into the transmit buffer. */ ifp->if_flags |= IFF_OACTIVE; maceput(sc, m); ifp->if_opackets++; /* # of pkts */ } } /* * reset and restart the MACE. Called in case of fatal * hardware/software errors. */ void mc_reset(struct mc_softc *sc) { mc_stop(sc); mc_init(sc); } void mc_init(struct mc_softc *sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_int8_t maccc, ladrf[8]; int s, i; s = splnet(); NIC_PUT(sc, MACE_BIUCC, sc->sc_biucc); NIC_PUT(sc, MACE_FIFOCC, sc->sc_fifocc); NIC_PUT(sc, MACE_IMR, ~0); /* disable all interrupts */ NIC_PUT(sc, MACE_PLSCC, sc->sc_plscc); NIC_PUT(sc, MACE_UTR, RTRD); /* disable reserved test registers */ /* set MAC address */ NIC_PUT(sc, MACE_IAC, ADDRCHG); while (NIC_GET(sc, MACE_IAC) & ADDRCHG) ; NIC_PUT(sc, MACE_IAC, PHYADDR); for (i = 0; i < ETHER_ADDR_LEN; i++) out8rb(sc->sc_reg + MACE_REG(MACE_PADR) + i, sc->sc_enaddr[i]); /* set logical address filter */ mace_calcladrf(sc, ladrf); NIC_PUT(sc, MACE_IAC, ADDRCHG); while (NIC_GET(sc, MACE_IAC) & ADDRCHG) ; NIC_PUT(sc, MACE_IAC, LOGADDR); for (i = 0; i < 8; i++) out8rb(sc->sc_reg + MACE_REG(MACE_LADRF) + i, ladrf[i]); NIC_PUT(sc, MACE_XMTFC, APADXMT); /* * No need to autostrip padding on receive... Ethernet frames * don't have a length field, unlike 802.3 frames, so the MACE * can't figure out the length of the packet anyways. */ NIC_PUT(sc, MACE_RCVFC, 0); maccc = ENXMT | ENRCV; if (ifp->if_flags & IFF_PROMISC) maccc |= PROM; NIC_PUT(sc, MACE_MACCC, maccc); mc_reset_rxdma(sc); mc_reset_txdma(sc); /* * Enable all interrupts except receive, since we use the DMA * completion interrupt for that. */ NIC_PUT(sc, MACE_IMR, RCVINTM); /* flag interface as "running" */ ifp->if_flags |= IFF_RUNNING; ifp->if_flags &= ~IFF_OACTIVE; splx(s); } /* * Close down an interface and free its buffers. * Called on final close of device, or if mcinit() fails * part way through. */ int mc_stop(struct mc_softc *sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; int s; s = splnet(); NIC_PUT(sc, MACE_BIUCC, SWRST); DELAY(100); ifp->if_timer = 0; ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); splx(s); return (0); } /* * Called if any Tx packets remain unsent after 5 seconds, * In all cases we just reset the chip, and any retransmission * will be handled by higher level protocol timeouts. */ void mc_watchdog(struct ifnet *ifp) { struct mc_softc *sc = ifp->if_softc; printf("mcwatchdog: resetting chip\n"); mc_reset(sc); } int mc_intr(void *arg) { struct mc_softc *sc = arg; struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_int8_t ir; ir = NIC_GET(sc, MACE_IR) & ~NIC_GET(sc, MACE_IMR); if (ir & JAB) { #ifdef MCDEBUG printf("%s: jabber error\n", sc->sc_dev.dv_xname); #endif ifp->if_oerrors++; } if (ir & BABL) { #ifdef MCDEBUG printf("%s: babble\n", sc->sc_dev.dv_xname); #endif ifp->if_oerrors++; } if (ir & CERR) { #ifdef MCDEBUG printf("%s: collision error\n", sc->sc_dev.dv_xname); #endif ifp->if_collisions++; } /* * Pretend we have carrier; if we don't this will be cleared * shortly. */ sc->sc_havecarrier = 1; if (ir & XMTINT) mc_tint(sc); if (ir & RCVINT) mc_rint(sc); return(1); } void mc_tint(struct mc_softc *sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; u_int8_t xmtrc, xmtfs; xmtrc = NIC_GET(sc, MACE_XMTRC); xmtfs = NIC_GET(sc, MACE_XMTFS); if ((xmtfs & XMTSV) == 0) return; if (xmtfs & UFLO) { printf("%s: underflow\n", sc->sc_dev.dv_xname); mc_reset(sc); return; } if (xmtfs & LCOL) { printf("%s: late collision\n", sc->sc_dev.dv_xname); ifp->if_oerrors++; ifp->if_collisions++; } if (xmtfs & MORE) /* Real number is unknown. */ ifp->if_collisions += 2; else if (xmtfs & ONE) ifp->if_collisions++; else if (xmtfs & RTRY) { printf("%s: excessive collisions\n", sc->sc_dev.dv_xname); ifp->if_collisions += 16; ifp->if_oerrors++; } if (xmtfs & LCAR) { sc->sc_havecarrier = 0; printf("%s: lost carrier\n", sc->sc_dev.dv_xname); ifp->if_oerrors++; } ifp->if_flags &= ~IFF_OACTIVE; ifp->if_timer = 0; mc_start(ifp); } void mc_rint(struct mc_softc *sc) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; #define rxf sc->sc_rxframe u_int len; len = (rxf.rx_rcvcnt | ((rxf.rx_rcvsts & 0xf) << 8)) - 4; #ifdef MCDEBUG if (rxf.rx_rcvsts & 0xf0) printf("%s: rcvcnt %02x rcvsts %02x rntpc 0x%02x rcvcc 0x%02x\n", sc->sc_dev.dv_xname, rxf.rx_rcvcnt, rxf.rx_rcvsts, rxf.rx_rntpc, rxf.rx_rcvcc); #endif if (rxf.rx_rcvsts & OFLO) { #ifdef MCDEBUG printf("%s: receive FIFO overflow\n", sc->sc_dev.dv_xname); #endif ifp->if_ierrors++; return; } if (rxf.rx_rcvsts & CLSN) ifp->if_collisions++; if (rxf.rx_rcvsts & FRAM) { #ifdef MCDEBUG printf("%s: framing error\n", sc->sc_dev.dv_xname); #endif ifp->if_ierrors++; return; } if (rxf.rx_rcvsts & FCS) { #ifdef MCDEBUG printf("%s: frame control checksum error\n", sc->sc_dev.dv_xname); #endif ifp->if_ierrors++; return; } mace_read(sc, rxf.rx_frame, len); #undef rxf } /* * stuff packet into MACE (at splnet) */ u_int maceput(struct mc_softc *sc, struct mbuf *m) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct mbuf *n; u_int len, totlen = 0; u_char *buff; buff = sc->sc_txbuf; for (; m; m = n) { u_char *data = mtod(m, u_char *); len = m->m_len; totlen += len; bcopy(data, buff, len); buff += len; MFREE(m, n); } if (totlen > PAGE_SIZE) panic("%s: maceput: packet overflow", sc->sc_dev.dv_xname); #if 0 if (totlen < ETHERMIN + sizeof(struct ether_header)) { int pad = ETHERMIN + sizeof(struct ether_header) - totlen; bzero(sc->sc_txbuf + totlen, pad); totlen = ETHERMIN + sizeof(struct ether_header); } #endif /* 5 seconds to watch for failing to transmit */ ifp->if_timer = 5; mc_putpacket(sc, totlen); return (totlen); } void mace_read(struct mc_softc *sc, caddr_t pkt, int len) { struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct mbuf *m; if (len <= sizeof(struct ether_header) || len > ETHERMTU + sizeof(struct ether_header)) { #ifdef MCDEBUG printf("%s: invalid packet size %d; dropping\n", sc->sc_dev.dv_xname, len); #endif ifp->if_ierrors++; return; } m = mace_get(sc, pkt, len); if (m == NULL) { ifp->if_ierrors++; return; } ifp->if_ipackets++; #if NBPFILTER > 0 /* Pass the packet to any BPF listeners. */ if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); #endif /* Pass the packet up. */ ether_input_mbuf(ifp, m); } /* * Pull data off an interface. * Len is 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. */ struct mbuf * mace_get(struct mc_softc *sc, caddr_t pkt, int totlen) { struct mbuf *m; struct mbuf *top, **mp; int len; MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return (NULL); m->m_pkthdr.rcvif = &sc->sc_arpcom.ac_if; m->m_pkthdr.len = totlen; len = MHLEN; top = 0; mp = ⊤ while (totlen > 0) { if (top) { MGET(m, M_DONTWAIT, MT_DATA); if (m == NULL) { m_freem(top); return (NULL); } len = MLEN; } if (totlen >= MINCLSIZE) { MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_free(m); m_freem(top); return (NULL); } len = MCLBYTES; } m->m_len = len = min(totlen, len); bcopy(pkt, mtod(m, caddr_t), len); pkt += len; totlen -= len; *mp = m; mp = &m->m_next; } return (top); } void mc_putpacket(struct mc_softc *sc, u_int len) { dbdma_command_t *cmd = sc->sc_txdmacmd; DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, len, sc->sc_txbuf_pa, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); cmd++; DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); dbdma_start(sc->sc_txdma, sc->sc_txdbdma); } /* * Interrupt handler for the MACE DMA completion interrupts */ int mc_dmaintr(void *arg) { struct mc_softc *sc = arg; int status, offset, statoff; int datalen, resid; int i, n, count; dbdma_command_t *cmd; /* We've received some packets from the MACE */ /* Loop through, processing each of the packets */ i = sc->sc_tail; for (n = 0; n < MC_RXDMABUFS; n++, i++) { if (i == MC_RXDMABUFS) i = 0; cmd = &sc->sc_rxdmacmd[i]; status = dbdma_ld16(&cmd->d_status); resid = dbdma_ld16(&cmd->d_resid); if ((status & DBDMA_CNTRL_ACTIVE) == 0) { continue; } count = dbdma_ld16(&cmd->d_count); datalen = count - resid; datalen -= 4; /* 4 == status bytes */ if (datalen < 4 + sizeof(struct ether_header)) { printf("short packet len=%d\n", datalen); /* continue; */ goto next; } DBDMA_BUILD_CMD(cmd, DBDMA_CMD_STOP, 0, 0, 0, 0); offset = i * MACE_BUFLEN; statoff = offset + datalen; sc->sc_rxframe.rx_rcvcnt = sc->sc_rxbuf[statoff + 0]; sc->sc_rxframe.rx_rcvsts = sc->sc_rxbuf[statoff + 1]; sc->sc_rxframe.rx_rntpc = sc->sc_rxbuf[statoff + 2]; sc->sc_rxframe.rx_rcvcc = sc->sc_rxbuf[statoff + 3]; sc->sc_rxframe.rx_frame = sc->sc_rxbuf + offset; mc_rint(sc); next: DBDMA_BUILD_CMD(cmd, DBDMA_CMD_IN_LAST, 0, DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); cmd->d_status = 0; cmd->d_resid = 0; sc->sc_tail = i + 1; } dbdma_continue(sc->sc_rxdma); return 1; } void mc_reset_rxdma(struct mc_softc *sc) { dbdma_command_t *cmd = sc->sc_rxdmacmd; int i; u_int8_t maccc; /* Disable receiver, reset the DMA channels */ maccc = NIC_GET(sc, MACE_MACCC); NIC_PUT(sc, MACE_MACCC, maccc & ~ENRCV); dbdma_reset(sc->sc_rxdma); bzero(sc->sc_rxdmacmd, 8 * sizeof(dbdma_command_t)); for (i = 0; i < MC_RXDMABUFS; i++) { DBDMA_BUILD(cmd, DBDMA_CMD_IN_LAST, 0, MACE_BUFLEN, sc->sc_rxbuf_pa + MACE_BUFLEN * i, DBDMA_INT_ALWAYS, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); cmd++; } DBDMA_BUILD(cmd, DBDMA_CMD_NOP, 0, 0, 0, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_ALWAYS); dbdma_st32(&cmd->d_cmddep, sc->sc_rxdbdma->d_paddr); cmd++; sc->sc_tail = 0; dbdma_start(sc->sc_rxdma, sc->sc_rxdbdma); /* Reenable receiver, reenable DMA */ NIC_PUT(sc, MACE_MACCC, maccc); } void mc_reset_txdma(struct mc_softc *sc) { dbdma_command_t *cmd = sc->sc_txdmacmd; dbdma_regmap_t *dmareg = sc->sc_txdma; u_int8_t maccc; /* disable transmitter */ maccc = NIC_GET(sc, MACE_MACCC); NIC_PUT(sc, MACE_MACCC, maccc & ~ENXMT); dbdma_reset(sc->sc_txdma); bzero(sc->sc_txdmacmd, 2 * sizeof(dbdma_command_t)); DBDMA_BUILD(cmd, DBDMA_CMD_OUT_LAST, 0, 0, sc->sc_txbuf_pa, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); cmd++; DBDMA_BUILD(cmd, DBDMA_CMD_STOP, 0, 0, 0, DBDMA_INT_NEVER, DBDMA_WAIT_NEVER, DBDMA_BRANCH_NEVER); out32rb(&dmareg->d_cmdptrhi, 0); out32rb(&dmareg->d_cmdptrlo, sc->sc_txdbdma->d_paddr); /* restore old value */ NIC_PUT(sc, MACE_MACCC, maccc); } /* * Go through the list of multicast addresses and calculate the logical * address filter. */ void mace_calcladrf(struct mc_softc *sc, u_int8_t *af) { struct ether_multi *enm; u_int32_t crc; struct ifnet *ifp = &sc->sc_arpcom.ac_if; struct arpcom *ac = &sc->sc_arpcom; struct ether_multistep step; /* * Set up multicast address filter by passing all multicast addresses * through a crc generator, and then using the high order 6 bits as an * index into the 64 bit logical address filter. The high order bit * selects the word, while the rest of the bits select the bit within * the word. */ *((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0; ETHER_FIRST_MULTI(step, ac, enm); while (enm != NULL) { if (bcmp(enm->enm_addrlo, enm->enm_addrhi, ETHER_ADDR_LEN)) { /* * We must listen to a range of multicast addresses. * For now, just accept all multicasts, rather than * trying to set only those filter bits needed to match * the range. (At this time, the only use of address * ranges is for IP multicast routing, for which the * range is big enough to require all bits set.) */ goto allmulti; } crc = ether_crc32_le(enm->enm_addrlo, sizeof(enm->enm_addrlo)); /* Just want the 6 most significant bits. */ crc >>= 26; /* Set the corresponding bit in the filter. */ af[crc >> 3] |= 1 << (crc & 7); ETHER_NEXT_MULTI(step, enm); } ifp->if_flags &= ~IFF_ALLMULTI; return; allmulti: ifp->if_flags |= IFF_ALLMULTI; *((u_int32_t *)af) = *((u_int32_t *)af + 1) = 0xffffffff; }